\$\begingroup\$
\$\endgroup\$
9
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);
}
200_success
145k22 gold badges190 silver badges478 bronze badges
asked Dec 31, 2012 at 11:42
user1131146 account abandoned user1131146 account abandoned
7731 gold badge10 silver badges19 bronze badges
-
1\$\begingroup\$ This seems not compiling on Visual Studio 2010... anybody tried on VS 2010? \$\endgroup\$Dan Niero– Dan Niero2013年08月21日 08:36:22 +00:00Commented 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– sepp2k2013年08月21日 13:07:35 +00:00Commented 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– user324382013年11月21日 14:54:51 +00:00Commented Nov 21, 2013 at 14:54
-
\$\begingroup\$ What's your license on this code? \$\endgroup\$user7535– user75352014年04月21日 06:34:25 +00:00Commented Apr 21, 2014 at 6:34
-
2\$\begingroup\$ @DigitalArchitect: Public domain. You may use it any way you like without attribution. \$\endgroup\$user1131146 account abandoned– user1131146 account abandoned2014年04月21日 06:39:33 +00:00Commented Apr 21, 2014 at 6:39
2 Answers 2
\$\begingroup\$
\$\endgroup\$
0
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;
answered Dec 31, 2012 at 11:53
\$\begingroup\$
\$\endgroup\$
~Any() { if (ptr) delete ptr; }
No need to check for nullptr here because delete
ignores null pointers.
200_success
145k22 gold badges190 silver badges478 bronze badges
answered Apr 17, 2015 at 18:44
lang-cpp