内联函数编译

时间:2015-11-16 11:31:28

标签: c++ compilation inline one-definition-rule header-only

我打算为操作系统API提供简单的包装器,它会在发生错误时抛出异常。这些包装器很简单,并且都被定义为头文件中的内联函数。由于系统API应该很大,因此头文件也应该很大,包含大量微小的内联函数。问题是,如果使用包含的头文件编译共享库(.so),将所有这些微小的包装器编译成生成的二进制文件,从而生成一个大的二进制文件,即使只有一小部分包装器实际存在用过的?可执行文件的情况如何,它会有所不同吗?如果是这种情况,将包装器分成多个头文件是唯一的解决方案吗?或者我应该通过指定static

来创建包装内部链接

这就是我的想法。包装器可以是ODR使用的(例如,取其地址)。在Linux平台上,默认情况下导出具有外部链接的函数(即,可由其他二进制模块链接)。所以我想链接器可能有必要为它们实际生成轮廓定义。请参阅说明部分here中的项目符号3)。

在Windows API中包装CloseHandle()的简单示例:

inline void close_handle(HANDLE handle) {
  if (!CloseHandle(handle)) {
    throw std::system_error(GetLastError(), std::system_category(), "CloseHandle");
  }
}

1 个答案:

答案 0 :(得分:2)

一个(相当小的)函数声明static inline(或者通常只是inline,或者甚至是中定义的成员函数class或{{1如果没有使用(参见this),则不会(实际上)出现在代码中,并且可能会在任何地方内联。当然,您需要在编译命令中启用优化。因此,如果使用GCC,请使用struct进行编译(您可以添加g++ -Wall -O2并查看生成的汇编程序代码以进行检查)。

如果没有要求优化,一些编译器(可能是-fverbose-asm -S)也不会打扰内联。并且内联总是一种优化,编译器在某些情况下可能不会这样做(特别是在某处存储该函数的地址时)

顺便说一句,看起来你正在重新发明一个类似于POCOQt的框架。您是否考虑过使用它们?

此外,最近的C ++ 11(和C ++ 14)实现已经包含了OS API的重要部分(特别是标准C ++ IO library和C ++ thread support library以及最近的C ++ 14 TS),通常已经使用异常,因此更好地利用它们并使用最近的 C ++编译器(对于GCC,这意味着GCC 5.2于2015年11月)。

(换句话说,至少为C ++ 11代码,而不是C ++ 98)

在使用GCC(或Clang/LLVM)的Linux上,如果创建了库,您可能会对链接时优化感兴趣(编译& 链接g++),precompiled headersvisibility函数attributes

关于Linux上的程序库,请阅读Program Library HowTo。对于共享库,请阅读Drepper的论文:How to Write Shared Librariesthis回答。

实际上,某些库中的内联函数通常不会在库中概述,而是在调用它的应用程序中概述。因此,如果您的共享库Foo在公共标题g++ -O2 -flto

中定义
<foo.h>

那么 inline int maxsq(int a, int b) { // you could add some conditional throw here... if (std::abs(a) < std::abs(b)) return b*b; else return a*a; } 的对象代码可能不会出现在maxsq内,而只会出现在您的程序中(其源代码中为libfoo.so)该计划需要#include <foo.h>概述,例如在某处存储maxsq的地址(或者如果你没有要求足够的优化)。

请记住,内联总是是一种优化,有些编译器有时可能会避免它(即使出于良好的性能原因)。在实践中,信任您的编译器(实际上,您的C ++实现还包括链接器,可能是“垃圾收集”sections)。