Some C API interfaces use callbacks. Often these callbacks have a user-defined parameter of type void * which can be used as a class instance pointer in C++. Usually in order to call the member function you need to write a static class function which would cast the void * parameter to an instance pointer and call the member function. I wanted to write a wrapper that would instantiate a static function which takes void * as a parameter and the rest of arguments is exactly like in the member function passed to wrapper.
template<
typename Result,
typename Function,
auto Func
>
struct instance_call_helper;
template<
typename Result,
typename Base,
typename... Args,
auto Func
>
struct instance_call_helper<
Result,
Result(Base::*)(Args...),
Func
> {
static Result call(void * ref, Args ... args) {
Base * instance = reinterpret_cast<Base *>(ref);
return (instance->*Func)(std::forward<Args>(args)...);
}
};
template <
typename Base
> struct instance_type_helper;
template <
typename Result, typename Base, typename... Args
>
struct instance_type_helper<Result(Base::*)(Args...)> {
using result_t = Result;
};
template<auto Func>
#if __cpp_concepts >= 201507
requires std::is_member_function_pointer<decltype(Func)>::value
#endif
class instance_call {
using functor_t = decltype(Func);
using type_helper = instance_type_helper<functor_t>;
public:
using helper = instance_call_helper<
typename type_helper::result_t,
functor_t,
Func
>;
};
Intended usage:
class Bar {
private:
int m_a1;
public:
Bar(int a1):
m_a1(a1) {}
int baz(int a2) {
return m_a1 + a2;
}
};
int foo() {
Bar b { 3 };
// Pointer we are going to pass somewhere as a callback
// f has signature int (*)(void*, int)
auto * f = &instance_call<&Bar::baz>::helper::call;
// Intended way of calling it — returns 5
return f(&b, 2);
}
instance_call<&Bar::baz>::helper::callis a member function, as such C does not know how to call it. Now on a lot of platforms the calling convention for static member functions is the same as the calling convention for C functions; but there is no guarantee of this in any standard and thus UB. \$\endgroup\$extern "C" <type> func(void*)\$\endgroup\$void *for additional user data becausestd::functionandstd::bindcan't be used due to space constraints. \$\endgroup\$noexceptor convert exceptions into form suitable for particular C library. \$\endgroup\$