#ifndef VARIANT_HPP
#define VARIANT_HPP

#include <typeinfo>
#include <typeindex>

class Variant {
    std::type_index _type;
protected:
    Variant(const std::type_index &t) : _type(t) {};
public:

    const std::type_index &type() const {return _type;}

    template <typename S> operator S&() throw(std::bad_cast);
    template <typename S> S &data() throw(std::bad_cast);
};

template <typename T>
class TypedVariant : public Variant {
    T _data;
public:
    TypedVariant(Variant &va) : Variant(typeid(T)) {
            if (va.type()==typeid(T)) _data=static_cast<TypedVariant<T>*>(&va)->get();
            else throw std::bad_cast(); }
    TypedVariant(const T &t) : Variant(typeid(T)), _data(t) {}

    template <typename S>
    S cast() const {return _data;}
    T &get() {return _data;}
};

template <typename S> Variant::operator S&() throw(std::bad_cast) {
    if (type()==typeid(S)) return static_cast<TypedVariant<S>*>(this)->get();
    throw std::bad_cast();
};

template <typename S> S& Variant::data() throw(std::bad_cast) {
    if (type()==typeid(S)) return static_cast<TypedVariant<S>*>(this)->get();
    throw std::bad_cast();
};

template <typename S, typename T>
S cast(const Variant &v) throw(std::bad_cast) {
    if (v.type()==typeid(T)) return ((TypedVariant<T>*)(&v))->cast<S>();
    throw std::bad_cast();
}

template <typename S, typename T>
S cast(const TypedVariant<T> &v) {return v.cast<S>();}

#endif // VARIANT_HPP
