0

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

这是我的想法。包装器可以被 ODR 使用(例如,获取其地址)。而在Linux 平台上,默认导出具有外部链接的函数(即可以被其他二进制模块链接)。所以我想链接器可能有必要为它们实际生成大纲定义。请参阅此处描述部分中的要点 3) 。

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

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

1 回答 1

2

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

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

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

此外,最近的 C++11(和 C++14)实现已经封装了 OS 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 上的程序库,请阅读Program Library HowTo。对于共享库,请阅读 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++ 实现还包括链接器,它可能会“收集垃圾”部分)。

于 2015-11-16T12:18:06.757 回答