If you're going to try to define your own delegates to wrap C++ member functions, free functions etc., I suggest you take a look at "5 years later, is there something better than the “Fastest Possible C++ Delegates”?. The discussion is quite nice and the accepted answer proposes a neat trick to reduce all callbacks to a pair of void*
and some free function R(void*,...)
that invokes a member function or free function and whatnot.
I've used a similar technique to implement wrappers for Win32 API callbacks. Here's an example (take a look at the Timer::function<>
and Timer::method<>
inner types). Using this technique, you can expose a C interface like:
/* type of callback functions passed through the C interface.
Obviously, C++ templates won't be of any help here, you need
to typedef all the function signatures. */
typedef void(*Callback)(void*,int, int);
/* Example function that uses one of the callbacks. */
extern "C" void* createTestClass(Callback,void*);
// Enhanced interface for passing object methods bound to
// a specific instance instead of the basic C-style callback.
#ifdef __cplusplus
template<typename T, void(T::M)(int,int)>
inline
void * createTestClass2(T& object,method<T,M> method)
{
// '&object' converts to 'void*' and 'method' object
// converts to 'void(void*,int,int)' function pointer.
return createTestClass(&object, method);
}
#endif
The role of the proposed mechanism is just generation of the void(void*,int,int)
functions that wrap member function calls so that they have the right signature. Everything else is just plain C function pointers and can be freely passed to your C API while preserving the ABI.
Just be careful exceptions don't leak from your C++ callback functions. See "Passing C++ exceptions across a C API boundary" for a more detailed discussion of the topic.
Update (working example)
Here's a full working example (tested with Visual Studio 2008).
library.h (public interface for C or C++ clients with stable ABI)
#ifndef _library_h_
#define _library_h_
#ifdef __cplusplus
extern "C" {
#endif
typedef int(*Callback)(void*,int,int);
void* createTestClass(Callback,void*);
#ifdef __cplusplus
}
#endif
#endif /* _library_h_ */
library.c (library implementation)
This demonstrates a C implementation, but you could have a C++ file instead and use std::function<>
, boost::function<>
or other to use the callback.
#include "library.h"
void* createTestClass(Callback callback, void* context)
{
callback(context, 1, 2);
return 0;
}
library.hpp (enhanced interface for C++ clients, builds on public ABI)
This is the enhanced interface for C++ clients. It adds support for using member functions instead of plain int(void*,int,int)
free functions. If you only plan to support C++ clients, then this can be in the same file as your C-style interface.
#ifndef _library_hpp_
#define _library_hpp_
#include "library.h"
template<typename T, int(T::*M)(int,int)>
class MethodCallback
{
static int wrapper(void* context, int x, int y)
{
return (static_cast<T*>(context)->*M)(x, y);
}
public:
operator Callback () const
{
return &wrapper;
}
};
template<typename T, int(T::*M)(int,int)>
void* createTestClass(T& object, MethodCallback<T,M> method)
{
return createTestClass(static_cast<Callback>(method), static_cast<void*>(&object));
}
#endif // _library_hpp_
main.cpp (test program)
Here's a simple demo on using the interface:
#include "library.hpp"
#include <iostream>
namespace {
class Foo
{
public:
int bar(int x, int y)
{
std::cout
<< "Foo::bar(" << x << "," << y << ")"
<< std::endl;
return 1;
}
};
}
int main(int, char**)
{
Foo foo;
void* instance = ::createTestClass
(foo, MethodCallback<Foo, &Foo::bar>());
// ...
}
You should expect this program to output Foo::bar(1, 2)
since the registered callback function will invoke foo.bar(1,2)
.