Functors are implemented with a handle-body architecture. Constructing a functor handle does not result in the construction of a complete functor object. You must first construct a functor body object and then bind that object to one or more functor handles.
As is the case with many other Threads.h++ classes, there are two commonly-used ways to construct a functor object:
You can choose a global template function that relies on the compiler to extract the signature of a function you specify, allowing it to select, construct, and initialize an appropriate functor body instance. This style is illustrated in the following code segment:
void function(int); RWTFunctor1<int> functor = rwtMakeFunctor1((void(*)(int))0,function) ;
See Section 8.4.2 for more information on using global template functions.
Alternatively, you can use a macro that takes an explicit definition of a function signature, and uses it to select, construct, and initialize an appropriate functor body instance:
int function(float); RWTFunctorR1<int,float> functor = rwtMakeFunctorR1G (int,float,int,function,float);
See Section 8.4.3 for more information on using macros.
You can also construct your own functor instance using a static make() function. See Section 8.4.3.3 for an example.
The type of functor you build depends on the specific function you encapsulate and the way you plan to invoke it. Although the functor is built from a particular function, remember that the functor's arguments and return value are not necessarily the same as the function's; the functor must match the way the caller will invoke it. Each functor requires a handle object bound to a matching body. If you construct your functor with a global template function, the compiler chooses the body class for you, but you always have to specify the handle class.
To choose a functor handle class, answer these questions:
Which function will the functor encapsulate?
How many function arguments could change across functor calls? These function arguments become the functor's caller arguments, whose values are specified when the functor is invoked. If any function arguments will always have the same value, disregard them for now.
Do you want to use the function's return value? Functors can return a function's return value or ignore it.
Once you know the answers to the questions, you are ready to choose a functor handle class. Functor handles are classified by whether they take 0, 1 or 2 caller arguments and whether they pass back the return value. If the function has a return value, and you need to use it, choose a functor handle with the R. Choose a handle class that ends in 0, 1, 2, R0, R1, or R2 to match the arguments and return value to be used when you invoke the functor.
For example, suppose your function looks like this:
int foo(long l, char* c);
and the char* argument will always be "calling foo". Then the char* value can be stored in the functor as a callee argument. The value of the long argument needs to be passed when the function is invoked, so you have one caller argument. Also, suppose you want to use the return value from foo(). Then you need the RWTFunctorR1 handle class.
A global template function relies on the compiler to extract the signature of a specified function and then select, construct, and initialize an appropriate functor body instance. To build a functor this way, you create a functor handle and call the global template function, specifying the information it needs to build and initialize the functor body. The compiler chooses the appropriate body class and builds the functor.
Global template functions are provided to simplify your development, but a word of caution; these are convenience functions that may not be 100% portable. They require your compiler to be able to extract template argument types from a function signature, and there are still a few, older compilers that are unable to do this. Check the Threads.h++ Build Guide for the latest information on which compilers suffer from this restriction. If you find yourself using one of these compilers, there's still hope. The templatized convenience functions can be replaced with macro invocations that, while not quite as simple to use, are 100% portable. See Section 8.4.3, "Using Macros."
You may also experience difficulties in compiling these functions when using a C++ reference data type. C++ compilers are notorious for having difficulties using reference types with templates. See Section 8.4.5, "Using Reference Types," for alternatives.
The names of the global template functions match the name of the functor handle type they return. Based on the analysis of functor requirements in Section 8.4.1, choose the functor handle whose name reflects the number of caller arguments (0, 1, or 2) and return value (R or not) that the functor needs. Use the matching global template function to construct the functor.
Functor Handle Class | Global Template Function |
RWFunctor0* |
rwtMakeFunctor0() |
RWTFunctor1 |
rwtMakeFunctor1() |
RWTFunctor2 |
rwtMakeFunctor2() |
RWTFunctorR0 |
rwtMakeFunctorR0() |
RWTFunctorR1 |
rwtMakeFunctorR1() |
RWTFunctorR2 |
rwtMakeFunctorR2() |
Example 70 constructs a functor to encapsulate a member function with two arguments and a return value. The functor will take only one caller argument. The encapsulated function's second argument will come from the functor's stored callee data.
#include <rw/functor/functorR1.h> // 1 ... class D { ... double print(char c, double f); // 2 ... }; D d2(20); // 3 RWTFunctorR1<double,char> m2 // 4 = rwtMakeFunctorR1((double(*)(char))0, d2, &D::print, 3.14);
//1 | Include all the functor classes whose invocation method takes one argument and returns a value. |
//2 | The function to be encapsulated is declared here. |
//3 | Create an object to call the function on, required because this is a member function. |
//4 | Construct a functor with the following characteristics: |
RWTFunctorR1<double, char> m2 |
Construct a functor handle m2 of type RWTFunctorR1, to be invoked with one argument of type char and a return value of type double. |
rwtMakeFunctorR1 |
This global template function matches the type of functor handle to be constructed. Specifications for the functor body are passed as arguments to the rwtMakeFunctorR1 function call. |
The arguments to rwtMakeFunctorR1 specify the functor's arguments and return value and the function it encapsulates:
(double(*)(char))0 |
This is how the caller will invoke the functor. Because this object serves only to provide information to the compiler, but is not used any other way, it is made a null pointer (0). The pointer is cast to the function pointer ((double(*)(char)) with a return value of type double and one argument of type char. If you want any type conversions, you tell the compiler here. In this case, the types are the same as the return and first argument types for print() declared in line //2. You could use any compatible types, though, as long as the compiler knows how to do the conversion. (See Section 8.1.5.4.) |
d2 |
This is the callee object, on which the function will be called (required for member functions). |
&D::print |
This is the function to be encapsulated. The compiler extracts its signature and automatically templatizes on those values for you. Because it is a member function, it requires the &. (For global functions, the compiler can do an implicit conversion from function to function pointer.) |
3.14 |
The second argument to print(), a double, is given the permanent value of 3.14 and stored in the functor as callee data. Each time the functor is called, 3.14 will be passed as the second argument print(). Any arguments supplied from callee data must be the function's last arguments. |
If you can't depend on your compiler to extract the signature of the function to be encapsulated, you can construct your functors with macros, instead of the global template functions. You must provide the types of the function's arguments and return value for the compiler to templatize on. The macro uses that data to select, construct, and initialize the appropriate functor body instance. Macros are less flexible and require more effort than global template functions, but macros are portable to all compilers.
Based on the analysis of functor requirements in Section 8.4.1, choose the functor handle whose name reflects the number of caller arguments (0, 1, or 2) and return value (R or not) that the functor needs. Each functor handle class has a matching set of macros for building functors. To choose a macro, consider these questions:
Will the functor encapsulate a global function or a member function?
Will any of the function's arguments remain the same across all functor calls? Functors treat these unchanging values as callee arguments, which are stored in the functor itself and added automatically to each function invocation.
The names of the macros are similar to the global template functions, except that they have extra character codes like the functor body classes.
A single letter indicating the function type:
G - The function is a global or static member function.
M - The function is a non-static member function.
An optional two-letter code describing the number of callee arguments that the functor will store and automatically pass to the function (in addition to the caller arguments):
A1 - One callee argument.
A2 - Two callee arguments.
A3 - Three callee arguments.
You choose the macro that fits the function to be encapsulated.
Functor Handle Class | Macros |
RWFunctor0* |
rwtMakeFunctor0(G|M)[A1|A2|A3] |
RWTFunctor1 |
rwtMakeFunctor1(G|M)[A1|A2] |
RWTFunctor2 |
rwtMakeFunctor2(G|M)[A1|A2] |
RWTFunctorR0 |
rwtMakeFunctorR0(G|M)[A1|A2|A3] |
RWTFunctorR1 |
rwtMakeFunctorR1(G|M)[A1|A2] |
RWTFunctorR2 |
rwtMakeFunctorR2(G|M)[A1|A2] |
* RWFunctor0 does not have a T in its name, because it has no arguments or return value to templatize on. The matching macros do have a t in their names, to match the corresponding global template function.
8.4.3.2 Constructing a Functor with a Macro
Example 71 constructs a functor for a member function that takes a char and a double as arguments and returns an int. Although the encapsulated function has two arguments, the functor will take only one caller argument. The encapsulated function's second argument will come from the functor's stored callee data.
#include <rw/functor/functorR1.h> // 1 ... class D { ... int print(char c, double f); // 2 ... }; D d2; RWTFunctorR1<int,char> m2 // 3 = rwtMakeFunctorR1MA1( int, char, D, d2, int, &D::print, char, double, 3.14);
//1 | Include all the functor classes whose invocation method takes one argument and returns a value. |
//2 | The function to be encapsulated is declared here. |
//3 | Construct a functor with the following characteristics: |
RWTFunctorR1<int, char> m2 |
Construct a functor m2 with handle type RWTFunctorR1, which has one argument of type char and a return value of type int. |
rwtMakeFunctorR1MA1 |
Use the macro rwtMakeFunctorR1MA1, which indicates: R You'll use the print() return value. 1 The functor will take one caller argument. M The functor encapsulates a member function. A1 One of the function's arguments will be supplied from stored callee data. |
The arguments to rwtMakeFunctorR1MA1 specify the functor's arguments and return value and the function's declared signature:
int, char |
This is how the caller will invoke the functor, with a return value of type int and one argument of type char. If you want any type conversions, you tell the compiler here. In this case, the types are the same as the return and first argument types for print() declared in //2. You could use any compatible types, though, as long as the compiler knows how to do the conversion. (See Section 8.1.5.4.) |
D, d2 |
This is the type and name of the callee object, on which the function will be called (required for a member function). |
int, &D::print, char, double |
This is the function to be encapsulated. Because it is a member function, it requires the &. (For global functions, the compiler can do an implicit conversion from function to function pointer.) |
3.14 |
The second argument to print(), a double, is given the permanent value of 3.14 and stored in the functor as callee data. Each time the functor is called, 3.14 will be passed as the second argument to print(). Any arguments supplied from callee data must be the function's last arguments. |
The macros resolve to code that constructs a functor using the static make() function provided by the body class. For example, the macro in Example 71 produces this code:
#include <rw/functor/functorR1.h> ... class D { ... int print(char c, double f); // 2 ... }; D d2; RWTFunctorR1<int,char> m2 = RWTFunctorR1MA1Imp<int,char,D,int,char,double>:: make(d2,&D::print,3.14);
In the debugger and in compiler and linker errors, you see references to code similar to this.
Because functors use the handle-body idiom, copying functors follows the general rules discussed in Section 7.3.1.4, "Handle-body Mechanics." When a handle class instance is copy-constructed from another handle class instance, the new handle is bound to the same body instance, if any, pointed-to by the other handle.
Similarly, assigning one handle to another causes the left-hand instance to detach from its current representation, if any, and then binds it to the same body instance, if any, pointed-to by the right-hand instance.
C++ compilers have difficulties using reference types with templates. If you are passing callee data as references, you must construct your functors directly with macros or the body class make() functions. In the following code, for example, the functor is created with a macro, because the action() function's counter argument is an int&.
RWFunctor0 pushButton_functor= rwtMakeFunctor0GA3 (RWBoolean, action, RWCString, message, int&, counter, int, 0);
Reference arguments do not work with the global template functions, because the function parameter and the data value are represented by different template arguments, and you can't cast the value to be a reference for the purposes of instantiation.
©Copyright 2000, Rogue Wave Software, Inc.
Contact Rogue Wave about documentation or support issues.