Rogue Wave banner
Previous fileTop of DocumentContentsIndexNext file

8.1 Introducing the Functor Package

You can think of a functor as an object that contains a function and the information necessary to invoke that function. Functors are also called function objects, or sometimes method or command objects. A functor acts as a sort of adapter that lets a program launch a function from any source without knowing the specifics of the function's signature. Functors were always an important part of Threads.h++, but in this release they have been uncovered for you and extended with the addition of functor maps and functor lists. The Functor package provides a convenient, flexible, and powerful way to encapsulate function calls.1

A functor captures the information necessary to describe and make a call to a specific function, including the function itself and any argument values to pass to it. The functor's interface can be quite independent of the encapsulated function. Because the information for invoking the function 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 deal with 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:

8.1.1 Functor Subpackages

In addition to basic functors, the Functor package includes two subpackages:

The subpackage software resides in separate directories under each of the functor directories.

8.1.2 Interpackage Dependencies

The Functor package depends on the Thread-compatible Exception, Execution Tracing, Synchronization, and Smart Pointer packages of Threads.h++. You can build the two Functor subpackages, Functor Lists and Functor Maps, independently of each other, but they each require the basic Functor package.

8.1.3 Including the Functor Header Files

Because there are so many header files, the Functor package provides six umbrella header files in the rw\functor directory: functor0.h, functor1.h, functor2.h, functorR0.h, functorR1.h, and functorR2.h. Each of these files includes the header files for a subset of the functor classes. If you are using a single handle class, include the header file whose name matches the handle class. If you are using multiple handle classes, you can access the declarations for the entire Functor package by including the functor.h file.

Each subpackage has an umbrella file to that includes all its header files. For functor lists, it is rw\functor\list\list.h, and for functor maps, it is rw\functor\map\map.h. If you are using only a few of the classes, however, you can include each file individually to avoid pulling in unnecessary classes.

8.1.4 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.h++ 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 Section 3.4, "Using Threads."

Functors also provide a level of abstraction useful for functional programming and creating higher-order functions.

8.1.5 How Do They Work?

In Threads.h++, functors are implemented by a large number of template classes for global, static, and member function calls. A variety of functors accommodates different numbers of arguments and different ways of handling the return type. 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 that is 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 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 RWTFunctorR1<double,char>. This is all the caller has to do. Everything else is the callee's responsibility.

8.1.5.1 When the Function Signature Matches the Functor Invocation

The callee selects one of its functions, which it uses to build a functor of type RWTFunctorR1<double,char>. The simplest case is a function that matches the functor invocation, like this:

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 45.

Figure 45 -- Invoking a functor

The caller calls the functor with its char argument:

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 launches the function:

The function sends its return value back to the functor, which in turn passes it to the caller.

8.1.5.2 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:

Instead of requiring the value of the double to be specified at every invocation, the functor stores the value 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 functor for as long as it exists, are often referred to as callee or client data. In this case, the functor contains both a function pointer and callee data.

The caller knows nothing about the second argument. The caller invokes the functor with one argument:

but the functor adds the second argument when it launches the function:

Now the process looks something like Figure 46.

Figure 46 -- Invoking a functor containing callee data


NOTE: The arguments stored as callee data must be the last ones in the function's signature, because the functor adds its callee arguments to the end of the function call.

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.

8.1.5.3 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 RWTFunctor1<char>. The name ending in 1, instead of R1, means the functor does not return a value.


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 RWTFunctor1, can encapsulate a function with any return type. A functor that specifies a return value, like RWTFunctorR1, can encapsulate only functions with convertible return types.

Now suppose the callee wants to supply this function to the caller:

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 47.

Figure 47 -- Invoking a functor with no return value

Nothing different is required in the invocation. The caller calls the functor in the usual way:

and the functor calls the function:

8.1.5.4 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 RWTFunctorR1<double,char>. Suppose the callee needs to supply this function:

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.

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:

and the functor calls the function:

The process looks something like Figure 48.

Figure 48 -- Invoking a functor requiring type conversion


Previous fileTop of DocumentContentsIndexNext file

©Copyright 2000, Rogue Wave Software, Inc.
Contact Rogue Wave about documentation or support issues.