c++ - How to emulate EBO when using raw storage? -



c++ - How to emulate EBO when using raw storage? -

i have component utilize when implementing low-level generic types store object of arbitrary type (may or may not class type) may empty take advantage of empty base of operations optimization:

template <typename t, unsigned tag = 0, typename = void> class ebo_storage { t item; public: constexpr ebo_storage() = default; template < typename u, typename = std::enable_if_t< !std::is_same<ebo_storage, std::decay_t<u>>::value > > constexpr ebo_storage(u&& u) noexcept(std::is_nothrow_constructible<t,u>::value) : item(std::forward<u>(u)) {} t& get() & noexcept { homecoming item; } constexpr const t& get() const& noexcept { homecoming item; } t&& get() && noexcept { homecoming std::move(item); } }; template <typename t, unsigned tag> class ebo_storage< t, tag, std::enable_if_t<std::is_class<t>::value> > : private t { public: using t::t; constexpr ebo_storage() = default; constexpr ebo_storage(const t& t) : t(t) {} constexpr ebo_storage(t&& t) : t(std::move(t)) {} t& get() & noexcept { homecoming *this; } constexpr const t& get() const& noexcept { homecoming *this; } t&& get() && noexcept { homecoming std::move(*this); } }; template <typename t, typename u> class compressed_pair : ebo_storage<t, 0>, ebo_storage<u, 1> { using first_t = ebo_storage<t, 0>; using second_t = ebo_storage<u, 1>; public: t& first() { homecoming first_t::get(); } u& second() { homecoming second_t::get(); } // ... }; template <typename, typename...> class tuple_; template <std::size_t...is, typename...ts> class tuple_<std::index_sequence<is...>, ts...> : ebo_storage<ts, is>... { // ... }; template <typename...ts> using tuple = tuple_<std::index_sequence_for<ts...>, ts...>;

lately i've been messing lock-free info structures , need nodes optionally contain live datum. 1 time allocated, nodes live lifetime of info construction contained datum live while node active , not while node sits in free list. implemented nodes using raw storage , placement new:

template <typename t> class raw_container { alignas(t) unsigned char space_[sizeof(t)]; public: t& data() noexcept { homecoming reinterpret_cast<t&>(space_); } template <typename...args> void construct(args&&...args) { ::new(space_) t(std::forward<args>(args)...); } void destruct() { data().~t(); } }; template <typename t> struct list_node : public raw_container<t> { std::atomic<list_node*> next_; };

which fine , dandy, wastes pointer-sized chunk of memory per node when t empty: 1 byte raw_storage<t>::space_, , sizeof(std::atomic<list_node*>) - 1 bytes of padding alignment. nice take advantage of ebo , allocate unused single-byte representation of raw_container<t> atop list_node::next_.

my best effort @ creating raw_ebo_storage performs "manual" ebo:

template <typename t, typename = void> struct alignas(t) raw_ebo_storage_base { unsigned char space_[sizeof(t)]; }; template <typename t> struct alignas(t) raw_ebo_storage_base< t, std::enable_if_t<std::is_empty<t>::value> > {}; template <typename t> class raw_ebo_storage : private raw_ebo_storage_base<t> { public: static_assert(std::is_standard_layout<raw_ebo_storage_base<t>>::value, ""); static_assert(alignof(raw_ebo_storage_base<t>) % alignof(t) == 0, ""); t& data() noexcept { homecoming *static_cast<t*>(static_cast<void*>( static_cast<raw_ebo_storage_base<t>*>(this) )); } };

which has desired effects:

template <typename t> struct alignas(t) empty {}; static_assert(std::is_empty<raw_ebo_storage<empty<char>>>::value, "good!"); static_assert(std::is_empty<raw_ebo_storage<empty<double>>>::value, "good!"); template <typename t> struct foo : raw_ebo_storage<empty<t>> { t c; }; static_assert(sizeof(foo<char>) == 1, "good!"); static_assert(sizeof(foo<double>) == sizeof(double), "good!");

but undesirable effects, assume due violation of strict aliasing (3.10/10) although meaning of "access stored value of object" debatable empty type:

struct bar : raw_ebo_storage<empty<char>> { empty<char> e; }; static_assert(sizeof(bar) == 2, "not good: bar::e , bar::raw_ebo_storage::data() " "are distinct objects of same type " "same address.");

this solution potential undefined behavior upon construction. @ point programme must build containee object within raw storage placement new:

struct : raw_ebo_storage<empty<char>> { int i; }; static_assert(sizeof(a) == sizeof(int), ""); a; a.value = 42; ::new(&a.get()) empty<char>{}; static_assert(sizeof(empty<char>) > 0, "");

recall despite beingness empty, finish object has non-zero size. in other words, empty finish object has value representation consists of 1 or more padding bytes. new constructs finish objects, conforming implementation set padding bytes arbitrary values @ construction instead of leaving memory untouched case constructing empty base of operations subobject. of course of study catastrophic if padding bytes overlay other live objects.

so question is, possible create standard-compliant container class uses raw storage/delayed initialization contained object and takes advantage of ebo avoid wasting memory space representation of contained object?

i think gave reply in various observations:

you want raw memory , placement new. requires have at to the lowest degree 1 byte available, if want build empty object via placement new. you want zero bytes overhead storing empty objects.

these requirements self-contradicting. reply hence no, not possible.

you alter requirements bit more, though, requiring 0 byte overhead empty, trivial types.

you define new class trait, e.g.

template <typename t> struct constructor_and_destructor_are_empty : std::false_type { };

then specialize

template <typename t, typename = void> class raw_container; template <typename t> class raw_container< t, std::enable_if_t< std::is_empty<t>::value , std::is_trivial<t>::value>> { public: t& data() noexcept { homecoming reinterpret_cast<t&>(*this); } void construct() { // nil } void destruct() { // nil } }; template <typename t> struct list_node : public raw_container<t> { std::atomic<list_node*> next_; };

then utilize this:

using node = list_node<empty<char>>; static_assert(sizeof(node) == sizeof(std::atomic<node*>), "good");

of course, still have

struct bar : raw_container<empty<char>> { empty<char> e; }; static_assert(sizeof(bar) == 1, "yes, 2 objects sharing address");

but normal ebo:

struct ebo1 : empty<char>, empty<usigned char> {}; static_assert(sizeof(ebo1) == 1, "two object in 1 place"); struct ebo2 : empty<char> { char c; }; static_assert(sizeof(ebo2) == 1, "two object in 1 place");

but long utilize construct , destruct , no placement new on &data(), you're golden.

c++ c++14

Comments

Popular posts from this blog

formatting - SAS SQL Datepart function returning odd values -

c++ - Apple Mach-O Linker Error(Duplicate Symbols For Architecture armv7) -

php - Yii 2: Unable to find a class into the extension 'yii2-admin' -