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

Joh*_*itb 14 c++ templates

我有以下模板

template<typename T> void f(T t) { }
Run Code Online (Sandbox Code Playgroud)

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

g(&f<int>);
Run Code Online (Sandbox Code Playgroud)

但是因为我想要是可移植的,我希望"f"的调用约定与C的那个匹配.所以我试验了语言链接如何影响调用约定和发现

  • 函数类型的语言链接会影响要使用的调用约定
  • 函数名称的语言链接会影响修改

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

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

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

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

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

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

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


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

extern "C" typedef void ftype(int);

template<typename T>
ftype f;
Run Code Online (Sandbox Code Playgroud)

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

有谁能解释一下?

Pot*_*ter 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) {}
Run Code Online (Sandbox Code Playgroud)

啊哈,可变参数函数!

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;
}
Run Code Online (Sandbox Code Playgroud)

您实际上并不需要类型推导,因为它只是一个回调,因此您可以将其传递& f<int>给 C 代码,所有回调都具有相同的类型,并且它可以在运行时进行类型确定并通过可变参数传递它想要的任何内容。