Search code examples
c++oopderived-classboost-any

Accessing Values in a Class Similar to boost::any


I'm making a simple boost::any-like class for educational purposes, but I can't figure out how to access the stored value. I can set the value perfectly, but when I try to access any member in the "holder" class the compiler just complains that the member wasn't found in the class it was derived from. I can't declare the members as virtual because of the templates.

Here's the relevant code:

class Element
{
    struct ValueStorageBase
    {
    };

    template <typename Datatype>
    struct ValueStorage: public ValueStorageBase
    {
        Datatype Value;

        ValueStorage(Datatype InitialValue)
        {
            Value = InitialValue;
        }
    };

    ValueStorageBase* StoredValue;

public:

    template <typename Datatype>
    Element(Datatype InitialValue)
    {
        StoredValue = new ValueStorage<Datatype>(InitialValue);
    }

    template <typename Datatype>
    Datatype Get()
    {
        return StoredValue->Value; // Error: "struct Element::ValueStorageBase" has no member named "Value."
    }
};

Solution

  • It's fine to add virtual functions to templates- just the functions themselves cannot be templates. A templated class or struct can still have virtual functions just fine. You need to use the magic of dynamic_cast.

    class Element
    {
        struct ValueStorageBase
        {
            virtual ~ValueStorageBase() {}
        };
    
        template <typename Datatype>
        struct ValueStorage: public ValueStorageBase
        {
            Datatype Value;
    
            ValueStorage(Datatype InitialValue)
            {
                Value = InitialValue;
            }
        };
    
        ValueStorageBase* StoredValue;
    
    public:
    
        template <typename Datatype>
        Element(Datatype InitialValue)
        {
            StoredValue = new ValueStorage<Datatype>(InitialValue);
        }
    
        template <typename Datatype>
        Datatype Get()
        {
            if(ValueStorage<DataType>* ptr = dynamic_cast<ValueStorage<DataType>*>(StoredValue)) {
                return ptr->Value;
            else
                throw std::runtime_error("Incorrect type!"); // Error: "struct Element::ValueStorageBase" has no member named "Value."
        }
    };
    

    If you change Get to return a Datatype* you can return NULL instead of throwing. You also haven't handled the memory of the previous value of StoredValue, but I'm leaving that up to you.