我打算为操作系统 API 提供简单的包装器,它会在发生错误时抛出异常。这些包装器很简单,并且都被定义为头文件中的内联函数。由于系统 API 应该很大,头文件也应该很大,包含大量微小的内联函数。问题是,如果共享库 (.so) 是在包含头文件的情况下编译的,那么所有这些微小的包装器是否都会被编译到生成的二进制文件中,从而产生一个大的二进制文件,即使实际上只有一小部分包装器是用过的?可执行文件的情况会有所不同吗?如果是这样,将包装器拆分为多个头文件是否是唯一的解决方案?或者我应该通过指定 static
使包装器内部链接?
这是我的想法。包装器可能会被 ODR 使用(例如,获取其地址)。在 Linux 平台上,默认导出具有外部链接的函数(即,可由其他二进制模块链接)。所以我想链接器可能有必要为它们实际生成大纲定义。请参阅描述部分中的要点 3) here .
在 Windows API 中包装 CloseHandle()
的简单示例:
inline void close_handle(HANDLE handle) {
if (!CloseHandle(handle)) {
throw std::system_error(GetLastError(), std::system_category(), "CloseHandle");
}
}
最佳答案
声明为 static inline
的(相当小的)函数(或者通常只是 inline
,或者甚至是定义在 inside 某些 class
或 struct
中的成员函数)将不会(在实践中)出现在代码中,如果它未使用(请参阅 this ),并且可能会在任何地方内联。当然,您需要在编译命令中启用优化。因此,如果使用 GCC ,则使用 g++ -Wall -O2
进行编译(您可以添加 -fverbose-asm -S
并查看生成的汇编代码以进行检查)。
一些编译器(可能还有 g++
)如果不被要求优化则不会打扰内联。内联始终是一种优化,编译器在某些情况下可能不会这样做(特别是在某处存储该函数的地址时)
顺便说一句,看起来您正在重新发明一个类似于 POCO 或 Qt 的框架。您是否考虑过改用它们?
此外,最近的 C++11(和 C++14)实现已经包装了操作系统 API 的重要部分(特别是标准 C++ IO library 和 C++ thread support library 以及最近的 C++14 TS ),通常已经使用异常,因此更好地利用它们并使用最新 C++ 编译器(对于 GCC,这意味着 2015 年 11 月的 GCC 5.2)。
(换句话说,至少为 C++11 编写代码,而不是 C++98)
在带有 GCC(或 Clang/LLVM)的 Linux 上,如果创建一个库,您可能对链接时间优化(编译并链接带有 g++ -O2 -flto
的库)、precompiled headers、visibility 函数 attributes 感兴趣。
关于 Linux 上的程序库,阅读 Program Library HowTo 。对于共享库,请阅读 Drepper 的论文:How to Write Shared Libraries 和 this 答案。
在实践中,一些库中的内联函数通常不会在库中概述,而是在调用它的应用程序中。因此,如果您的共享库 Foo 在公共(public) header <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++ 实现还包括链接器,它可能会“收集垃圾”sections)。
关于c++ - 内联函数编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33734341/