内联函数编译

Inline function compilation

本文关键字:编译 函数      更新时间:2023-10-16

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

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

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

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

static inline的(相当小的(函数(或者通常,只是inline,甚至是在某些classstruct定义的成员函数(如果不使用(请参阅此处(,则(在实践中(不会出现在代码中,并且可能会在任何地方内联。当然,您需要在编译命令中启用优化。因此,如果使用 GCC,请使用 g++ -Wall -O2 进行编译(您可以添加-fverbose-asm -S并查看生成的汇编代码进行检查(。

一些编译器(可能还有g++(如果不要求优化,就不会打扰内联。内联始终是一种优化,编译器在某些情况下可能不会这样做(特别是在将该函数的地址存储在某处时(

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

此外,最近的 C++11(和 C++14(实现已经包装了操作系统 API 的重要部分(特别是标准 C++ IO 库和C++线程支持库以及最近的 C++14 TS(,通常已经在使用异常,因此更好地利用它们并使用最新的C++编译器(对于 GCC,这意味着 2015 年 11 月的 GCC 5.2(。

(换句话说,至少为 C++11 编写你的东西,而不是 C++98(

在带有GCC(或Clang/LLVM(的Linux上,如果制作库,您可能会对链接时间优化(编译和链接g++ -O2 -flto(,预编译标头,可见性函数属性感兴趣。

关于 Linux 上的程序库,请阅读程序库操作方法。对于共享库,请阅读 Drepper 的论文:如何编写共享库和这个答案。

实际上,某些库中的

内联函数通常不会在库中概述,而是在调用它的应用程序中概述。因此,如果您的共享库Foo在公共标头中定义<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概述,例如将maxsq的地址存储在某处(或者如果你没有要求足够的优化(。

请记住,内联始终是一种优化,某些编译器有时可能会避免它(即使出于良好的性能原因(。在实践中,请信任您的编译器(实际上,您的C++实现还包括链接器,该链接器可能会"垃圾回收"部分(。