boost::any solves this problem. Internally boost::any uses a templates to create a wrapper class for the type you pass to it, and then create an instance of this class on the heap. It then manages retrieving the object inside using boost::any_cast safely.
Examples:
#include <boost/any.hpp>
#include <iostream>
#include <string>
#include <vector>
class A
{
int value_;
public:
A(int value) : value_(value) {}
void print() const {std::cout << "A's print: " << value_ << std::endl; }
};
class B
{
public:
void print() const {std::cout << "B's print" << std::endl; }
};
class C
{
public:
void print() const {std::cout << "C's print" << std::endl; }
};
int main()
{
typedef std::vector<boost::any> MixedContainer_t;
MixedContainer_t mixedContainer;
mixedContainer.push_back(A(1));
try
{
A a = boost::any_cast<A> (mixedContainer.back());
a.print();
}
catch(boost::bad_any_cast& bac)
{
bac.what();
}
//you can also retrieve the object via a pointer using the
//the overloaded boost::any_cast
if(A* a = boost::any_cast<A> (&mixedContainer.back()))
{
std::cout << "I can retrieve A by pointer" << std::endl;
}
boost::any a1 = mixedContainer.back();
if(typeid(A*) == a1.type())
std::cout << "last element is of A's type " << std::endl;
mixedContainer.push_back(B());
mixedContainer.push_back(C());
//push a string
std::string str("Hello World!");
mixedContainer.push_back(str);
void print_any(boost::any& element); //declaration
std::for_each(mixedContainer.begin(),
mixedContainer.end(),
print_any);
return 0;
}
void print_any(boost::any& element)
{
try
{
A a = boost::any_cast<A> (element);
a.print();
}
catch(boost::bad_any_cast& ex)
{
std::cout << "bad cast exception" << ex.what();
}
try
{
B b = boost::any_cast<B> (element);
b.print();
}
catch(boost::bad_any_cast& ex)
{
std::cout << "bad cast exception" << ex.what();
}
try
{
C c = boost::any_cast<C> (element);
c.print();
}
catch(boost::bad_any_cast& ex)
{
std::cout << "bad cast exception" << ex.what();
}
}
As you can see in the previous example, we can store any type in boost::any. Boost::any preserves the type and will not let you tamper with it without knowing the correct type. You can store pointers too, but retrieving pointers is a bit tricky and I'll discuss it in a later post. For now, we are interested in the two mechanism for retrieving the values stores in boost::any:
template<typename ValueType>
ValueType any_cast(const any & operand);
The argument is the boost::any we want to retrieve, and the ValueType is the type of the stored value. If the type does not correspond to what is stored in boost::any, then this version of any_cast will throw a bad_any_cast exception.
template<typename ValueType>
ValueType * any_cast(any * operand)
This overloaded any_cast takes a pointer to any, and returns a pointer to the stored value. If the type in the any isn 't Value_Type, a NULL is returned. There is also a version of this any_cast for const pointers.
Note the difference between the first mechanism where an exception is thrown, and the second where a null pointer is returned. Also note that we can use any of the overloaded any_cast exception to retrieve the value by either passing our argument by reference (to use the first mechanism) or value (to use the second) as we show in the preceding example:
mixedContainer.push_back(A(1));
try
{
A a = boost::any_cast<A> (mixedContainer.back());
a.print();
}
catch(...)
{}
if(A* a = boost::any_cast<A> (&mixedContainer.back()))
{
std::cout << "I can retrieve A by pointer" << std::endl;
}
References:
- Introduction to Boost by Bjorn Karlsson
- the boost website
No comments :
Post a Comment