namespace Meta__
{

template <Tag__ tag>
struct TypeOf;

template <typename Tp_>
struct TagOf;

$insert polymorphicSpecializations

    // Individual semantic value classes are derived from Base, offering a
    // member returning the value's Tag__, a member cloning the object of its
    // derived Semantic<Tag__> and a member returning a pointerr to its
    // derived Semantic<Tag__> data. See also Bisonc++'s distribution file
    // README.polymorphic-techical
class Base
{
    protected:
        Tag__ d_baseTag;    // The tag is assigned by Semantic.

    public:
        Base();
        Base(Base const &other) = delete;

        virtual ~Base();

        Tag__ tag() const;
        Base *clone() const;
        void *data() const;        

    private:
        virtual void *vData() const;
        virtual Base *vClone() const;
};

inline Base *Base::clone() const
{
    return vClone();
}

inline void *Base::data() const
{
    return vData();
}

inline Base::Base()
:
    d_baseTag(static_cast<Tag__>(sizeofTag))
{}

inline Tag__ Base::tag() const
{
    return d_baseTag;
}

    // The class Semantic stores a semantic value of the type matching tg_
template <Tag__ tg_>
class Semantic: public Base
{
    typename TypeOf<tg_>::type d_data;
    
    public:
        Semantic();
        Semantic(Semantic<tg_> const &other);

            // This constructor member template forwards its arguments to
            // d_data, allowing it to be initialized using whatever
            // constructor is available for DataType
        template <typename ...Params>
        Semantic(Params &&...params);

    private:
        Base *vClone() const override;
        void *vData() const override;
};

    // The class SType wraps a pointer to Base.  It becomes the polymorphic
    // STYPE__ type. It also defines get members, allowing constructions like
    // $$.get<INT> to be used.  
class SType
{
    Base *d_base;

    public:
        SType();
        SType(SType const &other);
        SType(SType &&tmp);

        ~SType();

            // Specific overloads are needed for SType = SType assignments
        SType &operator=(SType const &rhs);
        SType &operator=(SType &&tmp);

            // A template member operator= is used because it allows
            // the compiler to deduce the appropriate typename
        template <typename Type>
        SType &operator=(Type const &value);

        template <Tag__ tag, typename ...Args>
        void assign(Args &&...args);
    
            // By default the get()-members check whether the specified <tag>
            // matches the tag returned by SType::tag (d_data's tag). If they
            // don't match a run-time fatal error results.
        template <Tag__ tag>
        typename TypeOf<tag>::type &get();

        template <Tag__ tag>
        typename TypeOf<tag>::type const &get() const;

        Tag__ tag() const;

        bool valid() const;

        void swap(SType &other);
};

template <Tag__ tg_>
Semantic<tg_>::Semantic()
{
    d_baseTag = tg_;                // Base's data member:
}

template <Tag__ tg_>
Semantic<tg_>::Semantic(Semantic<tg_> const &other)
:
    d_data(other.d_data)
{
    d_baseTag = other.d_baseTag;
}

template <Tag__ tg_>
template <typename ...Params>
Semantic<tg_>::Semantic(Params &&...params)
:
    d_data(std::forward<Params>(params) ...)
{
    d_baseTag = tg_;
}

inline Tag__ SType::tag() const
{
    return d_base->tag();
}

inline bool SType::valid() const
{
    return tag() != static_cast<Tag__>(sizeofTag);
}

template <Tag__ tag, typename ...Args>
void SType::assign(Args &&...args)
{
    Semantic<tag> *semPtr = new Semantic<tag>(std::forward<Args>(args) ...);
    delete d_base;
    d_base = semPtr;
}

template <Tag__ tg>
typename TypeOf<tg>::type &SType::get()
{
$insert warnTagMismatches
    return *static_cast<typename TypeOf<tg>::type *>(d_base->data());
}

template <Tag__ tg>
typename TypeOf<tg>::type const &SType::get() const
{
$insert warnTagMismatches
    return *static_cast<typename TypeOf<tg>::type *>(d_base->data());
}

inline SType::SType()
:
    d_base(new Base)        // default Base object doesn't do anyting
{}                          // but prevents tests for d_base == 0

inline SType::SType(SType const &other)
:
    d_base(other.d_base->clone())
{}

inline SType::SType(SType &&tmp)
:
    d_base(tmp.d_base)
{
    tmp.d_base = 0;
}

inline SType::~SType()
{
    delete d_base;
}

inline SType &SType::operator=(SType &&tmp)
{
    swap(tmp);
    return *this;
}

    // A template assignment function is used because it allows
    // the compiler to deduce the appropriate typename
template <typename Type>
inline SType &SType::operator=(Type const &value)
{
    assign< TagOf<Type>::tag >(value);
    return *this;
}

template <Tag__ tg_>
Base *Semantic<tg_>::vClone() const
{
    return new Semantic<tg_>{*this};
}

template <Tag__ tg_>
void *Semantic<tg_>::vData() const 
{
    return const_cast<typename TypeOf<tg_>::type *>(&d_data);
}

}  // namespace Meta__
