tags:

views:

170

answers:

3

Boost comes with an example file in

boost_1_41_0\libs\function_types\example

called interpreter.hpp and interpreter_example.hpp

I am trying to create a situation where I have a bunch of functions of different arguments, return types, etc all register and be recorded to a single location. Then have the ability to pull out a function and execute it with some params.

After reading a few questions here, and from a few other sources I think the design implemented in this example file is as good as I will be able to get. It takes a function of any type and allows you to call it using a string argument list, which is parsed into the right data types. Basically its a console command interpreter, and thats probably what its meant to illustrate.

I have been studying the code and poking around trying to get the same implementation to accept class member functions, but have been unsuccessful so far. I was wondering if someone could suggest the modifications needed, or maybe worked on something similar and have some same code.

In the example you'll see

interpreter.register_function("echo", & echo);
interpreter.register_function("add", & add);
interpreter.register_function("repeat", & repeat);

I want to do something like

test x;
interpreter.register_function("classFunc", boost::bind( &test::classFunc, &x ) );

But this breaks the any number of arguments feature. So I am thinking some kind of auto generating boost::bind( &test::classFunc, &x, _1, _2, _3 ... ) would be the ticket, I just am unsure of the best way to implement it.

Thanks

A: 

One option is to make a set of templates

template <class T, class Ret>
void register_function(const char *name, Ret (T::*fn)()) { /* boost::bind or your way to register here */ }

template <class T, class Ret, class Arg1>
void register_function(const char *name, Ret (T::*fn)(Arg1)) { /*...*/ )

And so on.. Until C++0x come with its variadic templates, you can use Boost.Preprocessor to generate required amount of templates

Xeor
+1  A: 

I am not into fusion and therefore don't see how to fix it in a simple and elegant way (i mainly don't see how member functions are supposed to work), but i worked on something similar that might be an alternative for you.
If you want to take a look at the result, it is in the Firebreath repository.

In short:

The main changes would probably involve to strip the FB-specific types, tokenize the input sequence before invoking the functors and supply your own conversion functions.

Georg Fritzsche
Some interesting code, seems like its very similar to what I want and what the example code is doing, except with member functions.I can tell that we have both run into the same problem and you chose to go the variant route. I might end up there as well, but I cannot help but think that it could be much cleaner. An auto sig tockenizer and converter like this example presents would be ideal.
Charles
The variant route has to be taken there as they come from the scripting enviroment, one could feed strings in as well. But i agree, the fusion example looks quite elegant. I am curious though wether they support member functions at all.
Georg Fritzsche
+2  A: 

I've been working on this issue and i've somewhat succeeded to make the boost interpreter accept the member function such as:

    // Registers a function with the interpreter, will not compile if it's a member function.
template<typename Function>
typename boost::enable_if< ft::is_nonmember_callable_builtin<Function> >::type
register_function(std::string const& name, Function f);

// Registers a member function with the interpreter. Will not compile if it's a non-member function.
template<typename Function, typename TheClass>
typename boost::enable_if< ft::is_member_function_pointer<Function> >::type
register_function(std::string const& name, Function f, TheClass* concerned_object);

The enable_if statement is used to prevent the use of the wrong method at the compile time. Now, what you need to understand :

  • It uses the boost::mpl to parse trough the argument's parameter types of the callable builtin (which is basically a function pointer)
  • Then, prepares a fusion vector at the compile-time (which is a vector that can stock different objects of different types at the same time)
  • When the mpl is done parsing every arguments, the "parsing" apply method will fork in the "invoke" apply method, following the templates.
  • The main issue is that the first argument of a member callable builtin is the object which holds the called method.
  • As far a I know, the mpl cannot parse the arguments of something else than a callable builtin (i.e A Boost::Bind result)

So, what needs to be done is simply add one step to the "parsing" apply, which would be to add the concerned object to the apply loop! Here it goes:

template<typename Function, typename TheClass>
typename boost::enable_if< ft::is_member_function_pointer<Function> >::type
interpreter::register_function(std::string const& name, Function f, TheClass*     concerned_object);
{   
// instantiate and store the invoker by name
this->map_invokers[name] = boost::bind(& invoker<Function>::template         apply_object<fusion::nil,TheClass>,f,theclass,_1,fusion::nil());
}

template<typename Args, typename TheClass>
static inline
    void apply_object(Function func, TheClass* theclass, parameters_parser & parser, Args const & args)
{
    typedef typename mpl::next<From>::type next_iter_type;

    interpreter::invoker<Function, next_iter_type, To>::apply
        ( func, parser, fusion::push_back(args, theclass) );        
}

This way, it will simply skip the first argument type and parse everything correctly. The method can be called this way: invoker.register_function("SomeMethod",&TheClass::TheMethod,&my_object);

Alexandre Deschamps