【发布时间】:2017-01-31 08:44:03
【问题描述】:
我在日志辅助函数中使用strerror_r。如手册页所述,此函数有两个版本。 POSIX 版本返回一个 int。 GNU 版本返回一个字符串 (char*)。
因此,为了让我的 C++ 代码更具可移植性,我有一段类似这样的代码:
char buffer[1000];
int size = 1000;
int result = 0;
char* msg = buffer;
buffer[0] = '\0';
#ifdef _GNU_SOURCE
msg = strerror_r(err, buffer, size);
#else
result = strerror_r(err, buffer, size);
if (result != 0)
{
sprintf(buffer, "unknown error: %d", err);
}
#endif
LogToFile(msg);
在上面的代码块中,它将使用 strerror_r 的任一版本,这取决于 _GNU_SOURCE 的存在,这是由 g++ 始终设置的,因为 libstdc++ 需要它。在 Mac 和 Unix 的其他变体上,它将使用 POSIX 版本。
直到今天,这段代码已经运行了很长时间。一个用户试图在Alpine Linux 上编译我的代码,并在今天使用strerror_r 在线报告了这个编译器错误
main.cpp:16:21 error: invalid conversion from 'int' to 'char*' [-fpermissive]
映射到这一行:
#ifdef _GNU_SOURCE
msg = strerror_r(err, buffer, size);
在这个平台上的/usr/include/string.h 达到顶峰揭示了以下内容:
#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
|| defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
|| defined(_BSD_SOURCE)
...
int strerror_r (int, char *, size_t);
...
#endif
无论使用什么编译器环境,strerror_r 的唯一声明版本都是返回 int 的 POSIX 版本。这就解释了为什么会发生错误。
无需告诉用户他们可以手动#undef _GNU_SOURCE 或修改源代码,我该如何解决这个问题以使代码可以继续移植?全局未定义 _GNU_SOURCE 可能是一个非首发,因为这是 C++(如上所述,libstdc++ 需要)。我正在尝试查看是否还有其他可以测试的宏组合,但我无法提出任何明显的建议。
【问题讨论】:
-
官方宏检查(根据您链接到的手册页)是
(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE,如果提供了 POSIX 版本,则为真,否则提供 GNU 版本。这意味着您的检查应该足以确保提供了 GNU 版本,并且 Alpine Linux 中的 C 库似乎存在错误或过时。 -
将其包装在您自己的函数中。使其足够灵活以使用任一变体。
-
@n.m.你能详细说明吗?它已经包含在一个函数中......
-
C++ 足够强大,可以在没有宏帮助的情况下决定使用哪个版本的函数。想想函数重载。
-
Alpine Linux 上的 C 库是 musl,如果您使用最新的 Alpine 版本,它就是最新的。