c++ - Why no "variant" any in boost or Standard? -
one advantage of any
on variant
is, 1 not need specify types, may contain. i've noticed, number of types variant
may contain grows, people tend switch any
@ point, because don't keep track of types anymore. think hybrid between any
, variant
possible. 1 store "placeholder
" (via placement new
) of any
in aligned_storage
, size calculated in constexpr
function or template metafunction, sample of largest types, may end being stored. user, on other hand, not need specify types, any
might contain. any
throw @ time, if user try store larger aligned_storage
in there.
does such "variant_any
" class exist? there inherent problem idea?
here basic some
.
the t
copy/assign/move/etc can implemented in terms of emplace
. sfinae using can_store<t>
can ensure types some
can store assignable it, avoiding needless exceptions.
currently, moving some
destroys contents instead of moving it. , some
can empty (they "nulllable").
load_from
'can-fail' copy constructor some
-- returns false
on failure. add 'cannot-fail' smaller some
(even copy/assignment operator) complete it.
some_meta
manual virtual function table. 1 exists per type t
store in some
of size. stores type-erased operations on type t
some
wants use (in case, copy move , destroy), plus data type (size, alignment , type identity). augmented additional operations comparison , serialization. binary operations, logic handle "no matching type" has considered. stuff serialization, i'd have call free function serialize
, deserialize
on t
. in both cases, impose additional requirements on some
can store (you can, bit of work, handle "maybe serialize", gets messy).
you imagine system can store set of operations perform on data (binary , unary) , pass said operations bundled in types passed some. @ point, approaching boost
's type erasure library, however.
namespace details { template<std::size_t size, std::size_t align=0> struct storage_helper { using type = std::aligned_storage_t<size, align>; enum { alignment = alignof(type), size = size }; }; template<std::size_t size> struct storage_helper<size, 0> { using type = std::aligned_storage_t<size>; enum { alignment = alignof(type), size = size }; }; template<std::size_t size, std::size_t align> using storage_helper_t = typename storage_helper<size,align>::type; template<class t>using type=t; struct some_meta { type<void(void*)>* destroy; type<void(void* dest, void const* src)>* copy; type<void(void* dest, void* src)>* move; std::type_index type; size_t size; size_t align; template<class t> static some_meta const* get() { static const some_meta retval( create<t>() ); return &retval; }; private: template<class t> static some_meta create() { return { [](void* p){ ((t*)p)->~t(); }, [](void* out, void const* in){ new(out)t(*(t*)in); }, [](void* dest, void* src) { new(dest)t(std::move(*(t*)src)); }, typeid(t), sizeof(t), alignof(t) }; } }; } template<class>struct emplace_as{}; template< std::size_t size, std::size_t align=0 > struct { enum { align = details::storage_helper<size, align>::alignment }; using data_type = details::storage_helper_t<size, align>; template<size_t, size_t> friend struct some; template<class t> struct can_store : std::integral_constant< bool, ((align%alignof(t))==0) && sizeof(t) <= size) > {}; template<size_t x, size_t a> static bool can_fit( some<x,a> const& o ) { if (x<=size && ((align%some<x,a>::align)==0)) return true; // should cause optimizations if (!o.meta) return true; if (o.meta->size > size) return false; if (o.meta->align > align) return false; return true; } private: data_type data; details::some_meta const* meta = nullptr; public: // true iif (exactly) t template<class t> bool is() const { return meta && (meta->type == typeid(t)); } explicit operator bool()const { return meta!=nullptr; } template<class t> t* unsafe_get() { return reinterpret_cast<t*>(&data); } template<class t> t* get() { if (is<t>()) return unsafe_get<t>(); else return nullptr; } void clear() { if (meta) meta->destroy(&data); meta = nullptr; } template<class t, class... args> std::enable_if_t< can_store<t>{} > emplace(args&&...args) { clear(); new(&data) t(std::forward<args>(args)...); meta = details::some_meta::get<t>(); } some()=default; some(some const& o) { *this = o; } some(some const&&o):some(o){} some(some&o):some(const_cast<some const&>(o)){} some(some&& o) { *this = std::move(o); } some& operator=(some const&o) { if (this == &o) return *this; clear(); if (o.meta) { o.meta->copy( &data, &o.data ); meta=o.meta; } return *this; } some& operator=(some &&o) { if (this == &o) return *this; clear(); if (o.meta) { o.meta->move( &data, &o.data ); meta=o.meta; o.clear(); } return *this; } some& operator=(some const&&o) { return *this=o; } some& operator=(some &o) { return *this=const_cast<some const&>(o); } // non-some: template<class t,class=std::enable_if_t<can_store<std::decay_t<t>>{}>> some(t&& t){ emplace<std::decay_t<t>>(std::forward<t>(t)); } template<class t, class...args,class=std::enable_if_t<can_store<t>{}>> some( emplace_as<t>, args&&...args ){ emplace<t>(std::forward<args>(args)...); } template<class t,class=std::enable_if_t<can_store<std::decay_t<t>>{}>> some& operator=(t&&t){ emplace<std::decay_t<t>>(std::forward<t>(t)); return *this; } template<size_t x, size_t a> bool load_from( some<x,a> const& o ) { if ((void*)&o==this) return true; if (!can_fit(o)) return false; clear(); if (o.meta) { o.meta->copy( &data, &o.data ); meta=o.meta; } return true; } template<size_t x, size_t a> bool load_from( some<x,a> && o ) { if ((void*)&o==this) return true; if (!can_fit(o)) return false; clear(); if (o.meta) { o.meta->move( &data, &o.data ); meta=o.meta; o.clear(); } return true; } ~some() { clear(); } }; template<class t, class...ts> using some_that_fits = some< (std::max)({sizeof(t),sizeof(ts)...}), (std::max)({alignof(t),alignof(ts)...}) >;
the meta
object manually implemented virtual function table, basically. reduces memory overhead of given some
1 pointer (above storage buffer).
as demonstrated above, quite viable.
note create
returns pointer same meta
same type t
, if called more once.
i have exercised half code paths in test above. others have bugs.
some_that_fits
lets pass set of types, , returns some
type fits types.
no exceptions, other generated operations on stored types said stored types, thrown. when possible, test @ compile time ensure types fit.
i possibly add support greater alignment, small storage types starting them @ offset data?
Comments
Post a Comment