I am going to ignore alignment, or rather assume that data after a pointer is sufficiently aligned.
template<class T, unsigned N>
struct poly_anna;
template<class T,unsigned N>
struct poly_bob {
typedef poly_anna<T,N> poly_anna_;
T*(*get)(poly_anna_*) = nullptr;
void(*destroy)(poly_anna_*) = nullptr;
void(*move_to)(poly_anna_ *,poly_anna_*) = nullptr;
void(*copy_to)(poly_anna_ const*, poly_anna_*)=nullptr;
};
template<class T, unsigned N>
struct poly_anna {
private:
poly_bob<T,N> const*bob=nullptr;
char buff[N];
public:
template<class U> static poly_bob<T,N> const* get_bob() {
static poly_bob<T,N> b={
[](poly_anna*a)->T&{ return *(U*)&a->buff[0]; },
[](poly_anna*a){ ((U*)&a->buff[0])->~U(); a->bob = nullptr; },
[](poly_anna*s,poly_anna*d){
if (s->bob==d->bob){
*((U*)&d->buff[0])=std::move(*((U*)&d->buff[0]));
return;
}
if (d->bob != nullptr) {
d->bob->destroy(b);
}
d->store( std::move( *(U*)&s->buff[0] ) );
},
[](poly_anna const* s, poly_anna*d){
if (d->bob == s->bob){
*(U*)&d->buff[0] = *(U const*)&s->buff[0];
return;
}
if (d->bob){ d->bob->destroy(d); }
d->store( *(U const*)*s->buff[0] );
}
};
return &b;
};
template<class U_>
void store(U_&& u){
typedef typename std::decay<U_>::type U;
static_assert( sizeof(U)<=N, "N not large enough" );
if (bob) bob->destroy( this );
bob = get_bob<U>();
new (&buff[0]) U( std::forward<U_>(u) );
}
void reset(){ if (bob) bob->destroy(this); }
T& get() {
return bob->get(this);
}
T const& get() const {
return bob->get(const_cast<poly_anna*>(this));
}
poly_anna( poly_anna const& o ){
if (o.bob) o.bob->copy_to( &o, this );
}
poly_anna( poly_anna && o ){
if (o.bob) o.bob->move_to( &o, this );
}
poly_anna&operator=( poly_anna const& o ){
if (o.bob) o.bob->copy_to( &o, this );
else if (bob) bob->destroy(this);
return *this
}
poly_anna&operator=( poly_anna && o ){
if (o.bob) o.bob->move_to( &o, this );
else if (bob) bob->destroy(this);
return *this
}
poly_anna()=default;
~poly_anna(){if(bob)bob->destroy(this);}
explicit operator bool()const{return bob;}
};
That is my attempt at a polymorphic variant. It stores T
and children of T
so long as they are no larger than N
and can be stored in std
containers.
Let me know if it compiles.