sha*_*oth 6 c++ windows com calling-convention visual-c++
我们有一个庞大的C++代码库,有很多COM对象.暴露给COM的每个函数都必须具有__stdcall调用约定(通常是STDMETHODCALLTYPE宏),因此我们标记了许多函数STDMETHODCALLTYPE.
现在我看到一个函数不是通过COM直接调用的,而只是从我们的C++代码中调用,而且这个函数STDMETHODCALLTYPE在其签名中也有宏.我完全确定宏在那里没用 - 没有通过COM调用该函数.
我应该删除__stdcall它,使其成为"默认"调用约定函数吗?我该如何做出这样的决定?
我的方法是使用内部代码的默认编译器调用约定,并对跨模块边界导出的任何方法使用明确定义的显式调用约定.
大多数编译器的默认调用约定出于性能原因而充分利用了寄存器,因此在适当的地方使用寄存器是有好处的.它还使您的代码更容易,因为您不需要指定约定来获取默认值.
对于导出的函数,您显然需要指定约定.如果您正在创建一个预期将使用C或C++以外的语言调用的库,那么使用stdcall是常规的.如果您只期望C或C++客户端,那么cdecl可能是最常见的约定.
当Windows从__ cdecl切换到_ _stdcall作为默认调用约定时,产品的大小下降了大约10%.这节省完全与在调用stdcall方法后删除堆栈调整有关(__cdecl是"调用堆栈以调整堆栈以删除参数"调用约定,__ stdcall是"callee调整堆栈以删除参数"调用约定,因为还有更多调用者比被调用者,切换减少二进制文件的大小).
使用__stdcall的缺点是你没有变量#s of argments(因为被调用者调整了堆栈,他们无法知道调用者指定了多少参数).
底线:从"默认"调用约定切换到__stdcall可能会导致二进制文件的大小减小.这对你来说可能重要,也可能不重要.
然而如前所述mkaes,如果您的代码EVER在另一个compiland访问(例如,如果你提供一个.LIB文件给别人),这是绝对关键的是你声明所用的调用约定.
COM 内容显式设置调用约定的唯一原因是因为它是跨 DLL 边界使用的。
所以我的建议是放弃调用约定的显式设置并通过编译器设置进行设置。
一般来说:
如果函数导出为 DLL,则设置一个在标头中定义调用约定的宏。这可以防止 DLL 的用户在链接到 DLL 时使用错误的调用约定。显式覆盖编译器设置。
不要在局部函数上使用任何调用对流。约定可以通过编译器开关设置。如果您决定明确设置一项,请对所有函数执行此操作。然后你仍然有一个中心位置来更改调用约定。
当然,如果它有意义或者您需要一些特殊的调用约定(例如用于优化的 fastcall),那么您也需要显式设置。