Skip to content

Latest commit

 

History

History
244 lines (197 loc) · 5.68 KB

326.md

File metadata and controls

244 lines (197 loc) · 5.68 KB
Info

Example

template<class T>
struct container {
    //std::aligned_storage_t<sizeof(T), alignof(T)> t_buff; // deprecated
    alignas(T) std::byte t_buff[sizeof(T)]; // okay
};

https://godbolt.org/z/zMjq8s7En

Puzzle

  • Can you implement cointainer which will store aligned union?
union Union {
    int i;
    double d;
    char c;
};

template <class... Ts>
class container;  // TODO

int main() {
    using namespace boost::ut;

    container<int, double, char> container{};

    "container"_test = [=] {
        mut(container).construct<Union>(Union{.i = 42});
        expect(42_i == container.unsafe_get<int>());
        mut(container).destroy<Union>();
    };

    "container"_test = [=] {
        mut(container).construct<Union>(Union{.d = 77.});
        expect(77._d == container.unsafe_get<double>());
        mut(container).destroy<Union>();
    };
}

https://godbolt.org/z/doh1o8GrW

Solutions

template <class... Ts>
class container {
   public:
    template <class U>
        requires std::is_union_v<U>
    void construct(U&& u) {
        new (buf) U(std::forward<U>(u));
    }

    template <class U>
        requires std::is_union_v<U>
    void destroy() {
        // No-op since C unions can only be used with
        // trivially destructable types that don't need explicit destruction

        // To be safe, we can use the below to zero out the memory
        std::fill(std::begin(buf), std::end(buf), std::byte{});
    }

    template <class T>
    T unsafe_get() const {
        return *reinterpret_cast<const T*>(buf);
    }

   private:
    static constexpr auto largest_elem_size = std::max({sizeof(Ts)...});

   private:
    alignas(Ts...) std::byte buf[largest_elem_size];
};

https://godbolt.org/z/aMd1xWvzP

template <class... Ts>
union container {};

template <class T, class... Ts>
union container<T, Ts...> {
    T _first;
    container<Ts...> _rest;

    template <class U>
    auto construct(const U &value) {
        *this = std::bit_cast<container>(value);
    }

    template <class U>
    auto unsafe_get() const {
        if constexpr (std::same_as<T, U>) {
            return _first;
        } else {
            return _rest.template unsafe_get<U>();
        }
    }

    template <class U>
    auto destroy() {
        // :P
    }
};

https://godbolt.org/z/MKPznjnTn

template <typename T, std::size_t Align, std::size_t Size>
concept Fits = alignof(T) <= Align and sizeof(T) <= Size;

template <class... Ts>
class container {
    static constexpr auto storage_size = sizeof(std::variant<Ts...>);
    static constexpr auto storage_alignment = alignof(std::variant<Ts...>);
    alignas(storage_alignment) std::byte storage[storage_size];
    std::size_t hash_value{};
    std::function<void(void)> destructor;

   public:
    template <Fits<storage_alignment, storage_size> A, typename... Bs>
    void construct(Bs&&... args) {
        if (hash_value) {
            destructor();
        }
        new (&storage) A{std::forward<Bs>(args)...};
        hash_value = typeid(A).hash_code();
        destructor = [this] {
            reinterpret_cast<A*>(storage)->~A();
            hash_value = 0;
        };
    }

    template <typename T>
    T const& unsafe_get() const {
        if (hash_value) {
            return reinterpret_cast<T const&>(storage);
        } else {
            throw std::runtime_error(
                "Oh no! You want data but there isn't any available.");
        }
    }

    template <Fits<storage_alignment, storage_size> A>
    void destroy() {
        if (hash_value == typeid(A).hash_code()) {
            destructor();
            hash_value = 0;
        } else {
            throw std::runtime_error(
                "Yikes!!! You tried to destroy the wrong type!");
        }
    }

    ~container() {
        if (hash_value) {
            destructor();
        }
    }
};

https://godbolt.org/z/WbfMrKh86

template <class... Ts>
class container {
    static constexpr auto size = std::max({sizeof(Ts)...});
    alignas(Ts...) std::array<std::byte, size> buffer;

   public:
    template <class T>
    void construct(T&& value) {
        new (buffer.data()) T(std::forward<T>(value));
    }

    template <class T>
    const T unsafe_get() const {
        return *reinterpret_cast<const T*>(buffer.data());
    }

    template <class T>
    void destroy() {
        buffer.fill(std::byte{});
    }
};

https://godbolt.org/z/sr6crEqhn

template <auto alignment, auto size>
struct data {
    alignas(alignment) std::byte bytes[size];
};

template <class... Ts>
class container {
    static constexpr std::size_t alignment = std::max({alignof(Ts)...});
    static constexpr std::size_t size = std::max({sizeof(Ts)...});
    data<alignment, size> *d;

   public:
    template <class U>
    void construct(U u) {
        static_assert(sizeof...(Ts) > 0);
        static_assert(std::is_union_v<U>);
        static_assert(std::max({sizeof(Ts)...}) == sizeof(U));
        d = new data<alignment, size>();
        const std::byte *source = reinterpret_cast<std::byte *>(&u);
        std::memcpy(d->bytes, &u, sizeof(u));
    }

    template <class T>
    T unsafe_get() const {
        static_assert((std::is_same_v<T, Ts> || ...), "The type is not valid.");
        return *reinterpret_cast<const T *>(&d->bytes);
    };

    template <class U>
    void destroy() const {
        delete d;
    };
};

https://godbolt.org/z/5G1PocMrv