I attempted implementing a "thunk" class in C++ that allows obtaining a C function pointer to pass to C code that eventually calls a C++ member function. A specific requirement was that it works with C code that does not pass a void * context parameter (or similar) as is frequent in C code callbacks. Hence, a distinct global or static C glue function has to be created for each C<->C++ link. In my implementation this is realized through the template parameter N, which has to be distinct for each use of the thunk class and causes a different "trampoline" function to be created for each template concretization.
My question is: is there a way to implement this without the need for the template parameter N or a way for the compiler to automatically assign a new value N to each concretization based on some template "magic"?
#ifndef THUNK_H
#define THUNK_H
#include <map>
#include <stdexcept>
template <class Sig, class Obj, size_t N>
class Thunk;
class ThunkBase
{
public:
static void clear()
{
for (auto it : mThunkStorage) {
delete it.second;
}
mThunkStorage.clear();
}
virtual ~ThunkBase() {}
protected:
static std::map<void *, ThunkBase *> mThunkStorage;
};
template <class R, class... Args, class Obj, size_t N>
class Thunk<R(*)(Args...), Obj, N> : public ThunkBase
{
public:
Thunk(Obj &obj, R(Obj::*method)(Args...)) : mObj(obj), mMethod(method)
{
std::pair<decltype(mThunkStorage)::iterator, bool> resPair = mThunkStorage.emplace(
(void *)(&Thunk<R(*)(Args...), Obj, N>::trampoline),
this);
if (!resPair.second) {
throw std::runtime_error("failed to insert thunk object");
}
}
R(*entry())(Args...)
{
return &Thunk<R(*)(Args...), Obj, N>::trampoline;
}
private:
Obj &mObj;
R(Obj::*mMethod)(Args...);
static R trampoline(Args... args)
{
auto selfFuncPtr = &Thunk<R(*)(Args...), Obj, N>::trampoline;
auto it = mThunkStorage.find((void *)selfFuncPtr);
if (it == mThunkStorage.end()) {
throw std::runtime_error("could not find thunk object");
}
ThunkBase *base = it->second;
Thunk<R(*)(Args...), Obj, N> *self = static_cast<Thunk<R(*)(Args...), Obj, N> *>(base);
Obj &obj = self->mObj;
R(Obj::*method)(Args...) = self->mMethod;
return (obj.*method)(args...);
}
};
#endif
std::map<void *, ThunkBase *> ThunkBase::mThunkStorage;
// -> usage would be like, for example
struct MyClass
{
void myFunc(int);
};
MyClass myObj;
Thunk<void(*)(int), MyClass, 0> myThunk(myObj, &MyClass::myFunc);
void (*myFunc)(int) = myThunk.entry();
Aucun commentaire:
Enregistrer un commentaire