C++: Use arbitrary member function or functor object as C style plain callback and function pointer
Though nowadays the C++ world is full of class and template libraries, the ancestral C style callback using function pointer is still widely used in,
- 1, Windows platform API. C callbacks are heavily used in Windows APIs. A window needs callback to handle message, a hook needs callback to handle events, etc.
- 2, C library. Such as qsort, bsearch, etc.
The new unit, gplaincallback.h, in CppCallback library, is provided to convert any GCallback object, or functor object, to C style callback.
With the power of GCallback, now any member function or functor object can be used as C callbacks.
typedef int Compare(const void *, const void *);
struct CompareObject {
int operator() (const void * a, const void * b) const {
return (*(int*)a - *(int*)b);
}
};
struct RevertObject {
int revertCompare(const void * a, const void * b) {
return (*(int*)b - *(int*)a);
}
};
GCallback<Compare> cb;
CompareObject co;
void testPlaincallback() {
int values[] = { 6, 2, 1, 3, 5, 4 };
const int count = sizeof(values) / sizeof(values[0]);
qsort(values, count, sizeof(values[0]), &GPlainFunction<Compare, CompareObject, co>::invoke);
cout << "Normal qsort: \t\t";
for(int i = 0; i < count; ++i) {
cout << values[i] << " ";
}
cout << endl;
RevertObject ro;
cb = GCallback<Compare>(&ro, &RevertObject::revertCompare);
qsort(values, count, sizeof(values[0]), &GPlainCallback<Compare, cb>::invoke);
cout << "Reverted qsort: \t";
for(int i = 0; i < count; ++i) {
cout << values[i] << " ";
}
cout << endl;
}
In the sample code, C++ functor object "CompareObject" and member function "RevertObject::revertCompare" are passed to C function qsort, through the plain callback converter.
How can the plain callback help you?
- Reuse current C++ code.
- Cast away the difference of calling convention. With the plain callback, you can pass a function with any calling convention (cdecl, etc) to a Windows API, which requires a stdcall calling convention.
- Pass a function with different parameters count or type as C callback. You can use any bind or retype functors to cast away the difference.
How can not the plain callback help you?
- It can't help much if you have write new callback handler for C functions. If you goanna write new code, why not to write it in C function directly?
- It can't be used in running time. See the Limitation.
Limitation:
The functor object or callback object passed to GPlainFunction or GPlainCallback must have external linkage. It's required by C++ standard and due to the fact that they must be able to be resolved at compile time.
This limitation means the callback object must be global object, without declared "static".
This limitation also implicits that you can't use the plain callback at runtime. If a window object is created and it needs a new wndproc, it must be done with low level machine code to generate the code on the fly. The plain callback can't help here.