使用C调用约定调用函数模板专门化
Calling function template specialization using C calling conventions
我有以下模板
template<typename T> void f(T t) { }
我想把它的特定专门化的地址传递给一个C函数
g(&f<int>);
但是由于我想要可移植,我希望"f"的调用约定与c的调用约定相匹配。所以我实验了语言链接如何影响调用约定并发现
- 函数类型的语言链接影响使用的调用约定函数名的语言链接影响修改
c++规范的语言链接部分说
在链接规范中,指定的语言链接应用于所有函数声明符的函数类型、带有外部链接的函数名以及在链接规范中声明的带有外部链接的变量名。
因此,为了防止禁用mangling,这是模板在目标文件中区分不同专门化所需要的,我做了如下操作
extern "C" {
/* function name will not be affected */
template<typename T> static void f(T t) { }
}
但是它给了我一个编译器错误,说模板不能有C语言链接,我认为这意味着它抱怨函数模板的函数类型。事实上,我在规范中找到了
现在对我来说很明显,我们不想改变名称的链接,因为我们依赖于mangling来工作。但是禁止更改类型的链接的原因是什么呢?它似乎限制了我们必须使用c++调用约定;是否有人知道原因,是否有一个简单的工作,以实现我最初的目标?模板、模板显式特化(14.7.3)和类模板部分特化不能有C链接
我改变了我现在只给类型提供链接的方式,如下所示
extern "C" typedef void ftype(int);
template<typename T>
ftype f;
这个很好。遗憾的是,在使用这种技术时,我没有看到定义f
的方法。但无论如何,我尝试过的编译器都没有诊断出这个问题(尝试过EDG/comau, GCC和clang),尽管这看起来与之前的情况完全相同:名称应该没有C语言链接,但只有类型有。
有人能解释一下吗?
C头看起来像什么?在某个地方,C源代码必须枚举允许的回调类型。你应该抓住这个机会,使用一系列宏来生成单个存根函数的原型,并在c++源代码中使用相应的宏序列来生成extern "C"
存根。
对于第二个问题:是的,这是有效的,但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代码,所有的回调都有相同的类型,它可以在运行时确定类型,并通过变量传递它想要的任何东西。
我不知道限制的原因,但是您不能使用包装器extern "C"
函数调用您关心的特定模板实例化吗?
…是否有一个简单的工作,以实现我的初始目标?
你可以从几个角度来处理它,这取决于你想如何配置和声明它们。
接下来有四种方法,每个名称空间分别演示。"Type"可能是最简单的用法了。
#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: %sn", 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
对不起,我今晚没有仔细研究标准——希望这对你有帮助。好运! - 什么时候调用组成单元对象的析构函数
- 对RValue对象调用的LValue ref限定成员函数
- 为什么使用 "this" 指针调用派生成员函数?
- 函数调用中参数的顺序重要吗
- OpenGL - 在抛出"__gnu_cxx::recursive_init_error"实例后终止调用?
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 线程 std::调用未知类型,无法专门化函数错误
- 如何在c++运算符()中专门化调用模板
- 从专用模板方法调用无专门化的模板方法
- 从类模板的(部分)专门化调用静态函数
- 指针类型的类模板专门化调用中没有匹配的函数
- 基于运行时值调用不同的模板函数专门化
- 对STL算法进行专门化,以便在可用时自动调用高效的容器成员函数
- 根据调用方/上下文专门化函数的行为
- 专门化函数从其他函数调用
- 调用模板和专门化来交叉验证结果
- 如何在专门化版本中调用泛型模板函数
- C++模板专门化,对可以是指针或引用的类型调用方法
- 使用C调用约定调用函数模板专门化