c++boost-anyboost-type-erasure

Why does this type erasure implementation (simplified boost:any) gives segmentation fault?


I was trying my own implementation of a generic container of arbitrary type (similar to boost:any) just to learn about the idiom, and I am trying to understand why my code gives a segmentation fault.

class IValue
{
public:
    IValue() = default;
    virtual ~IValue() = default;
};

template<typename T>
class Value : public IValue
{
public:
    Value(T value) : val(value){}

    virtual ~Value() override
    {
    }

    T get()
    {
        return val;
    }
private:
    T val;
};

class any
{

public:
    template<typename U>
    any(U element)
    {
        elem = new Value<U>(element);
    }

    ~any()
    {
        delete elem;
    }

    IValue* get_type_erased_value()
    {
        return elem;
    }


private:
    IValue* elem;
};

template<typename T>
T any_cast(any a)
{
    Value<T>* stored_element = dynamic_cast<Value<T>*>(a.get_type_erased_value());

    if(stored_element)
    {
        return stored_element->get();
    }
    else
    {
        cout << "Any cast failed. Could not cast down to required type\n";
        return T();
    }

}

struct foo
{
    int a;
    float b;
    char c;
};

ostream& operator<<(ostream& stream, foo& a)
{
    stream << '{' << a.a << ',' << a.b << ',' << a.c << '}' << '\n';
    return stream;
}

int main()
{
    any a = string("Any string value");

    any b = int(17);

    any c = foo({27,3.5,'g'});

    string s = any_cast<string>(a);
    int i = any_cast<int>(b);
    foo f = any_cast<foo>(c);

    cout << "Any string contained: " << s << '\n';
    cout << "Any int contained: " << i << '\n';
    cout << "Any struct foo contained: " << f << '\n';

    return 0;
}

The output is what I desire and the casts seem to work correctly, but the program always crashes at the end. There must be something wrong with the destructor not being called correctly or the deallocation of the pointer. Can someone give me a hint?

Thanks


Solution

  • The implicitly defined copy constructor of your any type just copies the IValue pointer, so both the original object and the copy will delete the same pointer. You need to write a copy constructor that actually copies the stored object.