使用C调用约定调用函数模板特化

时间:2011-04-08 01:10:14

标签: c++ templates

我有以下模板

template<typename T> void f(T t) { }

我想将其特定专业化的地址传递给C函数

g(&f<int>);

但是因为我想要可移植,我希望调用约定“f”匹配C的那个。所以我试验了语言链接如何影响调用约定并找到

  • 函数类型的语言链接会影响调用约定
  • 函数名称的语言链接会影响错位

C ++规范的语言链接部分说

  

在链接规范中,指定的语言链接适用于所有函数声明符的函数类型,具有外部链接的函数名称,以及在链接规范中声明的具有外部链接的变量名称。

因此,为防止模板在目标文件中区分不同专业所需的修改,我按如下方式进行了修改

extern "C" {
    /* function name will not be affected */
    template<typename T> static void f(T t) { }
}

但它给了我一个编译器错误,说模板不能有C语言链接,我认为这意味着它抱怨函数模板的函数类型。事实上,我发现了规范

  

模板,模板显式特化(14.7.3)和类模板部分特化不应具有C链接

现在很明显,我们不想更改 name 的链接,因为我们依赖于修改工作。但是,禁止更改类型的链接的原因是什么?它似乎限制我们必须使用C ++调用约定;有人知道原因,是否有一个简单的工作来实现我的初始目标?


我改变了我尝试仅将类型链接到现在的方式,如下所示

extern "C" typedef void ftype(int);

template<typename T>
ftype f;

这很好用。遗憾的是,在使用这种技术时,我没有看到定义f的方法。但无论如何,我试过没有编译器诊断过这个(试过EDG / comeau,GCC和clang),尽管这看起来和以前完全一样:名称应该没有C语言链接,但只有类型有。

任何人都能解释一下吗?

3 个答案:

答案 0 :(得分:3)

C标头是什么样的?某处,C源必须枚举允许的回调类型。您应该利用这个机会获得一系列宏,这些宏为单个存根函数生成原型,并在C ++源代码中生成相应的宏序列,生成extern "C"存根。


关于第二个问题:是的,这有效,但typedef不在模板中。我试图将这样的typedef放在一个类中,但事实证明,extern "C"中不允许使用类模板。所以你可以有一个函数模板,但没有依赖类型的参数。

仅仅定义这个功能很简单:

extern "C" typedef void ftype(int);

template<typename T>
static ftype f; // <- added "static" here

template< typename T >
void f(int q) {}

啊哈,变量函数!

extern "C" typedef void ftype( int, ... );

template<typename T>
static ftype f;

template< typename T >
static void f( int z, ... ) {
    va_list va;
    va_start( va, z );
    T v = va_arg( va, T );
    va_end( va );

    std::cout << v;
}

你真的不需要类型推导,因为它只是一个回调,所以你可以将这个& f<int>传递给C代码,所有回调具有相同的类型,并且它可以在运行时进行类型确定并通过无论通过varargs想要什么。

答案 1 :(得分:1)

我不知道限制的原因,但你不能使用一个调用你关心的特定模板实例化的包装extern "C"函数吗?

答案 2 :(得分:1)

  

...是否有一个简单的工作来实现我的初始目标?

您可以从几个角度进行处理,具体取决于您希望如何配置和声明它们。

接下来是四种方法,每种方法都有一个名称空间“类型”可能是最简单的用法。

#include <stdio.h> // favored to reduce exports in later demonstration

#define FUNC_NAME __PRETTY_FUNCTION__
// or __func__ or __FUNCTION__ or...

extern "C" {

    /* C prototype */
    typedef void ftype(int a);

    /* the c function all our functions are piped into */
    void call(ftype* a);

    /* helper which serves as the implementation for our functions */
    void print(int a, const char* const func);

    /* C definitions (used in namespace examples below) */
    static void static_float(int a) {
        print(a, FUNC_NAME);
    }

    static void static_double(int a) {
        print(a, FUNC_NAME);
    }

    void extern_float(int a);
    void extern_float(int a) {
        print(a, FUNC_NAME);
    }

    void extern_double(int a);
    void extern_double(int a) {
        print(a, FUNC_NAME);
    }

    static void static_function_float(int a) {
        print(a, FUNC_NAME);
    }

    static void static_function_double(int a) {
        print(a, FUNC_NAME);
    }

} /* << extern C */

namespace Extern {

    /**
     interface demonstrates C functions as template arguments
    */
    template<ftype Fn>
    struct t_func {
        static ftype* Function() {
            return Fn;
        }
    };

    template<typename T> struct bind;

    template<> struct bind<float> {
        typedef t_func<extern_float> F;
    };

    template<> struct bind<double> {
        typedef t_func<extern_double> F;
    };

    template<typename T>
    void Call(T a) {
        (void) a;
        call(bind<T>::F::Function());
    }

} /* << Extern */

namespace Static {

    /**
     interface demonstrates template types wrapping static C functions
     */
    template<typename T> struct bind;

    template<> struct bind<float> {
        static ftype* F() {
            return static_float;
        }
    };

    template<> struct bind<double> {
        static ftype* F() {
            return static_double;
        }

    };

    template<typename T>
    void Call(T a) {
        (void) a;
        call(bind<T>::F());
    }

} /* << Static */

namespace Function {

    /**
     interface demonstrates template functions wrapping static C functions
     */

    template<typename T> ftype* bind();

    template<> ftype* bind<float> () {
        return static_function_float;
    }

    template<> ftype* bind<double> () {
        return static_function_double;
    }

    template<typename T>
    void Call(T a) {
        (void) a;
        call(bind<T> ());
    }

} /* << Function */

namespace Type {

    /**
     interface demonstrates template types implementing static functions.
     although gcc4.2 and clang both compile it, i'm uncertain that this is conforming.
    */

    template<typename T> struct bind {
        static void F(int a);
    };

    template<> void bind<float>::F(int a) {
        print(a, FUNC_NAME);
    }

    template<> void bind<double>::F(int a) {
        print(a, FUNC_NAME);
    }

    template<typename T>
    void Call(T a) {
        (void) a;
        call(bind<T>::F);
    }

} /* << Type */

int main(int argc, const char * argv[]) {
    (void) argc;
    (void) argv;

    const float f(1.0f);
    const double d(5.0);

    Extern::Call(f);
    Extern::Call(d);

    Static::Call(f);
    Static::Call(d);

    Function::Call(f);
    Function::Call(d);

    Type::Call(f);
    Type::Call(d);

    return 0;
}

void call(ftype* a) {
    a(11);
}

void print(int a, const char* const func) {
    printf("%i: %s\n", a, func);
}

输出:

11: void extern_float(int)
11: void extern_double(int)
11: void static_float(int)
11: void static_double(int)
11: void static_function_float(int)
11: void static_function_double(int)
11: static void Type::bind<T>::F(int) [with T = float]
11: static void Type::bind<T>::F(int) [with T = double]
制造

nm unstripped:
    0000000100000daf s  stub helpers
    0000000100001048 D _NXArgc
    0000000100001050 D _NXArgv
    0000000100000bde T __ZN4Type4bindIdE1FEi
    0000000100000bc0 T __ZN4Type4bindIfE1FEi
    0000000100000d98 s __ZZ12extern_floatE19__PRETTY_FUNCTION__
    0000000100000c98 s __ZZ12static_floatE19__PRETTY_FUNCTION__
    0000000100000d80 s __ZZ13extern_doubleE19__PRETTY_FUNCTION__
    0000000100000cb0 s __ZZ13static_doubleE19__PRETTY_FUNCTION__
    0000000100000d60 s __ZZ21static_function_floatE19__PRETTY_FUNCTION__
    0000000100000d38 s __ZZ22static_function_doubleE19__PRETTY_FUNCTION__
    0000000100000cc8 s __ZZN4Type4bindIdE1FEiE19__PRETTY_FUNCTION__
    0000000100000d00 s __ZZN4Type4bindIfE1FEiE19__PRETTY_FUNCTION__
    0000000100001060 D ___progname
    0000000100000000 A __mh_execute_header
    0000000100001058 D _environ
                     U _exit
    0000000100000c00 T _extern_double
    0000000100000b20 T _extern_float
    0000000100000c20 T _main
                     U _printf
    0000000100000b60 t _static_double
    0000000100000b40 t _static_float
    0000000100000ba0 t _static_function_double
    0000000100000b80 t _static_function_float
                     U dyld_stub_binder
    0000000100000ae0 T start


nm stripped:
    0000000100000000 A __mh_execute_header
                     U _exit
                     U _printf
                     U dyld_stub_binder
抱歉,我今晚没有仔细考虑标准 - 希望有所帮助。祝你好运!