I l@ve RuBoard Previous Section Next Section

5.3 C++ Callable Entities

To put together a generic implementation for forwarding commands, let's try to get Command-specific notions closer to terms familiar to C++ programming.

A forwarding command is a callback on steroids, a generalized callback. A callback is a pointer to a function that can be passed around and called at any time, as illustrated in the following example.



void Foo();


void Bar();





int main()


{


   // Define a pointer to a function that takes no


   // parameters and returns void.


   // Initialize that pointer with the address of Foo


   void (*pF)() = &Foo;


   Foo();                // Call Foo directly


   Bar();                // Call Bar directly


   (*pF)();              // Call Foo via pF


   void (*pF2)() = pF;   // Create a copy of pF


   pF = &Bar;            // Change pF to point to Bar


   (*pF)();              // Now call Bar via pF


   (*pF2)();             // Call Foo via pF2


}


There is an essential difference between calling Foo and calling (*pF).[1] The difference is that in the latter case you can copy and change pointers to functions. You can take a pointer to a function, store it somewhere, and call it when the right time comes—hence, the similarity with forwarding Commands, which are essentially a piece of work that is stored away from its actual executor and processed later.

[1] The compiler offers a syntactic shortcut: (*pF)() is equivalent to pF(). However, (*pF)() is more suggestive of what actually happens—pF is dereferenced, and the function-call operator () is applied to the dereferenced pointer.

In fact, callbacks are the C way of using the Command pattern in many windowing systems. X Windows, for instance, stores such a callback in each menu item and in each widget. The widget calls the callback when the user does something (like clicking on the widget). The widget does not know what the callback actually does.

In addition to simple callbacks, C++ defines many more entities that support the function-call operator. Let's enumerate all the things that support operator() in C++.

  • C-like functions

  • C-like pointers to functions

  • References to functions (which essentially act like const pointers to functions)

  • Functors, that is, objects that define an operator()

  • The result of applying operator.* or operator->* having a pointer to a member function in the right-hand side of the expression

You can add a pair of parentheses to the right of any of the enumerated items, put an appropriate list of arguments inside, and get some processing done. No other objects in C++ allow this except the ones just listed.

The objects that support operator() are known as callable entities. The goal of this chapter is to implement a set of forwarding commands that store and can forward a call to any callable entity.[2] The Functor class template will encapsulate the forwarding commands and provide a uniform interface.

[2] Note the avoidance of the notion of type here. We could have simply said, "Types that support operator() are callable entities." But, incredible as it seems, in C++ there are things to which you can apply operator() although they don't have a type, as you will soon see.

The implementation must handle the three essential cases: simple function calls, functor calls (including Functor calls; that is, you should be able to forward calls from one Functor to another), and member function calls. You might think to define an abstract base class and create a subclass for each case. All this sounds like straightforward C++. As you launch your favorite editor and start to type, however, an abundant crop of problems appears.

    I l@ve RuBoard Previous Section Next Section