Planet
navi homeaboutscreenshotsdownloaddevelopmentforum

Functor

TracNav(TracNav/TOC_Development)?

Description

The Functor is a wrapper around a function-pointer. You can call the function by executing the Functor without taking care of the types of parameters and return value. This is achieved by using MultiType to pass arguments.

Functor works with (almost) every function. This is possible because of a number of derived templates, one for every possible combination:

  • Static, member or const member
  • With or without returnvalue
  • With or without parameters (up to five parameters are allowed)

As you see, this results in a total amount of 3*2*6 = 36 combinations, each of them implemented as a template to allow arbitrary types for classes, returnvalues and parameters.

Functor.h implements all those templates by using a macro to avoid annoying code repetition.

Limitation: Because Functor uses MultiType to pass the arguments, parameters and return values must be of a supported type.

Hint: Functor is used in Executor. Executor adds a lot of features and makes using function-pointers even more comfortable.

Usage

Creation

To simplify creation of a new Functor, the helper function createFunctor(function-pointer) was created. Without that function, you would have to pick the right template (out of 36 possibilities) and specify all template arguments - quite annoying. But with createFunctor it's really easy:

int someFunction(float param1, bool param2);

Functor* myFunctor = createFunctor(&someFunction);

In this case, &someFunction is the function-pointer of the static function "someFuncton". For memberfunctions, this looks a bit different:

class SomeClass
{
    float someFunction(const std::string& param);
};

Functor* myFunctor = createFunctor(&SomeClass::someFunction);

Remember: This is just basic function-pointer knowledge, nothing special here. Functor does what it has to do and createFunctor works as usual.

Important: createFunctor uses new to create the Functor and returns a pointer. If you are responsible for the pointer, you have to delete the Functor after usage.

Call

To call the function, just use operator() and pass the arguments as if you would call the function-pointer directly:

void someFunction(int value);
Functor* myFunctor = createFunctor(&someFunction);

(*myFunctor)(10); // equivalent to someFunction(10);

Returnvalue

If your function returns a value, use getReturnvalue():

int doubleValue(int value) { return value*2; }
Functor* myFunctor = createFunctor(&doubleValue);

(*myFunctor)(10); // equivalent to doubleValue(10);

int result = myFunctor->getReturnvalue(); // result = 20

Information

There are some functions returning some information about the function:

  • getParamCount(): Returns the amount of parameters the function takes
  • hasReturnvalue(): Returns true if the function returns a value
  • getType(): Returns the type of the function as an enum: FT_MEMBER, FT_CONSTMEMBER, FT_STATIC
  • getTypenameParam(param number (0-4)): Returns the typename of the given parameter as a string
  • getTypenameReturnvalue(): Returns the typename of the returnvalue as a string

Subtypes

There are two subtypes of Functor: FunctorStatic? for static functions and FunctorMember? for member functions. The following sections explain the difference.

FunctorStatic?

C++ knows two types of static functions:

Static functions in a class:

class SomeClass
{
    static void someFunction();
};

And C functions outside of a class:

void someOtherFunction();

Both types are covered by FunctorStatic?. FunctorStatic? is in fact just a Functor, but you can use it to force static functions:

class SomeClass
{
    static void staticFunction();
    void nonstaticFunction();
};

FunctorStatic* functor1 = createFunctor(&SomeClass::staticFunction);    // this works
FunctorStatic* functor2 = createFunctor(&SomeClass::nonstaticFunction); // this fails

However, this would work:

Functor* functor2 = createFunctor(&SomeClass::nonstaticFunction);

FunctorMember?

There are two types of member functions: constant and non-constant.

class SomeClass
{
    void constFunction() const;
    void nonconstFunction();
};

Both types are covered by FunctorMember?, but in some cases you have to be aware of the intern difference.

FunctorMember? is in fact just a Functor, but it needs an object to call the function. Just pass the object in front of the arguments when calling the function:

class SomeClass
{
    void someFunction(int value);
};

SomeClass* object = new SomeClass();
FunctorMember* functor = createFunctor(&SomeClass::someFunction);

(*functor)(object, 10); // this is equivalent to object->someFunction(10);

You can bind an object to a Functor by adding the object with setObject(object). If an object is bound to a Functor, you can call the functor without passing the object again:

class SomeClass
{
    void someFunction(int value);
};

SomeClass* object = new SomeClass();
FunctorMember* functor = createFunctor(&SomeClass::someFunction);

function->setObject(object); // binds object to the Functor

(*functor)(10); // this is equivalent to object->someFunction(10);

Note: If you add a constant object, you can only call the Functor if the assigned function-pointer leads to a constant function too.

Template

Sometimes you might have to create know the template arguments of a Functor. This may be the case if the name of a function is ambiguous and createFunctor can't decide which function to use.

This is how the template arguments are arranged:

template <classname, returntype, paramtype1, ..., paramtype5>

If a function has no returnvalue, the returntype argument is discarded:

template <classname, paramtype1, ..., paramtype5>

If a function is static, the classname argument is discarded:

template <returntype, paramtype1, ..., paramtype5>

If a function has less than 5 parameters, the surplus parameters are discarded:

example: 3 parameters:
template <paramtype1, paramtype2, paramtype3>

Examples:

int function(float p1, float p2, float p3);
=> <int, float, float, float>

void function(const std::string& p);
=> <const std::string&>

void Class::function(int p);
=> <Class, int>

bool Class::function(int p);
=> <Class, bool, int>

Ambiguity

Sometimes two functions have the same name but different parameters. This is called overloading. Usually the compiler knows what you want because your arguments define the function. But if you just pass the function-pointer to createFunctor, the compiler has no idea which function to chose.

Example:

class SomeClass
{
    void someFunction(const Vector3& position);
    void someFunction(float x, float y, float z);
};

Functor* functor = createFunctor(&SomeClass::someFunction); // error!

This problem can be solved by specifying the template arguments by yourself. Read the chapter above to learn about how to arrange the template arguments.

This is how it works:

class SomeClass
{
    void someFunction(const Vector3& position);
    void someFunction(float x, float y, float z);
};

Functor* functor1 = createFunctor<SomeClass, const Vector3&>(&SomeClass::someFunction);
Functor* functor2 = createFunctor<SomeClass, float, float, float>(&SomeClass::someFunction);

Illustration

Last modified 9 years ago Last modified on Oct 1, 2008, 2:09:10 AM

Attachments (1)

Download all attachments as: .zip