27
\$\begingroup\$

This is a polymorphic wrapper capable of holding any type. (It is loosely based on boost::any)

In particular, this is useful when you want to store a heterogeneous collection, such as vector<Any>.

Synopsis

string s = ...;
int i = ...;

Any a1 = s;
Any a2 = i;

int j = a2; // ok j now equals i

string t = a1; // ok t now equals s

int k = a1; // runtime exception bad_cast

vector<Any> v;

v.push_back("foo");
v.push_back(42);

const char* s = v[0];
int l = v[1];

Implementation

#include <type_traits>
#include <utility>
#include <typeinfo>
#include <string>
#include <cassert>

using namespace std;

template<class T>
using StorageType = typename decay<typename remove_reference<T>::type>::type;

struct Any
{
    bool is_null() const { return !ptr; }
    bool not_null() const { return ptr; }

    template<typename U> Any(U&& value)
        : ptr(new Derived<StorageType<U>>(forward<U>(value)))
    {

    }

    template<class U> bool is() const
    {
        typedef StorageType<U> T;

        auto derived = dynamic_cast<Derived<T>*> (ptr);

        return derived;
    }

    template<class U>
    StorageType<U>& as()
    {
        typedef StorageType<U> T;

        auto derived = dynamic_cast<Derived<T>*> (ptr);

        if (!derived)
            throw bad_cast();

        return derived->value;
    }

    template<class U>
    operator U()
    {
        return as<StorageType<U>>();
    }

    Any()
        : ptr(nullptr)
    {

    }

    Any(Any& that)
        : ptr(that.clone())
    {

    }

    Any(Any&& that)
        : ptr(that.ptr)
    {
        that.ptr = nullptr;
    }

    Any(const Any& that)
        : ptr(that.clone())
    {

    }

    Any(const Any&& that)
        : ptr(that.clone())
    {

    }

    Any& operator=(const Any& a)
    {
        if (ptr == a.ptr)
            return *this;

        auto old_ptr = ptr;

        ptr = a.clone();

        if (old_ptr)
            delete old_ptr;

        return *this;
    }

    Any& operator=(Any&& a)
    {
        if (ptr == a.ptr)
            return *this;

        swap(ptr, a.ptr);

        return *this;
    }

    ~Any()
    {
        if (ptr)
            delete ptr;
    }

private:
    struct Base
    {
        virtual ~Base() {}

        virtual Base* clone() const = 0;
    };

    template<typename T>
    struct Derived : Base
    {
        template<typename U> Derived(U&& value) : value(forward<U>(value)) { }

        T value;

        Base* clone() const { return new Derived<T>(value); }
    };

    Base* clone() const
    {
        if (ptr)
            return ptr->clone();
        else
            return nullptr;
    }

    Base* ptr;
};

Test

int main()
{
    Any n;
    assert(n.is_null());

    string s1 = "foo";

    Any a1 = s1;

    assert(a1.not_null());
    assert(a1.is<string>());
    assert(!a1.is<int>());

    Any a2(a1);

    assert(a2.not_null());
    assert(a2.is<string>());
    assert(!a2.is<int>());

    string s2 = a2;

    assert(s1 == s2);
}
\$\endgroup\$
9
  • 1
    \$\begingroup\$ This seems not compiling on Visual Studio 2010... anybody tried on VS 2010? \$\endgroup\$
    – Dan Niero
    Aug 21, 2013 at 8:36
  • 2
    \$\begingroup\$ @DanNiero The C++11 support in Visual Studio (especially the 2010 version) is incomplete. It lacks various features including template aliases (which, I think, the 2012 version lacks as well), so you won't get this code to compile in Visual Studio. \$\endgroup\$
    – sepp2k
    Aug 21, 2013 at 13:07
  • \$\begingroup\$ Does it compile with VS2012? At least, not on my side. I've received a lot of compilation errors. \$\endgroup\$
    – user32438
    Nov 21, 2013 at 14:54
  • \$\begingroup\$ What's your license on this code? \$\endgroup\$
    – user7535
    Apr 21, 2014 at 6:34
  • 2
    \$\begingroup\$ @DigitalArchitect: Public domain. You may use it any way you like without attribution. \$\endgroup\$ Apr 21, 2014 at 6:39

2 Answers 2

15
\$\begingroup\$
template<class T>
using StorageType = typename decay<typename remove_reference<T>::type>::type;

This appears unnecessarily complex: 'decay' already removes a reference. Consider using:

template <class T>
using StorageType = typename decay<T>::type; 
\$\endgroup\$
0
4
\$\begingroup\$
~Any()
{
    if (ptr)
        delete ptr;
}

No need to check for nullptr here because delete ignores null pointers.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.