views:

353

answers:

7

I have a class that uses functors as units of work. It accepts a reference to a functor in its Run() method. To allow this class to operate on any functor, all these functors must derive from my base functor class which looks like this:

class baseFunctor{

public:
    virtual void operator()()=0;
    virtual baseFunctor Clone()=0;
};

This works, however obviously it restricts these functors to having an operator method that returns void and accepts no parameters. I need to be able to accept a functor in my class that can take any type of parameters and return anything. Its apparently do-able but I can't seem to find a way to do it. I have considered using templates, multiple inheritance, but I keep getting thwarted by the fact that the class that needs to run this functor must be able to accept any type, so will accept the base class type, and so will not know the actual type of the functor.

Any suggestions of what avenue to look at would be appreciated.

A: 

Why'd you want to return the functor? Are you storing some state as well? Some more detail will be much appreciated since it is not very clear what exactly you want to do.

If you plan to use inheritance, do look up Covariant Return Types (and the Virtual Constructor idiom).

Now, for the meat of the problem: the problem is really not with passing in a functor but with functor application. You may want to take a look at boost::lambda and boost::parameter as well.

dirkgently
A: 

I think you want an ellipsis argument, like varargs for C++.

Charlie Martin
+1  A: 

If you are open to using the Boost library (www.boost.org), you might find Boot.Bind and Boost.Function of particular interest. I have used them in the past to achieve something very much along the lines of what you are discussing.

If you use Boost.Bind, you can perform currying on the functors to account for differences between the number of arguments the functor expects and the number of arguments the Run method expects (i.e., zero). The code that creates the functor would have to bind any arguments to specific values and thus create a zero-argument functor that can be passed to Run().

MV

Martin Vuille
+2  A: 

How will the class that calls the functor know what parameters to provide and what to do with the return value, if any?

anon
A: 

Perhaps std::tr1::function is interesting for you?

SebastianK
+1  A: 

So, if I'm reading this right, you have a "Visitor pattern." It might be a good thing for you to look up.

Someone needs to know what type the functor is to give it arguments. Often with functors, the arguments are assigned to fields of the derived class, and operator() will operate on those fields. That is, the dumb method that calls the functor and doesn't know anything about it is given the closure (method plus arguments all in one class) by someone more knowledgeable.

If you do want generic functors that take multiple arguments in the operator(), templating will get you partway there, but you'll need one per arity.

Drew Hoskins
Ok, that makes sense. The main class, that creates the class and passes it the functor knows what type the functor is, its the class that recieves the functor that does not, and just calls the operator() method. I will look into the visitor pattern, thanks for the info.
Sam Cogan
+1  A: 

I agree with Neil. Your main class has to know what parameters to pass and what return value to expect from these functors. Can you just type-cast your "functor" to an appropriate class that supports the function with the necessary arguments and return value?

class baseFunctor
{
};

class functor1x2: public baseFunctor
{
public:
    virtual void* execute(void*, void*);

}

class MainClass
{
public:
   void Execute(baseFunctor* ipFunctor)
   {
      functor1x2* lpFunctor1x2 = dynamic_cast<functor1x2*>(ipFunctor);
      if(lpFunctor1x2)
      {
         lpFunctor1x2->execute(NULL, NULL);
      }
   }
}

I'm not sure what could be accomplished with this approach that couldn't more easily be accomplished with the Visitor pattern, as Drew noted.

Andrew Garrison