Many objects in the Lely libraries allow the user to register callback functions. The function signature and registration method all follow the pattern shown here.
Example of a public C header (lely/lib/obj.h):
#ifdef __cplusplus
extern "C" {
#endif
typedef void callback_func_t(Args... args, void *data);
void obj_get_func(const obj_t *obj, callback_func_t **pfunc, void **pdata);
void obj_set_func(obj_t *obj, callback_func_t *func, void *data);
#ifdef __cplusplus
}
#endif
Example of the C implementation (obj.c):
struct obj {
...
callback_func_t *callback_func;
void *callback_data;
...
};
static void obj_callback(obj_t *obj, Args... args);
struct __obj *
__obj_init(struct __obj *obj, Args... args)
{
...
obj->callback_func = NULL;
obj->callback_data = NULL;
...
}
void
obj_get_func(const obj_t *obj, callback_func_t **pfunc, void **pdata)
{
assert(obj);
if (pfunc)
*pfunc = obj->callback_func;
if (pdata)
*pdata = obj->callback_data;
}
void
obj_set_func(obj_t *obj, callback_func_t *func, void *data)
{
assert(obj);
obj->callback_func = func;
obj->callback_data = data;
}
static void
obj_callback(obj_t *obj, Args... args)
{
assert(obj);
if (obj->callback_func)
obj->callback_func(args..., obj->callback_data);
}
The user-specified data pointer can be used to allow the registration of C++ function objects or member functions. lely/util/c_call.hpp provides several templates to automatically generate the required wrapper functions.
Example of a public C++ header (lely/lib/obj.hpp):
class Obj {
public:
...
void
getFunc(callback_func_t** pfunc, void** pdata) const noexcept
{
obj_get_func(this, pfunc, pdata);
}
void
setFunc(callback_func_t* func, void* data) noexcept
{
obj_set_func(this, func, data);
}
template <class F>
void
setFunc(F* f) noexcept
{
setFunc(&c_obj_call<callback_func_t*, F>::function,
static_cast<void*>(f));
}
template <class C, typename c_mem_fn<callback_func_t*, C>::type M>
void
setFunc(C* obj) noexcept
{
setFunc(&c_mem_call<callback_func_t*, C, M>::function,
static_cast<void*>(obj));
}
...
};
}
Registering a C-style global (or static) function in C++ is similar to C:
void myFunc(Args... args, void* data) noexcept;
void
setFunc(Obj* obj, void* data)
{
obj->setFunc(&myFunc, data);
}
The second form of setFunc() allows registering a function object. The user is responsible for lifetime of the object.
struct MyFuncObj {
void operator()(Args... args) noexcept;
};
void
setFunc(Obj* obj, MyFuncObj* f)
{
obj->setFunc(f);
}
The third form of setFunc() allows member functions to be registered as callbacks. Again, the user is responsible for the lifetime of the instance of the class containing the member function.
class MyClass {
public:
void myFunc(Args... args) noexcept;
};
void
setFunc(Obj* obj, MyClass* cls)
{
obj->setFunc<MyClass, &MyClass::myFunc>(cls);
}
It is also possible to register private methods as callbacks, but only from a function which has private access (i.e., a friend or another method).