在函数声明之前添加预处理器变量意味着什么?
What does adding a pre-processor variable before a function declaration means?
我正在阅读 Dear ImGui API,我想知道添加预处理器变量是什么意思(此处 IMGUI_API) 在像这样的函数声明之前:
#ifndef IMGUI_API
#define IMGUI_API
#endif
...
namespace ImGui
{
...
IMGUI_API float GetWindowWidth();
...
};
提前致谢!
正如 Hans Passant 所说 - 它不会永远是空的。
动态共享 objects(和 Windows 中的 DLL)的主要属性之一是库的内部符号不会暴露给用户。在 GCC 中,这是通过在构建库时在命令行上将默认符号可见性指定为 "hidden" 来完成的。但是,这会产生不良影响,使所有符号(包括您希望用户调用的符号)都隐藏起来,从而使您的库实际上变得无用。
GCC 提供了一种手动控制可见性的方法 - __attribute__((visibility("default")))
。这将函数标记为具有默认可见性,这意味着它将由 DSO 公开。该属性在函数声明之前指定。
这会产生一个新问题 - 当您的图书馆的用户包含您的 header 文件时,他们不会 need/want 在您的图书馆功能上指定的这个属性。当你的 header 被用户包含时,这些定义应该被删除,只留下一个简单的函数声明。
有两种方法可以解决此问题 - 复制两份 header,或者使用预处理器在编译前更改代码。大多数(几乎所有)库程序员选择后者,因为保持同一文件的两个副本同步非常困难。
ImGui 的作者实现后一个选项的方式相当优雅——而不是像这样定义他们的 API 内联标记:
#ifdef IMGUI_API_BUILD
#define IMGUI_API __attribute__((visibility("default")))
#else
#define IMGUI_API
#endif
他们的定义如下:
#ifndef IMGUI_API
#define IMGUI_API
#endif
这具有将符号定义为默认值(如果未被覆盖)的效果。
这实际上是在编译过程中——通过在命令行上指定 IMGUI_API
的定义,他们可以更改代码的行为,而无需将其构建过程的元素暴露给世界。
他们所要做的就是将 -D IMGUI_API=__attribute((visibility("default")))
添加到编译器命令行,然后 header 文件神奇地将每个 API 函数标记为从共享 object 导出.
事实上,他们可以将定义更改为他们想要的任何值,而无需在其他程序员以这种方式使用的 header 文件中公开该细节。调试信息、附加功能选项、指定 hot/cold 路径信息...所有这些都可以在命令行上指定,而无需将该信息放入 header 文件中。
编辑:
现在查看他们的代码,他们似乎没有意识到这一点,并且在无意中为 Linux 创建了一个非常强大的 shared-library 解决方案。尽管如此,以上所有内容仍然有效——其他图书馆也可以做同样的事情。
我正在阅读 Dear ImGui API,我想知道添加预处理器变量是什么意思(此处 IMGUI_API) 在像这样的函数声明之前:
#ifndef IMGUI_API
#define IMGUI_API
#endif
...
namespace ImGui
{
...
IMGUI_API float GetWindowWidth();
...
};
提前致谢!
正如 Hans Passant 所说 - 它不会永远是空的。
动态共享 objects(和 Windows 中的 DLL)的主要属性之一是库的内部符号不会暴露给用户。在 GCC 中,这是通过在构建库时在命令行上将默认符号可见性指定为 "hidden" 来完成的。但是,这会产生不良影响,使所有符号(包括您希望用户调用的符号)都隐藏起来,从而使您的库实际上变得无用。
GCC 提供了一种手动控制可见性的方法 - __attribute__((visibility("default")))
。这将函数标记为具有默认可见性,这意味着它将由 DSO 公开。该属性在函数声明之前指定。
这会产生一个新问题 - 当您的图书馆的用户包含您的 header 文件时,他们不会 need/want 在您的图书馆功能上指定的这个属性。当你的 header 被用户包含时,这些定义应该被删除,只留下一个简单的函数声明。
有两种方法可以解决此问题 - 复制两份 header,或者使用预处理器在编译前更改代码。大多数(几乎所有)库程序员选择后者,因为保持同一文件的两个副本同步非常困难。
ImGui 的作者实现后一个选项的方式相当优雅——而不是像这样定义他们的 API 内联标记:
#ifdef IMGUI_API_BUILD
#define IMGUI_API __attribute__((visibility("default")))
#else
#define IMGUI_API
#endif
他们的定义如下:
#ifndef IMGUI_API
#define IMGUI_API
#endif
这具有将符号定义为默认值(如果未被覆盖)的效果。
这实际上是在编译过程中——通过在命令行上指定 IMGUI_API
的定义,他们可以更改代码的行为,而无需将其构建过程的元素暴露给世界。
他们所要做的就是将 -D IMGUI_API=__attribute((visibility("default")))
添加到编译器命令行,然后 header 文件神奇地将每个 API 函数标记为从共享 object 导出.
事实上,他们可以将定义更改为他们想要的任何值,而无需在其他程序员以这种方式使用的 header 文件中公开该细节。调试信息、附加功能选项、指定 hot/cold 路径信息...所有这些都可以在命令行上指定,而无需将该信息放入 header 文件中。
编辑:
现在查看他们的代码,他们似乎没有意识到这一点,并且在无意中为 Linux 创建了一个非常强大的 shared-library 解决方案。尽管如此,以上所有内容仍然有效——其他图书馆也可以做同样的事情。