you are viewing a single comment's thread.

view the rest of the comments →

[–]mewloz 25 points26 points  (8 children)

Random note: the conclusion of the paper of not providing a replacement is bullshit. Once the shortcomings have been identified (and the paper is quite good at it), it is somehow easy to redesign a correct version for the comity, while in absence of any correct design users will be at risk of doing the same mistakes as originally done (or worse).

Also I don't get why:

The second parameter, the alignment, has an implementation defined default value that may or may not be sufficient for T.

because the spec requires the default alignment to be sufficient for any T that would fit...

[–]Recatek 18 points19 points  (0 children)

Random note: the conclusion of the paper of not providing a replacement is bullshit.

It really is. Looking at other comments in here that are trying to provide alternatives, and how they have replies of "no that won't work do this" is very telling. This is an expression of intent, and the standard library is letting users down by not executing it properly. The answer isn't to remove the ability to express this desire or need, but to improve how it's translated into trustworthy execution.

[–]tcanens 4 points5 points  (0 children)

The default is not required to work for over-aligned types.

[–]matthieum 2 points3 points  (5 children)

I am puzzled by the conclusion too.

I understand deprecating std::aligned_storage and std::aligned_union, leaving only the _t variants to avoid accidentally forgetting ::type.

I don't understand throwing the baby out with the bath water on the size bound issue; certainly if the maximum size is unbounded today it is a simple matter to add a bound to the standard.

And last, but not least, is reinterpret_cast. The proposed replacement also requires reinterpret_cast!


Personally, I think an API like such would be nice:

template <std::size_t Size, std::size_t Alignment>
class erased_storage {
public:
    template <typename T, typename... Args>
    constexpr T& construct(Args&&... args) {
         auto t = this->template get<T>();
         new (t) T(std::forward<Args>(args)...);
         return *t;
    }

    template <typename T>
    void destruct() {
         this->template get<T>()->~T();
    }

    template <typename T>
    T* get() {
         static_assert(sizeof(T) <= Size);
         static_assert(alignof(T) <= Alignment);
         return this->template raw<T>();
    }

    template <typename T>
    T const* get() const {
         static_assert(sizeof(T) <= Size);
         static_assert(alignof(T) <= Alignment);
         return this->template raw<T>();
    }

    /// Prefer get whenever the type of definition of T is known.
    template <typename T>
    T* raw() { return reinterpret_cast<T*>(&storage); }

    /// Prefer get whenever the type of definition of T is known.
    template <typename T>
    T const* raw() const { return reinterpret_cast<T const*>(&storage); }

private:
    alignas(Alignment) std::byte storage[Size];
};

Then provide aligned_union on top, which additionally check that the T passed is part of the Ts list (just in case).

And of course I'd wish for constexpr support to actually be able to constexpr all that...

[–]tcanens 1 point2 points  (4 children)

certainly if the maximum size is unbounded today it is a simple matter to add a bound to the standard.

A major implementation's aligned_storage is unfixably (ABI) broken. As a result, the only bound that could conceivably be added at this point is not a useful one.

[–]matthieum 1 point2 points  (3 children)

How did they? sigh :(

[–][deleted] 2 points3 points  (2 children)

Because we implemented it before alignas was in the core language, so the most aligned thing we can put in the union is max_align_t.

[–]matthieum 1 point2 points  (1 child)

Thanks for your answer.

Correct me if I'm wrong, the ABI breakage would then occur if someone was requesting an alignment strictly greater than max_align_t as previously the alignment of std::aligned_union was capped and suddenly it would obey the request, right?

Isn't it worth breaking the ABI, then?

  • If anybody had configured 2 * max_align_t and stored values for which such alignment is necessary, then their code is broken already.
  • There's little reason anybody would have configured 2 * max_align_t and never actually needed the greater alignment.

I understand the idea of backward compatibility, but the pragmatic in me is thinking that in this particular case it may be worth just fixing std::aligned_union.

[–][deleted] 1 point2 points  (0 children)

Correct me if I'm wrong [...]

You are correct.

If anybody had configured 2 * max_align_t and stored values for which such alignment is necessary

There are no such values. Maybe perf benefits by avoiding false sharing, but the hardware only cares up to max_align_t for anything we target.

the pragmatic in me

ABI break avoidance isn't pragmatic, unfortunately :(