如何传递一个具有变量参数的成员函数作为模板参数?

时间:2014-08-24 15:03:17

标签: c++ templates c++11 member-function-pointers

我想编写一个可以将非静态成员函数转换为C风格函数指针的适配器。这是我现在得到的(参见下面的代码片段),但目前的解决方案并不常见。我想让int (T::*Func)(int)接受变量参数。

还有必要让CObjectT::fStoreVals::display具有相同的签名。

最终目标是将C ++成员函数与C库连接。

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;
}

2 个答案:

答案 0 :(得分:3)

我认为不可能根据模板参数动态更改函数名称,但您可以根据模板参数更改参数/返回类型。它确实需要模板声明中的一些额外的类型信息,但它确实允许你想要的。

#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;
}

输出:

Static: 1
Class: 1

这确实使用C ++ 11的可变参数模板和Rvalue引用来实现完美转发。这就是为什么你在函数调用中看到奇怪的std::forward< Args >( args )...

这不适用于variadic参数函数,例如printf等。它可能是可能的,但需要更多模板黑魔法,我没有时间编写和测试。

答案 1 :(得分:2)

如果要与C接口,则无法使用模板,也无法使用成员函数(甚至是静态)。唯一的方法是诚实的手写extern "C"功能。模板或成员函数不能具有C链接。

如果你想为了方便而牺牲便携性,可以这样做:

#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

}

注释

  1. 适配器有一个额外的Class*参数。请参阅评论以获得对此的解释。
  2. 不要打扰完美的转发。无论如何,你在那里传递C兼容类型。这仅指标量和指针。
  3. 丑陋的宏是必要的,以避免每次都重复typename(x), x。希望未来的C ++标准能够解决它。
  4. 可以在标准库中找到部分内容,欢迎使用指针。
  5. Demo