=head1 Functors and Callbacks Ermyth provides powerful functor and callback facilities. If you come from C, you will know function pointers. Functors provide similar functionality, but it is easier to pass them around and store them in containers. =head2 Creating and using a functor First, we need a function of any kind, be it a free function, static, non-static, virtual, derived member function or any other strange combination. To keep it simple, we will use a free void function that takes one argument. static void some_function (int number) { printf ("Number is %d\n", number); } Then, we can define a functor that will hold a pointer to this function. There are two possible syntaxes. A portable and a preferred syntax. // Portable syntax (it is not necessary to provide the // second argument, as it is void by default) functor::functor1 myfun; // Preferred syntax functor::functor myfun; There are three ways to bind a function to a functor. // 1) Using the constructor functor::functor1 myfun (some_function); // 2) Using the bind method myfun.bind (some_function); // 3) Using the make_functor convenience function myfun = functor::make_functor (some_function); Now you can do whatever you want with it. // Execute it (functors provide operator () so they can // be called like functions myfun (); // Store it in a container vector > funvec; funvec.push_back (myfun); // Compare them if (myfun == otherfun) puts ("Yes, they are the same."); =head2 Callbacks In addition to simple functors, Ermyth provides a callback system, that allows you to bind more than one function to a single object. Boost calls these signals. =head3 Simple example We define some functions and to keep it simple at first, we use void functions that take no argument. static void func1 () { puts ("Inside func1"); } static void func2 () { puts ("Inside func1"); } static void func3 () { puts ("Inside func1"); } Then we create a callback that will hold the functions above. functor::callback0<> cb; // Attach the functions cb.attach (func1); cb.attach (func3); And do some things with it. // Invoke the callback. This will invoke all functions attached. cb (); // The result is: // Inside func1 // Inside func3 // Attach the second function as well cb.attach (func2); // Invoke the callback. The functions are called in the same // order in which they were attached. cb (); // Detaching functions is generally an expensive operation. // It requires linear time (O(n)), as the underlying list // structure is an array (or vector). cb.detach (func3); // Invoking it again will call func1 and then func2 cb (); =head3 More complex example The above is the most simple example one can think of. Now it is time for a more advanced example. The first three functions that are called allow[123] return true if the passed value is 3, the deny function returns true if the passed value is 2. static bool allow1 (int num) { return num == 3; } static bool allow2 (int num) { return num == 3; } static bool allow3 (int num) { return num == 3; } static bool deny (int num) { return num == 2; } int main () { // Create callback functor::callback1 cb; // Attach allowing functions cb.attach (allow1); cb.attach (allow2); cb.attach (allow3); // cb (3) returns true, result is "allowed" puts (cb (3) ? "allowed" : "denied"); // cb (3) returns false, result is "denied" puts (cb (4) ? "allowed" : "denied"); cb.attach (deny); // Three of the four functions say true for 3, one says // true for 2. This means that everything combined can // never be true. Result is always "denied". puts (cb (3) ? "allowed" : "denied"); } The above code demonstrates the use of non-void functions. Non-void functions B require a combiner that combines all return values into a single one. Currently, Ermyth provides a void combiner (which is trivial), two bool combiners and four int combiners. A list of combiners can be found further down. =head2 Writing your own combiner Writing a combiner is very simple. The basic structure is shown here in the example of the strongtrue bool combiner. class strongtrue { typedef std::vector return_vector; public: // Every combiner needs to provide this operator () bool &operator () (return_vector &retvec) { /** * static, because we can't return a reference to a * temporary variable. One implication of this is that * The result should be released as soon as possible * for example by copying it. * It also implies that this is not threadsafe at all... */ static bool retval = false; return_vector::iterator it = retvec.begin (); return_vector::iterator et = retvec.end (); while (it != et) { /** * strongtrue: true overrules all falses * if one function returned true, the combiner * returns true. */ if ((retval = *it) == true) return retval; ++it; } return retval; } }; =head2 Using a custom combiner To use a custom combiner, you need to pass it as template parameter. functor::callback0 cb; =head2 List of provided combiners Other combiners can be found in F. =over =item functor::combiner::boolean::strongfalse If one of the functions returns false, the combiner returns false. =item functor::combiner::boolean::strongtrue If one of the functions returns true, the combiner returns true. =item functor::combiner::integer::min The combiner returns the lowest value returned. =item functor::combiner::integer::max The combiner returns the highest value returned. =item functor::combiner::integer::avg The combiner returns the average of all values. =item functor::combiner::integer::sum The combiner returns the sum of all values. =back =head2 Methods All functors provide the following methods. In the following list, C means C< functor::functorNEvoidE > where N is 1 to unlimited. Unlimited actually means arbitary limit. The generator script generates an arbitrary number of functors. =over =item functor () =item functor (const functor &other) =item void operator = (const functor &rhs) =item bool operator == (const functor &rhs) const =item bool operator != (const functor &rhs) const =item bool operator < (const functor &rhs) const =item bool operator > (const functor &rhs) const =item template functor (Y *pthis, RetType (X::*funcptr) (arguments)) And its const overload. =item template void bind (Y *pthis, RetType (X::*funcptr) (arguments)) And its const overload. =item functor (RetType (*funcptr) (arguments)) =item void operator = (RetType (*funcptr) (arguments)) =item void bind (RetType (*funcptr) (arguments)) =item RetType operator () (arguments) const Invoke the functor. =item bool operator ! () const =item void clear () Clears the functor, making it empty. Invoking an empty functor has no effect. =back =head2 Performance There is little overhead in the functor system itself, but the pure fact, that it stores function pointers, makes it slow. Function pointers just are slow. The following is an example of calling a pure virtual function of a virtually derived class with a non-virtual base class through functor, function pointer and native call: 2.770366 seconds (functor) 2.141891 seconds (function pointer) 0.305556 seconds (native call) Another test was a free void function. 3.048686 seconds (functor) 1.813422 seconds (function pointer) 0.302582 seconds (native call) We can conclude, that if you need speed, you should stick to native function calls. In reality, though, you likely won't notice a difference. These tests were done with 300,000,000 iterations. If you want to call a function 300 million times, you will probably not want functors.