// C API void doWithCallback (void (*fn) (int, void*), void *userdata); // C++ code struct Foo { void doFoo (int param); }; int main () { Foo foo; doWithCallback (MAGIC (/* &Foo::doFoo */), &foo); }
MAGIC
you can use a free function, a static member function or even a lambda (2017 is the year outside, after all), but writing the appropriate construction each time for each function with your hands is somewhat lazy, and the preprocessor, as we all Of course, we know - moveton.this
, of course, which we have to pass). A typical frontal solution would then look something like this: doWithCallback ([] (void *udata) { return static_cast<Foo*> (udata)->doFoo (); }, &foo);
#include <iostream> void doWithCallback (void (*fn) (void*), void *userdata) { fn (userdata); } struct Foo { int m_i = 0; void doFoo () { std::cout << m_i << std::endl; } }; int main () { Foo foo { 42 }; doWithCallback ([] (void *udata) { return static_cast<Foo*> (udata)->doFoo (); }, &foo); }
template<typename Ptr> auto MakeWrapper (Ptr ptr) { return [ptr] (void *udata) { return (static_cast<Foo*> (udata)->*ptr) (); }; } int main () { Foo foo { 42 }; doWithCallback (MakeWrapper (&Foo::doFoo), &foo); }
prog.cc:36:5: error: no matching function for call to 'doWithCallback' doWithCallback (MakeWrapper (&Foo::doFoo), &foo); ^~~~~~~~~~~~~~ prog.cc:3:6: note: candidate function not viable: no known conversion from '(lambda at prog.cc:29:12)' to 'void (*)(void *)' for 1st argument void doWithCallback (void (*fn) (void*), void *userdata) ^
template<typename R, typename C, R (C::*Ptr) ()> auto MakeWrapper () { return [] (void *udata) { return (static_cast<C*> (udata)->*Ptr) (); }; } int main () { Foo foo { 42 }; doWithCallback (MakeWrapper<void, Foo, &Foo::doFoo> (), &foo); }
template<typename T> struct MakeWrapperHelper { template<typename R, typename C> static R DetectReturnImpl (R (C::*) ()); template<typename R, typename C> static C DetectClassImpl (R (C::*) ()); template<typename U> using DetectReturn = decltype (DetectReturnImpl (std::declval<U> ())); template<typename U> using DetectClass = decltype (DetectClassImpl (std::declval<U> ())); using R = DetectReturn<T>; using C = DetectClass<T>; template<R (C::*Ptr) ()> auto Make () { return [] (void *udata) { return (static_cast<C*> (udata)->*Ptr) (); }; } }; template<typename T> auto MakeWrapper (T) { return MakeWrapperHelper<T> {}; } int main () { Foo foo { 42 }; doWithCallback (MakeWrapper (&Foo::doFoo).Make<&Foo::doFoo> (), &foo); }
#include <iostream> #include <tuple> void doWithCallback (void (*fn) (void*), void *userdata) { fn (userdata); } struct Foo { int m_i = 0; void doFoo () { std::cout << m_i << std::endl; } }; template<typename T> struct MakeWrapperHelper { template<typename R, typename C> static R DetectReturnImpl (R (C::*) ()); template<typename R, typename C> static C DetectClassImpl (R (C::*) ()); template<typename U> using DetectReturn = decltype (DetectReturnImpl (std::declval<U> ())); template<typename U> using DetectClass = decltype (DetectClassImpl (std::declval<U> ())); using R = DetectReturn<T>; using C = DetectClass<T>; template<R (C::*Ptr) ()> auto Make () { return [] (void *udata) { return (static_cast<C*> (udata)->*Ptr) (); }; } }; template<typename T> auto MakeWrapper (T) { return MakeWrapperHelper<T> {}; } int main () { Foo foo { 42 }; doWithCallback (MakeWrapper (&Foo::doFoo).Make<&Foo::doFoo> (), &foo); }
template<typename R, typename C> C DetectClassImpl (R (C::*) ()); template<auto T> auto MakeWrapper () { using C = decltype (DetectClassImpl (T)); return [] (void *udata) { return (static_cast<C*> (udata)->*T) (); }; } int main () { Foo foo { 42 }; doWithCallback (MakeWrapper<&Foo::doFoo> (), &foo); }
DetectArgsImpl
, which returns a list of argument types, can be defined: template<typename R, typename C, typename... Args> std::tuple<Args...> DetectArgsImpl (R (C::*) (Args...));
template<auto, typename> struct MakeWrapperHelper; template<auto T, typename... Args> struct MakeWrapperHelper<T, std::tuple<Args...>> { auto operator() () { using C = decltype (DetectClassImpl (T)); return [] (Args... args, void *udata) { return (static_cast<C*> (udata)->*T) (args...); }; } }; template<auto T> auto MakeWrapper () { return MakeWrapperHelper<T, decltype (DetectArgsImpl (T))> {} (); }
#include <iostream> #include <tuple> void doWithCallback (void (*fn) (int, void*), void *userdata) { fn (7831505, userdata); } struct Foo { int m_i = 0; void doFoo (int val) { std::cout << m_i << " vs " << val << std::endl; } }; template<typename R, typename C, typename... Args> C DetectClassImpl (R (C::*) (Args...)); template<typename R, typename C, typename... Args> std::tuple<Args...> DetectArgsImpl (R (C::*) (Args...)); template<auto, typename> struct MakeWrapperHelper; template<auto T, typename... Args> struct MakeWrapperHelper<T, std::tuple<Args...>> { auto operator() () { using C = decltype (DetectClassImpl (T)); return [] (Args... args, void *udata) { return (static_cast<C*> (udata)->*T) (args...); }; } }; template<auto T> auto MakeWrapper () { return MakeWrapperHelper<T, decltype (DetectArgsImpl (T))> {} (); } int main () { Foo foo { 42 }; doWithCallback (MakeWrapper<&Foo::doFoo> (), &foo); }
Source: https://habr.com/ru/post/321934/
All Articles