A functor is an object used to represent a function invocation. Functors capture the information necessary to describe and make a call to a specific function, including a pointer to the function and the argument values to pass to it.
The key feature of functors is their ability to supply an interface for invocation that is independent of the actual function. This allows functors to be invoked by entities that have no knowledge of the encapsulated function or its arguments. OTL encapsulates a functor in the COtlFunctor class. Functors can be used by themselves, or in conjunction with COtlThreadFunction to encapsulate a function call as a thread procedure.
Constructing Functors
Functors are implemented using a handle-body architecture. The construction of a functor handle does not result in the construction of a complete functor object. To build a viable functor object we must first construct a functor implementation object and bind that object to one or more functor handles. Functors can be implemented on global functions and non-static class member functions.
A functor object is constructed using an otlMakeFunctor() global template function. This relies on the compiler to extract the signature of the function you specify, allowing it to select, construct, and initialize an appropriate functor implementation instance. This is illustrated in the following code fragment:
void func( int n )
COtlFunctor functor = otlMakeFunctor( func, 1000 );
Functors can also be created on non-static public class member functions, as shown below:
class CDog
void Bark( int nDecibels );
CDog dog;
int nDb = 120;
COtlFunctor BarkLoudly = otlMakeFunctor( dog,&CDog::Bark, nDb );
Invoking Functors
Invoking a functor is simple. Each functor class overloads operator() to provide a style of invocation identical to that of a function call:
functor( );
BarkLoudly( );
Interface Tokens and Functors
An interface token requires no special handling to be used as a parameter in a functor. With that in mind, we can create a functor that accepts tokenized interface pointers as arguments. Consider the global fworker() function from the interface token section:
void fworker( LPWSTR strTitle, COtlInterfaceToken<IMarshalThis> pimt)
// use the token just like an interface pointer
short s;
HRESULT hr = pimt->Method1(34, &s);
This function takes two parameters: a string and an interface token. We can construct a functor for this in the following manner:
CComPtr<IMarshalThis> spimt;
COtlFunctor functok = otlMakeFunctor(fworker,
L"token marshaling",
Threads and Functors
Once you construct a functor, you can use the functor to represent a thread procedure when combined with the COtlThreadFunction class. The result is an encapsulated thread procedure that can accept typed parameters and interface pointers as tokens. COtlThreadFunction takes a functor that you want to be the thread procedure as a constructor argument. To make the functor described above into a worker thread procedure, you would use the following code:
COtlThreadFunction sta(functok);
Starting the thread is easy, as the following code demonstrates:
When Start() executes, the thread is resumed and the global fworker() function is called with an interface pointer token that is ready to use. No unmarshaling is required. Call interface methods the same way you would call a normal interface pointer: with the -> operator provided by the interface token. You do not need to call the COM CoInitialize or CoUninitialize APIs in your thread function. COtlThreadFunction handles that for you.