I've got 15 minutes. Incoming tagged union. Not as good as boost::variant
, and I might not actually compile it, but it should get you started.
template<size_t S>
using size = std::integral_constant<std::size_t, S>;
template<bool b>
using bool_t = std::integral_constant<bool, b>;
template<size_t...Ss>
struct max_size:size<0>{};
template<size_t S0, size_t...Ss>
struct max_size:size<(std::max)(S0, max_size<Ss...>{}())>{};
template<class...Ts>
struct max_alignof : max_size< alignof(Ts)... >{};
template<class...Ts>
struct max_sizeof : max_size< sizeof(Ts)... >{};
template<class X>struct tag{using type=X;};
template<class...>struct types{using type=types;};
template<class Tag>using type_t=typename Tag::type;
template<class T, class Types>
struct index_of {};
template<class T, class...Ts>
struct index_of<T, types<T,Ts...>>:size<0>{};
template<class T, class T0, class...Ts>
struct index_of<T, types<T0,Ts...>>:size<
index_of<T, types<Ts...>>{}+1
>{};
template<class X>
struct emplace_as {};
template<class F, class...Ts>
void invoke( types<Ts...>, void* p, F&& f, size_t i ) {
auto* pf = std::addressof(f);
using operation=void(*)(decltype(pf), void*);
operation table[]={
+[](decltype(pf), void* p){
Ts* pt = static_cast<Ts*>(p);
std::forward<F>(*pf)( *pt );
}...
};
table[i]( pf, p );
}
template<class T0, class...Ts>
struct one_of {
std::aligned_storage< max_sizeof<T0, Ts...>{}, max_alignof<T0, Ts...>{} > data;
size_t index = -1;
using my_types = types<T0, Ts...>;
template<class T>
using sfinae_my_type = tag< size<index_of<X,my_types>{}> >;
one_of():one_of(emplace_as<T0>{}) {}
// brace construction support for only the first type:
one_of(T0&&t0):one_of(emplace_as<T0>{}, std::move(t0)) {}
template<class X, class...Args, class=sfinae_my_type<X>>
one_of(emplace_as<X>, Args&&... args){
emplace( emplace_as<X>{}, std::forward<Args>(args)... );
}
template<class X, class=sfinae_my_type<std::decay_t<X>>>
one_of(X&& x) {
emplace_as(std::forward<X>(x));
}
template<class X, class=sfinae_my_type<X>>
X* get() {
if (index_of<X, my_types>{}==index) {
return static_cast<X*>(&data);
} else {
return nullptr;
}
}
template<class X, class=sfinae_my_type<X>>
X const* get() const {
if (index_of<X, my_types>{}==index) {
return static_cast<X const*>(&data);
} else {
return nullptr;
}
}
template<class X, class=sfinae_my_type<std::decay_t<X>>>
void emplace(X&& x) {
emplace_as<std::decay_t<X>>{}, std::forward<X>(x));
}
template<class X, class...Args, class=sfinae_my_type<X>>
void emplace( emplace_as<X>, Args&&...args ) {
destroy();
new(&data) X(std::forward<Args>(args)...);
index = index_of<X, list<T0, Ts...>>{};
}
template<class F>
void my_invoke(F&& f) {
my_invoke( std::forward<F>(f), index );
}
template<class F>
void apply(F&& f) {
invoke( my_types{}, &data, std::forward<F>(f), index );
}
void destroy() {
if (index != -1) {
apply([&](auto&& x){
using X=std::decay_t<decltype(x)>;
index = -1;
x.~X();
});
};
}
one_of& operator=(one_of const& rhs){
if (this == &rhs) return *this;
destroy();
rhs.apply( [&](auto const& x) {
using X=std::decay_t<decltype(x)>;
emplace( emplace_as<X>{}, decltype(x)(x) );
} );
return *this;
}
one_of& operator=(one_of&& rhs){
if (this == &rhs) return *this;
destroy();
rhs.apply( [&](auto & x) {
using X=std::decay_t<decltype(x)>;
emplace( emplace_as<X>{}, std::move(x) );
} );
return *this;
}
~one_of(){destroy();}
};
which is a quick sketch of a tagged union type.
Store a one_of<int, double, std::string, char>
as your T
. Access via either apply
passing in a function that has overrides for each, or get<T>
.
The above is C++14, because it makes it easier. It doesn't include an apply
that has a return value, again because it makes it easier. Both can be remedied, but it takes work, and really, you should look at how boost
does it rather than use the above.