3

I was trying to built atop the thread here: Variable length template arguments list? to have a default Functor class, this is only of academic interest. My goal is to build a generic Fucntor class: given a class name, method name and argument types(of variable length), it builds a class that has an operator() method which takes variable number of arguments of type specified in template args and takes a pointer and applies the given method. Imagine a class thus:

class MyClass
{
 public:
   float Fraction( float n, int m)
   {
       return n/m;
   }
   int Increment(int n)
   {
       return n+1;
   }
} ;

And a templatized functor class that can be used in any function thus:

int k = FunctorClass<MyClass, Increment, int, int /*return type*/> (3);
assert(k == 4);
float l = FunctorClass<MyClass, Fraction,  float, int, float, /*return type*/> (4,3);
assert(l == (4/3));

Can such a functor class be constructed? Sidenote: Cant use Variadic templates, (building in VS2010, no ... template arguments) Thanks for the help

Community
  • 1
  • 1
user1186270
  • 33
  • 1
  • 4

2 Answers2

3

This is certainly doable, e.g. Boost bind() uses this approach under the hood. Without variadics you won't get full generality, however, because you will be limited to a fixed number of template arguments and you need to type the implementation for each different number of arguments you want to support. Also, without rvalue references you won't get perfect forwarding.

That said, the way you are trying to use it won't work: when stating the member functions, you can't just name them. You need to obtain the correct member function point using e.g. &MyClass::Increment and &MyClass::Fraction. If the member function is overloaded, you need to disambiguate it.

Since you apparently want to enable the use of this function object for non-static member functions, you also need to provide an object on which the member function is to be called. The most reasonable approach for this is to pass a reference to the object as a constructor argument of the function object class and to store it to be used whenever the function is being called. That is, the use looks somewhat different but it can be simplified with some sort of factory function. Here is a version which adjusts the various things and implements a corresponding function object template:

#include <cassert>

// -----------------------------------------------------------------------------

template <typename T, T> class FunctorClass;

template <typename RC, typename Class,
          RC (Class::*Member)()>
class FunctorClass<RC (Class::*)(), Member>
{
public:
    FunctorClass(Class& object): object_(&object) {}
    RC operator()() const { return (this->object_->*Member)(); }
private:
    Class* object_;
};

template <typename RC, typename Class, typename A0,
          RC (Class::*Member)(A0)>
class FunctorClass<RC (Class::*)(A0), Member>
{
public:
    FunctorClass(Class& object): object_(&object) {}
    RC operator()(A0 a0) const { return (this->object_->*Member)(a0); }
private:
    Class* object_;
};

template <typename RC, typename Class, typename A0, typename A1,
          RC (Class::*Member)(A0, A1)>
class FunctorClass<RC (Class::*)(A0, A1), Member>
{
public:
    FunctorClass(Class& object): object_(&object) {}
    RC operator()(A0 a0, A1 a1) const { return (this->object_->*Member)(a0, a1); }
private:
    Class* object_;
};

// -----------------------------------------------------------------------------

class MyClass
{
 public:
    int foo() { return 17; }
    float Fraction( float n, int m)
    {
        return n/m;
    }
    int Increment(int n)
    {
        return n+1;
    }
};

int main()
{
    MyClass object;
    int i = FunctorClass<int (MyClass::*)(), &MyClass::foo>(object)();
    assert(i == 17);

    int k = FunctorClass<int (MyClass::*)(int), &MyClass::Increment>(object)(3);
    assert(k == 4);
    float l = FunctorClass<float (MyClass::*)(float, int), &MyClass::Fraction>(object)(4,3);
    assert(l == (4.0f/3));
}
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Thank you for the well thought out answers. Yes, my case is for non-static members and have to send in the object. Can you elaborate a little bit on this: Also, without rvalue references you won't get perfect forwarding. ? thanks again – user1186270 Feb 19 '12 at 17:30
  • C++2011 support rvalues references (spelled `T&&` for some type `T`) and special template deduction rules if a type if a function template takes an rvalue reference as parameter. This allows to capture how the parameter was passed (i.e. if it came from an lvalue or if it came from a temporary) and to forward this on to the wrapped function. Since temporary can be passed through without copying, this can prevent certain copies which isn't possible with C++2003. A full discussion is a rather lengthy article, though. – Dietmar Kühl Feb 19 '12 at 22:46
  • @user1186270: Dietmar is right. It does indeed make for a rather lengthy article. Fortunately someone has already written it here: http://blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx – ForeverLearning Feb 23 '12 at 22:10
1

I'm not sure you would need variadics to pull this off. Consider the following interface...

template < typename RETURN_TYPE >
class iFunctor abstract {

    public:

        virtual RETURN_TYPE operator () ( void ) = 0;

};

Abstract interfaces are not full-blown classes, they can contain a partial implementation such as function signatures and some data members. With the template, you can generalized a return type. But what about the argument list you say?

Note how there is no constructor in the interface. In your concrete class (or derived classes) you can pass the burden of variable argument lists to the constructor, like so...

template < typename TYPE >
class ConcreteFunctor_add : public iFunctor < TYPE > {

    private:

       int A;
       int B;

    public:

       explicit ConcreteFunctor_add ( const int &a, const int &b ) : A(a), B(b) {};

       TYPE operator () ( void ) { return ( A + B ); };

};

You deal with the argument list on a case by case basis through the constructor.

The explicit constructor requires an argument list upon declaration, so you'll get your variable list here. So in practice...

ConcreteFunctor_add < int > addInteger ( 10, 10 );
addInteger();

...and you'd be cool.