I want to write an adapter which can convert non static member functions to C-style function pointers. Here is what I got now(see the following code snippet), but the current solution is not general. I want to make int (T::*Func)(int)
accept variable arguments.
Also it's necessary to make CObjectT::f
and StoreVals::display
have the same signature.
The final goal is Interfacing C++ member functions with C libraries.
class StoreVals
{
int val;
public:
int display(int k) { cout << k << endl; return 0; }
};
template<class T, int (T::*Func)(int)>
class CObjectT
{
public:
/*
* The signagure of 'f(...)' should change by the argument of template.
* They must be the same, but i don't know how to achieve this goal.
*/
static int f(int i)
{
T obj;
return (obj.*Func)(i);
}
};
void main()
{
CObjectT<StoreVals, &StoreVals::display>::f(7);
auto function_t = &CObjectT<StoreVals, &StoreVals::display>::f;
// Now it's a C-style function pointer
cout << typeid(function_t).name() << endl;
}
I don't think it is possible to dynamically change the name of a function based on a template parameter, but you can change the arguments/return type based on the template parameter. It does require some extra type information in the template declaration but it does allow what you want.
#include <iostream>
#include <utility>
template< typename T, T t >
class Delegate;
template< typename R, typename C, typename... Args, R ( C::*F ) ( Args... )>
class Delegate< R ( C::*)( Args... ), F > {
public:
template< typename... Ts >
static R invoke( Ts&&... args ) {
C t;
return ( t.*F )( std::forward< Ts >( args )... );
}
};
template< typename R, typename... Args, R ( *F ) ( Args... ) >
class Delegate< R ( * ) ( Args... ), F > {
public:
template< typename... Ts >
static R invoke( Ts&&... args ) {
return F( std::forward< Ts >( args )... );
}
};
void print( int v ) {
std::cout << "Static: " << v << std::endl;
}
class Class {
void print( int v ) {
std::cout << "Class: " << v << std::endl;
}
};
int main( int argc, char** argv ) {
Delegate< void ( * )( int ), &print >::invoke( 1 );
Delegate< void ( Class::* ) ( int ), &Class::print >::invoke( 1 );
return 0;
}
Output:
Static: 1
Class: 1
This does use C++11's variadic templates and Rvalue References for perfect forwarding. That is why you see the weird std::forward< Args >( args )...
in the function calls.
This doesn't work with variadic parameter functions such as printf and the like. It might be possible but would require a lot more template black magic that I don't have time to write and test.
If you want to interface with C, you cannot use templates and you cannot use member functions (even static). The only way is a honest hand-written extern "C"
function. Templates or member functions cannot have C linkage.
If you want to sacrifice portability for convenience you can do something like this:
#define TYPE_AND_VALUE(x) decltype(x),x
#define MemFuncTypeAdapter(x) MemFuncTypeAdapterStruct<TYPE_AND_VALUE(x)>
extern "C"
{
int (*cfunc)(struct A*, int);
}
template <typename MemFuncType> struct MemFuncTypes;
template <typename Class, typename Ret, typename... Args>
struct MemFuncTypes<Ret (Class::*)(Args...)>
{
using RetType = Ret;
using ClassType = Class;
};
template <typename MemFuncType, MemFuncType memFunc>
struct MemFuncTypeAdapterStruct
{
using RetType = typename MemFuncTypes<MemFuncType>::RetType;
using ClassType = typename MemFuncTypes<MemFuncType>::ClassType;
template <typename... Args>
static RetType func (ClassType* c, Args... args)
{
return (c->*memFunc)(args...);
}
};
struct A
{
A() : a(33) {};
int a;
int plus (int b) { return a + b; }
};
int main ()
{
MemFuncTypeAdapter(&A::plus) aplus;
A a;
aplus.func(&a, 22);
cfunc = &MemFuncTypeAdapter(&A::plus)::func; //<- C interface here
}
Notes.
Class*
argument. See comments for an explanation of this. typename(x), x
every time. Hopefully a future C++ standard will deal with it.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.