当我构建 POSIX C 程序时,我希望能够移植并仅使用 POSIX 或标准 C 库函数。因此,例如,使用 gcc 或 clang,我会这样构建:
\ngcc -std=c99 -D_XOPEN_SOURCE=600
将标准设置为 C99 会删除所有扩展,然后_XOPEN_SOURCE公开 POSIX 接口。我的环境不再被 GNU、BSD 等扩展所污染。
然而,C++ 的情况似乎更加阴暗。我想做这个:
\ng++ -std=c++14 -D_XOPEN_SOURCE=600
这对我来说在各种操作系统上都运行良好:至少是 Linux/glibc、Haiku、MinGW、macOS。但显然,POSIX 功能测试宏和 C++ 存在问题。Oracle 文档有这样的说法:
\n\n\n没有为 POSIX 或 SUSv4 定义 C++ 绑定,因此指定功能测试宏(例如 _POSIX_SOURCE、_POSIX_C_SOURCE 和 _XOPEN_SOURCE)可能会由于标准 C++ 和这些规范的要求冲突而导致编译错误。
\n
虽然我没有 Oracle Solaris 的副本,但我发现 FreeBSD 和 OpenBSD 存在问题。
\n在 FreeBSD 上:
\n#include <iostream>\nint main() { }\nRun Code Online (Sandbox Code Playgroud)\n$ clang++ -std=c++14 -D_POSIX_C_SOURCE=200112L t.cpp\nIn file included from t.cpp:1:\nIn file included from /usr/include/c++/v1/iostream:37:\nIn file included from /usr/include/c++/v1/ios:215:\n/usr/include/c++/v1/__locale:631:16: error: use of undeclared identifier \'isascii\'\n return isascii(__c) ? (__tab_[static_cast<int>(__c)] & __m) !=0 : false;\n...\nRun Code Online (Sandbox Code Playgroud)\n(这可以很好地构建_XOPEN_SOURCE=600)。FreeBSD 上的 C++ 标头使用,一个非标准函数,因此在设置isacii时不会公开。_POSIX_C_SOURCE
或者在 OpenBSD 上:
\n#include <fstream>\nint main() { }\nRun Code Online (Sandbox Code Playgroud)\n$ clang++ -std=c++14 -D_XOPEN_SOURCE=600 t.cpp\nIn file included from t.cpp:1:\nIn file included from /usr/include/c++/v1/fstream:183:\nIn file included from /usr/include/c++/v1/ostream:138:\nIn file included from /usr/include/c++/v1/ios:215:\nIn file included from /usr/include/c++/v1/__locale:32:\nIn file included from /usr/include/c++/v1/support/newlib/xlocale.h:25:\n/usr/include/c++/v1/support/xlocale/__strtonum_fallback.h:23:64: error: unknown type name \'locale_t\'\n char **endptr, locale_t) {\nRun Code Online (Sandbox Code Playgroud)\n大概<locale.h>没有被包含在 \xe2\x80\x9c 应该 \xe2\x80\x9d 的地方。
我得出的令人担忧的结论是,您无法移植一个不含非 POSIX 扩展的 POSIX C++ 环境。如果删除功能测试宏,这些示例在 OpenBSD 和 FreeBSD 上运行良好。这看起来是因为 BSD 头文件公开了 BSD 函数,除非在标准 C 模式下,但它们不关心标准 C++ 模式(它们显式检查是否设置了与 C89、C99 或 C11 对应的宏)。glibc 看起来是一样的:它仍然在标准 C++ 模式下公开非标准 C 函数,所以也许我在那里遇到构建错误只是时间问题。
\n所以实际的问题是:您可以编写不公开特定于平台的功能的可移植 POSIX C++ 代码吗?或者,如果我的目标是使用 C++ 的 POSIX,我是否应该不设置任何功能测试宏并希望得到最好的结果?
\n编辑:
\n我开始思考这一点的含义(例如,我为什么关心?),我认为下面的程序说明了这一点。这是 Linux/glibc:
\n#include <ctime>\nint dysize;\nRun Code Online (Sandbox Code Playgroud)\n$ g++ -c -std=c++14 t.cpp\nt.cpp:2:5: error: \xe2\x80\x98int dysize\xe2\x80\x99 redeclared as different kind of entity\n 2 | int dysize;\n | ^~~~~~\nIn file included from t.cpp:1:\n/usr/include/time.h:262:12: note: previous declaration \xe2\x80\x98int dysize(int)\xe2\x80\x99\n 262 | extern int dysize (int __year) __THROW __attribute__ ((__const__));\nRun Code Online (Sandbox Code Playgroud)\n这是标准<ctime>标头,不包含任何称为dysize. 这是 glibc 出于兼容性目的而包含的一个旧 SunOS 函数。用 构建的 C 程序-std=c99不会暴露它,但 C++ 总是会暴露它。并且没有真正的方法可以知道各种实现将使用哪些非保留标识符。如果-std=c++14导致非标准标识符被隐藏,那就可以避免这个问题,但事实并非如此,而且我看不到解决这个问题的方法。
这可能意味着功能测试宏是一个转移注意力的东西:问题的根源在于,至少在一些现实世界的实现中,C++ 编译器暴露了它们不应该暴露的符号。
\n小智 0
我的建议是构建一个工具链,并使用库、包含、正确的编译器(可能是只能使用 POSIX 库、包含等的剥离版本)。
为了使其可移植,通常您将使用静态链接器构建应用程序。可能需要其他链接器选项来专门指向或包含您的工具链环境路径。
如果您使用 POSIX 线程,则可能需要 -pthread。
我发现您正在使用系统范围的标头和库,而实际上,您可能需要特定于您的 POSIX 应用程序工具链,以避免污染。
| 归档时间: |
|
| 查看次数: |
463 次 |
| 最近记录: |