Here is the basic some
.
T
copy / assign / move / etc can be implemented in terms of emplace
. SFINAE using can_store<T>
can ensure that only some
types can actually be stored, assigned to it, avoiding unnecessary exceptions.
Currently, moving from some
destroys its contents, rather than just moving away from it. And some
may be empty (they are "nulllable").
load_from
is a constructor of "can-fail" copies from another some
- it returns false
on failure. I could add 'can not-fail' from the smaller some
(even the copy / assign statement) to complete it.
some_meta
is a manual table of virtual functions. One exists for type T
, which you store in some
any size. It stores the erase operations of a type of type T
, which wants to use some
(in this case, copy the move and destroy), as well as some data about the type (size, alignment, and type identifier). This can be complemented by additional operations such as comparison and serialization. For binary operations, the "inappropriate type" processing logic must be considered. For things like serialization, I would call it the free serialize
and deserialize
on T
In both cases, we impose additional requirements on what some
can store (you can deal with “serialization” with a little work, but it becomes messy).
You can even imagine a system in which you can store a set of operations for executing data (binary and unary) and transfer the specified operations associated with types passed to some. At the moment, we are approaching the boost
erase library.
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 some { 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;
the meta
object is a virtual function table implemented manually. This reduces the memory overhead for a given one by one pointer (above the storage buffer).
living example
As shown above, this is quite viable.
Note that create
returns a pointer to the same meta
for the same type T
, even if called more than once.
I used half the codes in my test above. Others probably have errors.
some_that_fits
lets you pass a set of types and returns the type some
, which is suitable for these types.
No exceptions generated by operations with saved types by specified stored types are thrown. Whenever possible, I test at compile time to ensure type matching.
Could I add support for greater alignment, small types of repositories, running them with an offset in my data?