I wrote that stuff last hours, and added it to my collection of useful stuff. The most difficult thing is to cope with the factory function, if the types you want to create are not related in any way. I used a boost::variant
for this. You have to give it a set of types you ever want to use. Then it will keep track what is the current "active" type in the variant. (boost::variant is a so-called discriminated union). The second problem is how you store your function pointers. The problem is that a pointer to a member of A
can't be stored to a pointer to a member of B
. Those types are incompatible. To solve this, i store the function pointers in an object that overloads its operator()
and takes a boost::variant:
return_type operator()(variant)
Of course, all your types' functions have to have the same return type. Otherwise the whole game would only make little sense. Now the code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
It uses pretty fun techniques from boost preprocessor, function types and bind library. Might loop complicated, but if you get the keys in that code, it's not much to grasp anymore. If you want to change the parameter count, you just have to tweak variant_call_type:
typedef boost::function variant_call_type;
Now you can call member functions that take an int. Here is how the call side would look:
return factory[class_method].second(v, 42);
Have fun!
If you now say the above is too complicated, i have to agree with you. It is complicated because C++ is not really made for such dynamic use. If you can have your methods grouped and implemented in each object you want create, you can use pure virtual functions. Alternatively, you could throw some exception (like std::runtime_error) in the default implementation, so derived classes do not need to implement everything:
struct my_object {
typedef std::string return_type;
virtual ~my_object() { }
virtual std::string one() { not_implemented(); }
virtual std::string two() { not_implemented(); }
private:
void not_implemented() { throw std::runtime_error("not implemented"); }
};
For creating objects, a usual factory will do
struct object_factory {
boost::shared_ptr create_instance(std::string const& name) {
// ...
}
};
The map could be composed by a map mapping IDs to a pair of class and function name (the same like above), and a map mapping that to a boost::function:
typedef boost::function function_type;
typedef std::map< std::pair, function_type>
class_method_map_type;
class_method_map[actions["first"]] = &my_object::one;
class_method_map[actions["second"]] = &my_object::two;
Calling the function would work like this:
boost::shared_ptr p(get_factory().
create_instance(actions["first"].first));
std::cout << class_method_map[actions["first"]](*p);
Of course, with this approach, you loose flexibility and (possibly, haven't profiled) efficiency, but you greatly simplify your design.