I am trying to implement a pointer to member function wrapper where the return type and parameter types are known but the class type of the member function is not known. This is for a C++ project targeting resource constrained (No heap allocation/c++ standard library) microprocessor.
The following code seems to work (you can run it HERE) and it fulfills our general requirements, but it uses reinterpret_cast hackery.
//can't use any c++ std libs or heap allocation
#include <cstdio>
template<typename Ret,typename... Args>
struct member_cb
{
template<typename T>
void set_cb(T *obj_,Ret (T::*func)(Args...))
{
obj = reinterpret_cast<member_cb*>(obj_);
cb = reinterpret_cast<Ret (member_cb::*)(Args...)>(func);
}
Ret operator()(Args... num)
{
return (obj->*cb)(num...);
}
member_cb* obj;
Ret (member_cb::*cb)(Args...);
};
struct Bar{
void someFunc(int n2){
printf("someFunc called with parameter: %d\n",n2);
}
};
int main()
{
//class type of Bar is not known at wrapper creation
member_cb<void,int> foo;
Bar bar;
foo.set_cb(&bar, &Bar::someFunc);
foo(42);
}
I want to do this without the reinterpret_cast. I believe there is a solution using a lambda that is created by the set_cb() function something like this:
template<typename Ret,typename... Args>
struct member_cb
{
template<typename T>
void set_cb(T *obj_,Ret (T::*func)(Args...))
{
cb_func = [=](Args...num)
{
(obj_->*func)(num...);
};
}
Ret operator()(Args... args)
{
return cb_func(args...);
}
Ret (*cb_func)(Args...);
}
The above code will not compile (you can try it HERE). Problem is that the regular function pointers like cb_func can only point to lambdas that have no captures, and the lambda has to capture the obj_ and the func parameters. Any suggestions on making this work would be appreciated.
A simple, compiler-independent solution:
template <typename Signature>
struct member_cb;
template <typename Ret, typename... Args>
struct member_cb<Ret(Args...)>
{
template <typename T, Ret (T::*func)(Args...)>
static Ret wrapper(void *object, Args&&... args) {
T *o = reinterpret_cast<T *>(object);
return (o->*func)(std::forward<Args>(args)...);
}
template <typename T, Ret (T::*func)(Args...)>
void set_cb(T *obj_)
{
obj = obj_;
cb_func = wrapper<T, func>;
}
// since C++17:
template <auto func, typename T>
void set_cb(T *obj_)
{
obj = obj_;
cb_func = wrapper<T, func>;
}
Ret operator() (Args&& ...args)
{
return cb_func(obj, std::forward<Args>(args)...);
}
void *obj;
Ret (*cb_func)(void *, Args&&...);
};
...
member_cb<void(int)> foo;
foo.set_cb<Bar, &Bar::someFunc>(&bar);
foo.set_cb<&Bar::someFunc>(&bar); // since C++17
The key limitation is that someFunc
must be known at compile-time at the place set_cb
is called, so you can’t take arbitrary method pointer and convert it to a member_cb
. It is possible to change the object keeping the method pointer intact, though (but that’s not type-safe).
Another possible problem is that the compiler may need to emit a wrapper
instance for each and every method that is ever used as a callback.
The upside is that it should work on any C++11-compliant compiler (C++17 for nicer syntax), and member_cb
itself is small.