Here,
how do I fix this c++ typelist template compile error?
we built a typelist, using the code from modern c++ design.
Question is now -- how do I take this and built it into a variant class?
Here,
how do I fix this c++ typelist template compile error?
we built a typelist, using the code from modern c++ design.
Question is now -- how do I take this and built it into a variant class?
/*
* variant_modern.hpp
*
* Created on: Jun 4, 2010
* Author: vvenedik
*/
#ifndef _VARIANT_MODERN_HPP_
#define _VARIANT_MODERN_HPP_
struct NullType {} ;
template <class T, class U>
struct TypeList
{
typedef T Head;
typedef U Tail;
};
#define TYPELIST_1(T1) TypeList<T1, NullType>
#define TYPELIST_2(T1, T2) TypeList<T1, TYPELIST_1(T2) >
#define TYPELIST_3(T1, T2, T3) TypeList<T1, TYPELIST_2(T2, T3) >
#define TYPELIST_4(T1, T2, T3, T4) TypeList<T1, TYPELIST_3(T2, T3, T4) >
#define TYPELIST_5(T1, T2, T3, T4, T5) TypeList<T1, TYPELIST_4(T2, T3, T4, T5) >
#define TYPELIST_6(T1, T2, T3, T4, T5, T6) TypeList<T1, TYPELIST_5(T2, T3, T4, T5, T6) >
#define TYPELIST_7(T1, T2, T3, T4, T5, T6, T7) TypeList<T1, TYPELIST_6(T2, T3, T4, T5, T6, T7) >
#define TYPELIST_8(T1, T2, T3, T4, T5, T6, T7, T8) TypeList<T1, TYPELIST_7(T2, T3, T4, T5, T6, T7, T8) >
#define TYPELIST_9(T1, T2, T3, T4, T5, T6, T7, T8, T9) TypeList<T1, TYPELIST_8(T2, T3, T4, T5, T6, T7, T8, T9) >
namespace vlad
{
namespace modern
{
template <class TL> struct MaxSize ;
template <>
struct MaxSize<NullType>
{
enum { value = 0 } ;
} ;
template <class Head, class Tail>
struct MaxSize< TypeList<Head, Tail> >
{
private :
enum { tailValue = size_t(MaxSize<Tail>::value) } ;
public:
enum { value = sizeof(Head) > tailValue ? sizeof(Head) : tailValue } ;
} ;
//TL Length
template <class TL> struct Length ;
template <>
struct Length<NullType>
{
enum { value = 0 } ;
} ;
template <class Head, class Tail>
struct Length< TypeList<Head,Tail> >
{
enum { value = 1 + Length<Tail>::value } ;
} ;
//TL IndexOf
template <typename TL, typename T > struct IndexOf ;
template <class T>
struct IndexOf<NullType, T>
{
//if not found IndexOf == max_num_of_t
enum { value = -1 };
};
template <class Tail, class T>
struct IndexOf<TypeList<T,Tail>,T>
{
enum { value = 0 };
};
template <class Head, class Tail, class T>
struct IndexOf<TypeList<Head,Tail>,T>
{
private:
enum { nextVal = IndexOf<Tail,T>::value };
public:
enum { value = nextVal == -1 ? -1 : 1 + nextVal } ;
};
template <typename TL, unsigned int i> struct TypeAt ;
template <class Head, class Tail>
struct TypeAt<TypeList<Head, Tail>, 0>
{
typedef Head type;
};
template <class Head, class Tail, unsigned int i>
struct TypeAt<TypeList<Head, Tail>, i>
{
typedef typename TypeAt<Tail, i-1>::type type;
};
template <typename TL>
class Variant
{
public :
//compute the needed buffer size
enum { max_size_t = MaxSize<TL>::value } ;
enum { max_num_of_t = Length<TL>::value } ;
//struct for alignment
typedef struct { unsigned char buf_t[max_size_t] ; } base_t ;
//default constructor
template <typename T>
explicit Variant() : _type_index(0)
{
new (&_variant_holder) TypeAt<TL, 0>::type() ;
}
virtual ~Variant() { } ; //{ _type_ptr->~T() ; }
template <typename T>
explicit Variant( const T &t )
{
_type_index = IndexOf<TL, T>::value ;
if ( _type_index == max_num_of_t )
throw std::bad_cast() ;
new (&_variant_holder) T(t) ;
}
template <typename T>
const T & get() {
std::size_t type_index = IndexOf<TL, T>::value ;
if ( type_index == max_num_of_t || type_index != _type_index)
throw std::bad_cast() ;
T * _type_ptr = reinterpret_cast<T *>(&_variant_holder) ;
return *_type_ptr ;
}
private :
base_t _variant_holder ;
std::size_t _type_index ;
};
} //namespace modern
}//namespace vlad
#endif /* _VARIANT_MODERN_HPP_ */
Use case :
typedef modern::vlad::Variant<TYPELIST_2(int, std::string)> variant_t;
variant_t v (123) ;
int value = v.get<int>() ;
std::string tmp = v.get<std::string>() ; //throws exception
The proper, but more advanced, approach would be actually store the values in a holder type that knows how to manage the actual type it contains.
A simpler approach, for learning purposes, would be to map types to numbers (i.e. their position in the typelist). With that you can remember what type you are currently storing in the variant.
To get that a working version you'll probably want to have templated constructors and setters and also an accessor function that use that type-number mapping.
Quite simplified it could look something like this:
template<class TypeList>
class variant {
unsigned type;
void* value;
public:
// ...
template<class T>
void set_to(const T& t) {
STATIC_ASSERT(contains<TypeList, T>::value);
// ... clean up previous value
type = index_of<TypeList, T>::value;
value = static_cast<void*>(new T(t));
}
template<class T>
const T& get_as() {
STATIC_ASSERT(contains<TypeList, T>::value);
if(type == index_of<TypeList, T>::value)
return *static_cast<T*>(value);
else
throw type_error();
}
};