I want to fill a map with class name and method, a unique identifier and a pointer to the method.
typedef std::map<std::string, std::string, std::string, int> actions_type; typedef actions_type::iterator actions_iterator; actions_type actions; actions.insert(make_pair(class_name, attribute_name, identifier, method_pointer)); //after which I want call the appropriate method in the loop while (the_app_is_running) { std::string requested_class = get_requested_class(); std::string requested_method = get_requested_method(); //determine class for(actions_iterator ita = actions.begin(); ita != actions.end(); ++ita) { if (ita->first == requested_class && ita->second == requested_method) { //class and method match //create a new class instance //call method } } }
If the method is static then a simple pointer is enough and the problem is simple, but I want to dynamically create the object so I need to store a pointer to class and an offset for the method and I don’t know if this works (if the offset is always the same etc).
The problem is that C++ lacks reflection, the equivalent code in a interpreted language with reflection should look like this (example in PHP):
$actions = array ( 'first_identifier' => array('Class1','method1'), 'second_identifier' => array('Class2','method2'), 'third_identifier' => array('Class3','method3') ); while ($the_app_is_running) { $id = get_identifier(); foreach($actions as $identifier => $action) { if ($id == $identifier) { $className = $action[0]; $methodName = $action[1]; $object = new $className() ; $method = new ReflectionMethod($className , $methodName); $method -> invoke($object); } } }
PS: Yes I’m trying to make a (web) MVC front controller in C++. I know I know why don’t use PHP, Ruby, Python (insert your favorite web language here) etc?, I just want C++.
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::variantfor 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 ofAcan’t be stored to a pointer to a member ofB. Those types are incompatible. To solve this, i store the function pointers in an object that overloads itsoperator()and takes a boost::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:
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:
Now you can call member functions that take an int. Here is how the call side would look:
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:
For creating objects, a usual factory will do
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:
Calling the function would work like this:
Of course, with this approach, you loose flexibility and (possibly, haven’t profiled) efficiency, but you greatly simplify your design.