Boost Reflect Library 0.1.0
|
There are two primary uses for the Boost.Reflect library:
Basic reflection of a type is achieved using the BOOST_REFLECT(TYPE,INHERITS,MEMBERS) macro to specify base classes and class members. Below is an example of reflecting a struct.
struct my_struct { int hello; std::string world; }; BOOST_REFLECT( my_struct, (hello)(world) )
Now that we have reflected the struct, we can dump it to XML with a simple visitor.
template<typename T> struct xml_printer { xml_printer( const T& c ):self(c){} template<typename Type> static xml_printer<Type> make( const Type& t ) { return xml_printer<Type>(t); } template<typename Member, typename Class, Member Class::*p> void operator()( const char* name )const { std::cerr<<"<"<<name<<">"<<(self.*p)<<"</"<<name<<">\n"; } const T& self; };
The visitor can then be applied like so:
my_struct s; boost::reflect::reflector<my_struct>::visit( xml_printer<my_struct>(s) ); boost::reflect::reflector<my_struct>::visit( xml_printer::make(s) );
Type Erasure is one means to hide implementation details by defining a type that can contain any object that implements the required interface. Examples are boost::any and boost::function. Boost.Reflect enables the rapid generation of new type erasures for arbitrary interfaces via the any_ptr<Interface> type.
boost::reflect::any_ptr can hold a pointer or shared pointer to any type that implements a particular reflected interface.
Here is an example on how to define a new type erasure for two interfaces, Service and Calculator.
struct Service { std::string name()const; int exit(); }; struct Calculator : Service { double add( double v ); double sub( double v ); double mult( double v ); double div( double v ); double result()const; }; BOOST_REFLECT_ANY( Service,(name)(exit) ) BOOST_REFLECT_ANY_DERIVED( Calculator, (Service), (add)(sub)(mult)(div)(result) )
class CalculatorService { public: CalculatorService():m_result(0){} std::string name()const { return "CalculatorService"; } int exit() { ::exit(0); } double add( double v ) { return m_result += v; } double sub( double v ) { return m_result -= v; } double mult( double v ) { return m_result *= v; } double div( double v ) { return m_result /= v; } double result()const { return m_result; } private: double m_result; }; void try_it() { reflect::any_ptr<Calculator> calc( new CalculatorService() ); calc->add(5); calc->add(6); std::string name = calc->name(); assert( calc->result() == 11 ); }
Some things to note, CalculatorService did not inherit either Calculator or Service, it simply exposed all of the methods defined by the interface. If it looks like a Duck, quacks like a Duck, then it is a Duck.
The interface of any_ptr<Calculator> is 'identical' to the Calculator defined above.
Pointer semantics were chosen to discourage using reflect::anys as values where assignment and copy construction creates a new instance. Initialization of any_ptr with a new pointer is a relatively expensive operation and value semantics would create objects that are much bigger and more expensive to copy than traditional types as each method is a functor that must be initialized at copy/construction time.
Generally speaking, any_ptr<> should be used to abstract long-lived objects and not for temporaries.
© Daniel Larimer 2010-2011 - Licensed under Boost Software License, Version 1.0 | Boost Reflect Library |