strerror_r 在 Alpine Linux 上的声明不正确

strerror_r is incorrectly declared on Alpine Linux

我在日志记录辅助函数中使用 strerror_r。如手册页所述,此函数有两个版本。 POSIX 版本 returns 一个 int。 GNU版本returns一个字符串(char*).

因此,为了让我的 C++ 代码更具可移植性,我有一段与此类似的代码:

    char buffer[1000];
    int size = 1000;
    int result = 0;
    char* msg = buffer;
    buffer[0] = '[=12=]';
#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 的唯一声明版本是 returns 一个 int 的 POSIX 版本。这样就解释了为什么会发生错误。

在不必告诉用户他们可以手动 #undef _GNU_SOURCE 或修改源代码的情况下,我该如何解决这个问题以使代码可以继续移植?全局取消定义 _GNU_SOURCE 可能是行不通的,因为这是 C++(并且如上所述,libstdc++ 需要)。我正在尝试查看是否还有其他可以测试的宏组合,但我无法想出任何明显的东西。

您可以利用 C++ 函数重载:

char* check_error(int result, char* buffer, int err) {
    if(result)
        sprintf(buffer, "unknown error: %d", err);
    return buffer;
}

char* check_error(char* result, char*, int) {
    return result;
}

并且在用法中去掉条件编译:

char buffer[1000];
buffer[0] = '[=11=]';
char* msg = check_error(strerror_r(err, buffer, sizeof buffer), buffer, err);
LogToFile(msg);