C ++ Member Function Wrapping in C Templates - Visual Studio 2013

I am creating an easy cross-platform plug-in module that uses C-interfaces between the application and the plug-in (as usual, and not always, written in C ++).

One of my problems helping C ++ application and plugin developers find a simple way to expose the functionality of C ++ objects through the C interface. My real solution seems simple and uses templates to "create" C-signature functions that wrap based on C ++ member functions based on this big question and the answer to the stack question

template <typename Tc, typename F, F> struct MemberFuncWrapper; template <typename Tc, // C interface structure tag typename T, // C++ class, derived from Tc typename R, // C++ member function return type typename ...Args, // C++ member function argument types R (T::*f)(Args...) const> // C++ member function struct MemberFuncWrapper<Tc, R (T::*)(Args...) const, f> { static R call(const Tc * tc, Args... args) { const T * t = static_cast<const T *>(tc); return ((*t).*f)(args...); } }; 

The activation of this template compiles and works well under Linux (gcc) and mac (clang), but compilation in Visual Studio 2013 fails:

 error C2440: 'specialization' : cannot convert from 'overloaded-function' to 'void (__cdecl Greeter::* )(void) const' error C2973: 'MemberFuncWrapper<Tc,R(__cdecl T::* )(Args...) const,f>' : invalid template argument 'overloaded-function' 

In a separate code example, the following line shows the failure of Visual Studio (in the Greeter class Greeter ). I hope someone can:

  • Suggest a workaround that Visual Studio will entice, or
  • Suggest an alternative strategy to achieve similar goals.

Here is a separate code demonstrating the template code used in the context of implementing the C interface using the C ++ class in a fairly detailed Hello world application:

 #include <iostream> #include <utility> // // C interface and function(s) typically defined elsewhere // #ifdef __cplusplus extern "C" { #endif // The C interface implemented by a 'greeter' struct greeter_c { void(*greet_cb)(const struct greeter_c * greeter, const char * recipient); }; // Some C function that makes use of a greeter void broadcast(const struct greeter_c * greeter) { greeter->greet_cb(greeter, "world"); } #ifdef __cplusplus } // extern "C" #endif // // Template magic that envelopes a C++ member // function call in a C-signature function // template <typename Tc, typename F, F> struct MemberFuncWrapper; template <typename Tc, // C interface structure tag typename T, // C++ class, derived from Tc typename R, // C++ member function return type typename ...Args, // C++ member function argument types R (T::*f)(Args...) const> // C++ member function struct MemberFuncWrapper<Tc, R (T::*)(Args...) const, f> { static R call(const Tc * tc, Args... args) { // Cast C structure to C++ object const T * t = static_cast<const T *>(tc); // Details such as catching/handling exceptions omitted. // Call C++ member function return ((*t).*f)(args...); } }; // Repeat of the above for non-const member functions omitted // // A C++ class that implements the C 'greeter' interface // class Greeter : public greeter_c { public: // Constructor Greeter(const char * greeting) : m_greeting(greeting) { // Set up C interface callback by wrapping member function // !! The following line causes the Visual Studio compilation error !! greet_cb = MemberFuncWrapper<greeter_c, void (Greeter::*)(const char *) const, &Greeter::greet>::call; } // C++ member function that 'does' the greeting void greet(const char * recipient) const { std::cout << m_greeting << " " << recipient << std::endl; } private: const char * m_greeting; }; // An application that greets using a Greeter C interface int main(int argc, char * argv[]) { // Create C++ object that implements C interface Greeter a("Hello"); // Greet using Greeter C interface broadcast(&a); return 0; } 

Technical details:

  • Linux development: Centos 7, g ++ (GCC) 4.8.3 20140911
  • Mac Development: Apple's LLVM Version 6.1.0 (clang-602.0.49)
  • Windows Development: Visual Studio Express 2013 Update 5 CTP Update
+6
source share
1 answer

Preface: std::forward is useless here, since Args... explicitly specified. In other words, the C interface instance is no longer a template. std::forward not used in code without templates. For this reason, std::forward not used in the following solutions.

Version 1:

 template <typename Base, typename Derived, typename R, typename... Args> struct c_interface_gen { template <R(Derived::*mem_fn)(Args...)> inline static R invoke(Base* pb, Args... args) { return (static_cast<Derived*>(pb)->*mem_fn)(args...); } template <R(Derived::*mem_fn)(Args...) const> inline static R invoke(const Base* pb, Args... args) { return (static_cast<const Derived*>(pb)->*mem_fn)(args...); } }; 

This version works. But this is by no means elegant. The main problem is the lengthy and non-intuitive syntax of using the tool.

Version 2:

 template <typename Sig> struct mem_fn_sig; template <typename R, typename D, typename... Args> struct mem_fn_sig<R(D::*)(Args...)> { template <R(D::*mem_fn)(Args...)> struct mem_fn_inst { template <typename Base> struct base { inline static R invoke(Base* pb, Args... args) { return (static_cast<D*>(pb)->*mem_fn)(args...); } }; }; }; template <typename R, typename D, typename... Args> struct mem_fn_sig<R(D::*)(Args...) const> { template <R(D::*mem_fn)(Args...) const> struct mem_fn_inst { template <typename Base> struct base { inline static R invoke(const Base* pb, Args... args) { return (static_cast<const D*>(pb)->*mem_fn)(args...); } }; }; }; template <typename Sig, Sig inst, typename Base> struct c_interface_gen: mem_fn_sig<Sig>:: template mem_fn_inst<inst>:: template base<Base> {}; 

Obviously, this version has more code than the previous one. But the good point is that the syntax for using an object is simple and intuitive. In fact, the syntax is similar to the syntax of your source object. I just added some code to facilitate the compilation process for MSVC.

Typically, you will use the tool as follows:

... = c_interface_gen<decltype(&Derived::f), &Derived::f, Base>::invoke;

If Derived::f overloaded, you will need to explicitly specify its type as follows:

... = c_interface_gen<void(Derived::*)() const, &Derived::f, Base>::invoke;

Note that there is no need to specify const Base for the const member function. You just specify the base type. Templates will automatically figure out whether to add a const modifier or not.

The following is an example of your code using this second version:

 #include <iostream> template <typename Sig> struct mem_fn_sig; template <typename R, typename D, typename... Args> struct mem_fn_sig<R(D::*)(Args...)> { template <R(D::*mem_fn)(Args...)> struct mem_fn_inst { template <typename Base> struct base { inline static R invoke(Base* pb, Args... args) { return (static_cast<D*>(pb)->*mem_fn)(args...); } }; }; }; template <typename R, typename D, typename... Args> struct mem_fn_sig<R(D::*)(Args...) const> { template <R(D::*mem_fn)(Args...) const> struct mem_fn_inst { template <typename Base> struct base { inline static R invoke(const Base* pb, Args... args) { return (static_cast<const D*>(pb)->*mem_fn)(args...); } }; }; }; template <typename Sig, Sig inst, typename Base> struct c_interface_gen: mem_fn_sig<Sig>:: template mem_fn_inst<inst>:: template base<Base> {}; // // C interface and function(s) typically defined elsewhere // #ifdef __cplusplus extern "C" { #endif // The C interface implemented by a 'greeter' struct greeter_c { void(*greet_cb)(const struct greeter_c * greeter, const char * recipient); }; // Some C function that makes use of a greeter void broadcast(const struct greeter_c * greeter) { greeter->greet_cb(greeter, "world"); } #ifdef __cplusplus } // extern "C" #endif // // A C++ class that implements the C 'greeter' interface // class Greeter : public greeter_c { public: // Constructor Greeter(const char * greeting) : m_greeting(greeting) { // Set up C interface callback by wrapping member function // !! The following line causes the Visual Studio compilation error !! greet_cb = c_interface_gen<decltype(&Greeter::greet), &Greeter::greet, greeter_c>::invoke; } // C++ member function that 'does' the greeting void greet(const char * recipient) const { std::cout << m_greeting << " " << recipient << std::endl; } private: const char * m_greeting; }; // An application that greets using a Greeter C interface int main(int argc, char * argv[]) { // Create C++ object that implements C interface Greeter a("Hello"); // Greet using Greeter C interface broadcast(static_cast<const greeter_c *>(&a)); return 0; } 
+2
source

Source: https://habr.com/ru/post/986141/


All Articles