[Xorp-hackers] Question about callback in XORP

Orion Hodson hodson@icir.org
Mon, 24 Nov 2003 13:06:34 -0800


/-- Liang Qin wrote:
| Thanks!
|
| Please look at the code copied from callback.hh. In this example
| callback( ) takes 1-3 arguments respectively. How can
| we know the function's parameter list?

It's a kind of magic, and whilst it's nice to understand the magic,
it's not crucial to using the callback code.  The keys to the magic
are function overloading, the use of templates, and in particular
function templates.  An explanation follows...

At a high level, the callbacks in Xorp work by creating reference
counted objects that typically hold a function pointer or an object
pointer with a member function pointer.  When dispatch() is invoked
the relevant function or member function is called.

There are many implementations of callback() in callback.hh and for
each callback() method there is a corresponding templatized
XorpCallback class.  callback() methods exist for each combination of
late-bound and run-time arguments for function calls and member
function calls.

When the compiler comes across an invocation of callback() in the
code, it finds the appropriate callback() method to use and since
callback() is templatized, the relevant type information is available
for constructing an appropriate XorpCallback object.

Here's a snippet for the two argument callback used in the example
cited:

/**
 * @short Base class for callbacks with 2 dispatch time args.
 *
 * A pointer of this type can point to any XorpCallback2 object.
 */
template<class R, class A1, class A2>
struct XorpCallback2 {
    typedef ref_ptr<XorpCallback2> RefPtr;

    XorpCallback2() {}
    virtual ~XorpCallback2() {}
    virtual R dispatch(A1, A2) = 0;
};

/**
 * @short Callback object for functions with 2 dispatch time
 * arguments and 0 bound (stored) arguments.
 */
template <class R, class A1, class A2>
struct XorpFunctionCallback2B0 : public XorpCallback2<R, A1, A2> {
    typedef R (*F)(A1, A2);
    XorpFunctionCallback2B0(F f)
	: XorpCallback2<R, A1, A2>(), _f(f)
    {}
    R dispatch(A1 a1, A2 a2) { return (*_f)(a1, a2); }
protected:
    F   _f;
};

/**
 * Factory function that creates a callback object targetted at a
 * function with 2 dispatch time arguments and 0 bound arguments.
 */
template <class R, class A1, class A2>
typename XorpCallback2<R, A1, A2>::RefPtr
callback(R (*f)(A1, A2)) {
    return XorpCallback2<R, A1, A2>::RefPtr(new XorpFunctionCallback2B0<R, A1, 
A2>(f));
}

Now, suppose as we have in the original snippet a method called sum
that looks like:

	int sum(int a, int b);

and a callback() call like:

    	TwoIntArgCallback cb3 = callback(sum);

Then the compiler searches through all the templatized callback()
methods and finds the one that takes a function pointer to a function
taking two arguments.  This is the one above, it instantiates the
method with "int" as the template argument type for R, A1, and A2.
When this method is invoked it creates a XorpCallback object for a
XorpFunctionCallback2B0<int, int, int> wrappered in a reference
pointer.

The reference pointer simplifies memory management, ie it's trivial to
assign callback objects without worrying about memory.  The ref
pointer type points to the XorpCallback2<int, int, int>.  This is the
base type for all callbacks returning an integer and taking 2 integer
arguments to the dispatch() method, ie for <int,int,int>:

XorpCallback2
  +- XorpFunctionCallback2B0	(2 arguments, 0 bound arguments)
  +- XorpFunctionCallback2B1	(2 arguments, 1 bound arguments)
     ...
  +- XorpFunctionCallback2B6	(2 arguments, 6 bound arguments)
  +- XorpMemberCallback2B0	(member function, 2 args, 0 bound)
  +- XorpMemberCallback2B1	(member function, 2 args, 1 bound)
     ...
  +- XorpMemberCallback2B6	(member function, 2 args, 6 bound)
  +- XorpConstMemberCallback2B0	(const member function, 2 args, 0 bound)
  +- XorpConstMemberCallback2B1	(cember function, 2 args, 1 bound)
     ...
  +- XorpConstMemberCallback2B6	(const member function, 2 args, 6 bound)

So a ref pointer object to the base is able to hold any of the above
types.  This makes assignment flexible, as you can assign any of the
above callbacks types to be held by the same reference pointer.

Hopefully, this is comprehensible (?).  It is a hard piece of code to
understand, but it's flexiblity is very useful.

- Orion