XORP is an asynchronous programming environment and as a result there are many places where callbacks are useful. Callbacks are typically invoked to signify the completion or advancement of an asynchronous operation.
XORP provides a generic and flexible callback interface that utilizes overloaded templatized functions for for generating callbacks in conjunction with many small templatized classes. Whilst this makes the syntax a little ugly, it provides a great deal of flexibility.
XorpCallbacks are callback objects are created by the callback() function which returns a reference pointer to a newly created callback object. The callback is invoked by calling dispatch(), eg.
#include "libxorp/xorp.h" #include "libxorp/callback.hh" static void hello_world() { cout << "Hello World" << endl; } int main() { // Typedef callback() return type for readability. SimpleCallback // declares a XorpCallback taking 0 arguments and of return type void. typedef XorpCallback0<void>::RefPtr SimpleCallback; // Create XorpCallback object using callback() SimpleCallback cb = callback(hello_world); // Invoke callback, results in call to hello_world. cb->dispatch(); return 0; }
The callback() method is overloaded and can also be used to create callbacks to member functions, eg.
#include "libxorp/xorp.h" #include "libxorp/callback.hh" class Foo { public: void hello_world() { cout << "Foo::Hello World" << endl; } }; int main() { typedef XorpCallback0<void>::RefPtr SimpleCallback; Foo f; // Create a callback to a member function SimpleCallback cb = callback(&f, &Foo::hello_world); // Invoke f.hello_world cb->dispatch(); return 0; }
In addition, to being able to invoke member functions, callbacks can also store arguments to functions. eg.
#include "libxorp/xorp.h" #include "libxorp/callback.hh" static int sum(int x, int y) { cout << "sum(x = " << x << ", y = " << y << ")" << endl; return x + y; } int main() { // Callback to function returning "int" typedef XorpCallback0<int>::RefPtr NoArgCallback; NoArgCallback cb1 = callback(sum, 1, 2); cout << "cb1->dispatch() returns " << cb1->dispatch() << endl; // "3" cout << endl; // Callback to function returning int and taking an integer argument typedef XorpCallback1<int,int>::RefPtr OneIntArgCallback; OneIntArgCallback cb2 = callback(sum, 5); cout << "cb2->dispatch(10) returns " << cb2->dispatch(10) << endl; // 15 cout << endl; cout << "cb2->dispatch(20) returns " << cb2->dispatch(20) << endl; // 25 cout << endl; // Callback to function returning int and taking 2 integer arguments typedef XorpCallback2<int,int,int>::RefPtr TwoIntArgCallback; TwoIntArgCallback cb3 = callback(sum); cout << "cb3->dispatch() returns " << cb3->dispatch(50, -50) << endl; // 0 return 0; }
Bound arguments, as with member functions, are implemented by the overloading of the callback() method. At dispatch time, the bound arguments are last arguments past to the wrappered function. If you compile and run the program you will see:
sum(x = 10, y = 5) cb2->dispatch(10) returns 15
and:
sum(x = 20, y = 5) cb2->dispatch(20) returns 25
for the one bound argument cases.
There are a host of XorpCallbackN types. The N denotes the number of arguments that will be passed at dispatch time by the callback invoker. The template parameters to XorpCallbackN types are the return value followed by the types of arguments that will be passed at dispatch time. Thus type:
XorpCallback1<double, int>::RefPtr
corresponds to callback object returning a double when invoked and requiring an integer argument to passed at dispatch time.
When arguments are bound to a callback they are not specified in the templatized argument list. So the above declaration is good for a function taking an integer argument followed by upto the maximum number of bound arguments.
Note: In this header file, support is provided for upto %d bound arguments and %d dispatch arguments.
Callback objects may be set to NULL, since they use reference pointers to store the objects. Callbacks may be unset using the ref_ptr::release() method:
cb.release();
and to tested using the ref_ptr::is_empty() method:
if (! cb.is_empty()) { cb->dispatch(); }
In many instances, the RefPtr associated with a callback on an object will be stored by the object itself. For instance, a class may own a timer object and the associated timer expiry callback which is a member function of the containing class. Because the containing class owns the callback object corresponding the timer callback, there is never an opportunity for the callback to be dispatched on a deleted object or with invalid data.