Threads Module User's Guide : PART III Foundation Packages : Chapter 8 The Functor Package
Chapter 8 The Functor Package
Introducing the Functor Package
A functor, also known as a function object, is an object type that can be called as if it were a function. A functor acts as an adapter, providing a common interface for invoking a callable object without regard to how that callable object is implemented. The Functor package provides a convenient, flexible, and powerful way to encapsulate callable types, including functions, lambda expressions, and other callable types.
Along with its support for functors, the Functor package also provides a powerful API to support parameter binding. Parameter binding allows for the creation of a binder object that encapsulates both a callable object as well as some or all of the parameters required to invoke that object.
The functor's interface can be independent of the encapsulated callable object. Because the information for invoking the callable type is encapsulated in an object, the invocation can be deferred or delegated. Software components can pass a functor as a parameter or return value, like any other object.
Functors make it straightforward to write a tool that can accept and carry out any arbitrary piece of work. Instead of trying to handle a variety of unknown function signatures, such a tool takes one type of functor whose arguments and return value fit the tool's own requirements. Each functor handles the details of adapting its encapsulated function to those requirements.
The Functor package provides the following features:
Functors are type flexible. Their parameters are templates, ensuring consistent interfaces and code generation by the compiler.
Functors are object-oriented and generic. You can use them in any situation, like any other object.
Functors implement a consistent interface for different callable types. The encapsulated callable object need not match the functor's signature exactly, as long as the types are convertible.
Functors are convenient. Once you abstract out the notion of a function to a functor object, you can reuse the functor in any number of situations.
Functors are an easy way to exchange information about functions, because you are dealing with an object, rather than a function pointer.
Functors are compile-time type safe. The compiler does the type conversions. If you try to make a functor out of a function that is not compatible with the functor type, you get a compiler error.
Interpackage Dependencies
The Functor package depends on the Thread-compatible Exception, Execution Tracing, Synchronization, and Smart Pointer packages of the Threads Module.
Including the Functor Header Files
The rw/functor directory contains header files for the RWTFunctor type, RWTFunctor.h, as well as for the utility function rwBind(), rwBind.h. You can also access the declarations for the entire package through functor.h, an umbrella file that includes the header files for all the classes.
What Are They For?
Functors are convenient for solving some common problems. The functor classes offer a consistent way to implement a callback interface. Suppose you have two separate software components, and component A calls component B. The functor encapsulates a function supplied by component B, and adapts that function to the signature expected by the calling function in component A. Functors solve the problem of calling unknown objects in C++. When you develop a component, all you need to know is what data it needs to send to the unknown object and what it needs to receive in return, and specify the appropriate functor type. In this way, functors can connect libraries from different sources.
Functors are required, in some cases, when you use other Threads Module packages. In multithreaded applications, for example, you can pass a functor to tell a new thread where to begin execution. You also use functors to create runnable classes whose instances can execute any function. See “Using Threads.”
Functors also provide a level of abstraction useful for functional programming and creating higher-order functions.
How Do They Work?
The functor classes overload operator() to supply a function call operator that invokes the encapsulated function. When the caller calls the functor, the syntax is identical to a function invocation.
What we have, then, is a caller, some software routine that invokes the functor, and a callee, the software that provides the function encapsulated by the functor. Any place you would want to pass a function pointer to the caller, you can pass a functor. If necessary, the functor can adapt the callee function to the signature that the caller expects.
The functor cannot make every function fit, but with the help of a binder object, it can deal with extra arguments, a return value when none is wanted, and a less than exact match in argument and return types.
For example, suppose the calling routine needs to call various functions with one argument value of type char and to receive a double in return. The routine can be written to call a functor type that takes one argument and returns a value, in this case RWTFunctor <double(char)>. This is all the caller has to do. Everything else is the callee's responsibility
When the Function Signature Matches the Functor Invocation
The callee selects one of its functions, which it uses to build a functor of type RWTFunctor<double(char)>. The simplest case is a function that matches the functor invocation, like this:
 
double function1 (char c);
In this case, the functor contains only a function pointer. The functor just passes the argument from the caller to the function and passes the return value from the function to the caller. The process looks something like Figure 46.
Figure 46 – Invoking a functor
The caller calls the functor with this char argument:
 
functor1('y');
When the caller passes argument values directly to the functor like this, the arguments are called caller data. The functor, in turn, passes the value on when it invokes the function:
 
function1('y');
The function sends its return value back to the functor, which in turn passes it to the caller.
When Function Arguments Won’t Change Across Invocations
Suppose that another function has an argument that will always take the same value when invoked by the functor. The function looks like this:
 
double function2 (char c, double f);
Instead of requiring the value of the double to be specified at every invocation, the functor stores the value via a binder object, and adds it to the end of the function call. When the functor object is constructed, for example, a value of 3.14 might be specified for the second argument. That value always stays the same for every function call. Arguments like this, which are stored within the binder object for as long as it exists, are often referred to as callee or client data. In this case, the functor contains a binder object, which in turn contains both a function pointer and callee data.
The caller knows nothing about the second argument. The caller invokes the functor with one argument:
 
functor2('y');
The functor in turn invokes the binder object, which adds the second argument when it launches the function:
 
function2('y',3.14);
Now the process looks something like Figure 47.
Figure 47 – Invoking a functor containing callee data
You can use callee data to handle a function that has more arguments than the functor invocation specifies, provided that the extra arguments do not have to change across calls.
When You’re Not Using a Return Value
The previous examples have used the function’s return value, but suppose we have a different caller that needs to call functions with one char argument, but does not use a return value. This routine is written to call a functor of type RWTFunctor <void(char)>.
NOTE >> A routine that is not going to use the return value should never specify a functor that has one, to avoid limiting the functions the routine can access.
A functor without a return value, like RWTFunctor <void(char)>, can encapsulate a function with any return type. A functor that specifies a return value, like RWTFunctor <double(char)>, can encapsulate only functions with convertible return types.
Now suppose the callee wants to supply this function to the caller:
 
double function3 (char c);
A functor without a return value automatically ignores the function’s return value. The caller never sees it. In this case, the process looks something like Figure 48.
Figure 48 – Invoking a functor with no return value
Nothing different is required in the invocation. The caller calls the functor in the usual way:
 
functor3('y');
and the functor calls the function:
 
function3('y');
When the Types Don’t Quite Match
Now let’s go back to the caller that needs a return value and takes a functor of type RWTFunctor<double(char)>. Suppose the callee needs to supply this function:
 
int function4 (int i);
The difference between the types of the functor invocation arguments and those of the function may look like a problem, but it really isn’t. Functors have some type flexibility, and the compiler does the conversions.
The types of the function’s arguments and return value must be compatible with the functor’s, but they do not have to match exactly. Compatibility requires the types to be convertible. Often the conversion is type promotion, which means the more specific type is promoted to a more general type as the value moves through the process. Keep in mind that argument values and return values are passed in opposite directions.
Arguments — The functor’s argument types must be convertible to the function’s argument types, because the functor passes its arguments to the function.
Return value — The function’s return type must be convertible to the functor’s return type, because the function passes its return value back to the functor.
At compile time, the functor’s char argument is promoted to the int that the function expects. The function’s int return value is promoted to the functor’s double. The caller calls the functor in the usual way:
 
functor4('y');
and the functor calls the function:
 
function4(int('y'));
The process looks something like Figure 49.
Figure 49 – Invoking a functor requiring type conversion