如何知道 Qt 项目是否正在使用调用约定?
How to know whether a calling convention is in use with a Qt project?
你知道,当你定义一个函数时,你可以同时定义它的调用约定,就像这样:
int __stdcall foo(int) { return 1; };
int __cdecl bar(int) { return 1; };
而如果要使用此函数推导模板参数,则必须处理调用约定,例如:
template<typename T> void tmpl_func( T( __stdcall f )(T) ) { other_func<T>(); };
使用此模板,您只能使用“foo”作为参数。如果使用“bar”,则无法编译。
tmpl_func(foo); // ok
tmpl_func(bar); // err
因此,如果您有一个使用 __cdecl 的函数,您必须使用两种调用约定来定义模板函数,如下所示:
template<typename T> void tmpl_func( T( __stdcall f )(T) ) { other_func<T>(); };
template<typename T> void tmpl_func( T( __cdecl f )(T) ) { other_func<T>(); };
和foo实例化__stdcall版本,bar实例化__cdecl版本,没有重新定义。
总的来说,这很好用,但最近我遇到了一个问题:
在Qt项目中,当我按照上面所说的方式定义模板函数时,编译器说:__cdecl模板函数已经定义。
而如果在__stdcall版本中只定义tmpl_func,也可以使用bar 作为参数。
这是否意味着我编写的所有调用约定标识符都不起作用,并且所有函数都以__cdecl方式调用?
为什么以及我怎么知道这种情况是否会发生?我可以使用宏或编译选项检查它吗?
抱歉我的英语不好,我希望我已经说清楚了,但这确实困扰了我一段时间。
You know, when you define a function, you can define it's calling convention at the same time
这是错误的。您描述的是 C++ 的(特定于供应商的)扩展。例如,在 Linux 上我的 GCC 编译器上,它可能无法工作。
In a Qt project,
根本不要在您的 Qt 代码中使用任何 explicit__stdcall
或 __cdecl
注释。
(在您的 Qt 代码中使用它是搬起石头砸自己的脚:它很疼;坚持 Qt5 项目的标准 C++11 代码)
如果你需要调用一些外部奇怪的函数(使用奇怪的&explicit调用约定)写一些extern "C"
(或者一些 static inline
一个,如果它很短)包装函数这样做(并且你的包装函数有一个通常的签名,没有显式调用约定注释)。
实际上,在编写 Qt 项目时,您使用所有警告和调试信息对其进行编译(因此 g++ -Wall -g
如果使用 GCC),您使用 gdb
调试器对其进行调试,然后进行优化(例如用 g++ -Wall -g -O2
编译)它 - 例如对于基准测试和生产代码——你相信编译器可以很好地优化,内联许多函数调用,并选择足够好的调用约定。不要乱用 Qt 代码中的调用约定。
(隐含地你在问 __stdcall
或 __cdecl
是否以及如何改变 C++ 的类型系统;并且 C++ 类型已经足够复杂,你不想要更乱)
你知道,当你定义一个函数时,你可以同时定义它的调用约定,就像这样:
int __stdcall foo(int) { return 1; };
int __cdecl bar(int) { return 1; };
而如果要使用此函数推导模板参数,则必须处理调用约定,例如:
template<typename T> void tmpl_func( T( __stdcall f )(T) ) { other_func<T>(); };
使用此模板,您只能使用“foo”作为参数。如果使用“bar”,则无法编译。
tmpl_func(foo); // ok
tmpl_func(bar); // err
因此,如果您有一个使用 __cdecl 的函数,您必须使用两种调用约定来定义模板函数,如下所示:
template<typename T> void tmpl_func( T( __stdcall f )(T) ) { other_func<T>(); };
template<typename T> void tmpl_func( T( __cdecl f )(T) ) { other_func<T>(); };
和foo实例化__stdcall版本,bar实例化__cdecl版本,没有重新定义。
总的来说,这很好用,但最近我遇到了一个问题:
在Qt项目中,当我按照上面所说的方式定义模板函数时,编译器说:__cdecl模板函数已经定义。
而如果在__stdcall版本中只定义tmpl_func,也可以使用bar 作为参数。
这是否意味着我编写的所有调用约定标识符都不起作用,并且所有函数都以__cdecl方式调用?
为什么以及我怎么知道这种情况是否会发生?我可以使用宏或编译选项检查它吗?
抱歉我的英语不好,我希望我已经说清楚了,但这确实困扰了我一段时间。
You know, when you define a function, you can define it's calling convention at the same time
这是错误的。您描述的是 C++ 的(特定于供应商的)扩展。例如,在 Linux 上我的 GCC 编译器上,它可能无法工作。
In a Qt project,
根本不要在您的 Qt 代码中使用任何 explicit__stdcall
或 __cdecl
注释。
(在您的 Qt 代码中使用它是搬起石头砸自己的脚:它很疼;坚持 Qt5 项目的标准 C++11 代码)
如果你需要调用一些外部奇怪的函数(使用奇怪的&explicit调用约定)写一些extern "C"
(或者一些 static inline
一个,如果它很短)包装函数这样做(并且你的包装函数有一个通常的签名,没有显式调用约定注释)。
实际上,在编写 Qt 项目时,您使用所有警告和调试信息对其进行编译(因此 g++ -Wall -g
如果使用 GCC),您使用 gdb
调试器对其进行调试,然后进行优化(例如用 g++ -Wall -g -O2
编译)它 - 例如对于基准测试和生产代码——你相信编译器可以很好地优化,内联许多函数调用,并选择足够好的调用约定。不要乱用 Qt 代码中的调用约定。
(隐含地你在问 __stdcall
或 __cdecl
是否以及如何改变 C++ 的类型系统;并且 C++ 类型已经足够复杂,你不想要更乱)