6

我们有一个包含大量 COM 对象的庞大 C++ 代码库。每个暴露给 COM 的函数都必须有__stdcall调用约定(通常是STDMETHODCALLTYPE宏),所以我们有很多函数标记为STDMETHODCALLTYPE

现在我看到了一个不是通过 COM 直接调用的函数,而是仅从我们的 C++ 代码中调用的函数,并且该函数STDMETHODCALLTYPE的签名中也有宏。我完全确定宏在那里毫无用处 - 没有通过 COM 调用该函数。

我应该放弃__stdcall它以使其成为“默认”调用约定函数吗?我该如何做出这样的决定?

4

5 回答 5

9

我的方法是对内部代码使用默认的编译器调用约定,并对跨模块边界导出的任何方法使用定义明确的显式调用约定。

出于性能原因,大多数编译器的默认调用约定充分利用了寄存器,因此在适当的情况下使用它有好处。它还使您的代码更容易看,因为您无需指定约定即可获得默认值。

对于导出的函数,您显然需要指定约定。如果您正在创建一个您预计将从 C 或 C++ 以外的语言调用的库,则使用 stdcall 是常规的。如果您只期望 C 或 C++ 客户端,那么 cdecl 可能是最常见的约定。

于 2011-05-03T14:05:32.497 回答
5

当 Windows 从 __ cdecl 切换到 _ _stdcall 作为默认调用约定时,产品的大小下降了大约 10%。这种节省完全与调用 stdcall 方法后删除堆栈调整有关(__cdecl 是“调用者调整堆栈以删除参数”调用约定,__stdcall 是“被调用者调整堆栈以删除参数”调用约定,因为还有更多调用者比被调用者,切换会减小二进制文件的大小)。

使用 __stdcall 的缺点是你没有可变参数#s(由于被调用者调整堆栈,他们无法知道调用者指定了多少个参数)。

底线:从“默认”调用约定切换到 __stdcall 可以减少二进制文件的大小。这对你来说可能重要也可能不重要。

但是,正如上面提到的 mkaes,如果您的代码在另一个编译器中被访问过(例如,如果您将.lib文件传递​​给其他人),那么声明使用的调用约定绝对至关重要。

于 2011-05-03T15:09:24.093 回答
3

COM 东西显式设置调用约定的唯一原因是因为它是跨 DLL 边界使用的。
所以我的建议是放弃调用约定的显式设置并通过编译器设置进行设置。
一般来说:
如果函数作为 DLL 导出,请设置一个宏,该宏在 Headers 中定义调用约定。这可以防止来自 DLL 的用户在链接到您的 DLL 时使用错误的调用约定。显式覆盖编译器设置。
不要在本地函数上使用任何调用对流。约定可以由编译器开关设置。如果您决定显式设置一个,请在所有函数上执行此操作。然后你仍然有一个中心位置来改变调用约定。
当然,如果它有意义或者您需要一些特殊的调用约定,例如用于优化的 fastcall,那么您也需要显式设置。

于 2011-05-03T14:05:42.223 回答
2

您是否启用了整个程序优化链接时代码生成?如果是这样,并且您没有从 DLL 导出函数或传递指向它的指针,那么编译器可能会为该函数生成自定义调用约定或将其内联(即使它未在头文件中定义)。

于 2011-05-03T15:11:08.327 回答
1

您可以通过搜索与解决方案关联的 ODL 文件来查看您的地图是否被引用。如果它不存在,则它没有接口,您可以更改调用约定。存在其他人假设所有函数都使用此调用约定设置的风险,并且他们可以在以后添加接口。

于 2011-05-03T14:09:04.587 回答