namespace Meta__
{
    template <Tag__ tag>
    struct TypeOfBase;

    template <typename Tp_>
    struct TagOf;

$insert polymorphicSpecializations

        // determining the nature of a polymorphic semantic value:
        // if it's a class-type, use 'Type const &' as returntype of const
        // functions; if it's a built-in type (like 'int') use Type:

    struct ClassType
    {
        char _[2];
    };
    
    struct BuiltinType
    {
        char _;
    };

    template <typename T>
    BuiltinType test(...);

    template <typename T>
    ClassType test(void (T::*)());

    template <Tag__ tg_>
    struct TypeOf: public TypeOfBase<tg_>
    {
        typedef typename TypeOfBase<tg_>::DataType DataType;
        enum: bool 
        { 
            isBuiltinType = sizeof(test<DataType>(0)) == sizeof(BuiltinType)
        };

        typedef typename std::conditional<
                    isBuiltinType, DataType, DataType const &
                >::type ReturnType;
    };

        // The Base class: 
        // Individual semantic value classes are derived from this class.
        // This class offers a member returning the value's Tag__
        // and two member templates get() offering const/non-const access to
        // the actual semantic value type.
    class Base
    {
        Tag__ d_tag;
    
        protected:
            Base(Tag__ tag);

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

            Tag__ tag() const;
    
            template <Tag__ tg_>
            typename TypeOf<tg_>::ReturnType get() const;
    
            template <Tag__ tg_>
            typename TypeOf<tg_>::DataType &get();
    };
    
        // The class Semantic is derived from Base. It stores a particular
        // semantic value type. The stored data are declared 'mutable' to
        // allow the definitions of a const and non-const conversion operator.
        // This way, const objects continue to offer non-modifiable data
    template <Tag__ tg_>
    class Semantic: public Base
    {
        typedef typename TypeOf<tg_>::DataType DataType;
    
        mutable DataType d_data;
    
        public:
            typedef typename TypeOf<tg_>::ReturnType ReturnType;
    
                // The default constructor and constructors for 
                // defined data types are available
            Semantic();
            Semantic(DataType const &data);
            Semantic(DataType &&tmp);

                // Conversion operators allow const/non-const access to d_data
            operator ReturnType() const;
            operator DataType &();
    };

        // The class Stype wraps the shared_ptr holding a pointer to Base.
        // It becomes the polymorphic STYPE__
        // Constructors expect (l/r-value) references to defined semantic
        // value types.
        // It also wraps Base's get members, allowing constructions like
        // $$.get<INT> to be used, rather than $$->get<INT>.
        // Furthermore, its operator= can be used to assign a Semantic *
        // directly to the SType object. The free functions (in the parser's
        // namespace (if defined)) semantic__ can be used to obtain a 
        // Semantic *. 
    class SType: public std::shared_ptr<Base>
    {
        public:
            SType() = default;
            SType(SType const &other) = default;
            SType(SType &&tmp) = default;
        
            SType &operator=(SType const &rhs) = default;
            SType &operator=(SType &&tmp) = default;
            template <typename Tp_>
            SType &operator=(Tp_ &&value);

            Tag__ tag() const;

                // this get()-member checks for 0-pointer and correct tag 
                // in shared_ptr<Base>, and may throw a logic_error
            template <Tag__ tg_>                    
            typename TypeOf<tg_>::ReturnType get() const;
    
                // this get()-member checks for 0-pointer and correct tag 
                // in shared_ptr<Base>, and resets the shared_ptr's Base * 
                // to point to Meta::__Semantic<tg_>() if not
            template <Tag__ tg_>
            typename TypeOf<tg_>::DataType &get();

                // the data()-members do not check, and may result in a 
                // bad_cast exception or segfault if used incorrectly

            template <Tag__ tg_>
            typename TypeOf<tg_>::ReturnType data() const;

            template <Tag__ tg_>
            typename TypeOf<tg_>::DataType &data();
    };

}  // namespace Meta__
