Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

strerror_r is incorrectly declared on Alpine Linux

I'm using strerror_r in a logging helper function. As the man page describes, there are two versions of this function. The POSIX version returns an int. The GNU version returns a string (char*).

As such, so that my C++ code is more portable, I have a block of code similar to this:

    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);

In the above code block, it will use either version of strerror_r depending on the presence of _GNU_SOURCE, which is ALWAYS set by g++ because libstdc++ requires it. On Mac, and other variations of Unix, it will use the POSIX version.

Now this code has been working out well for a long time until today. A user trying to compile my code on Alpine Linux and reported this compiler error today on the line using strerror_r

main.cpp:16:21 error: invalid conversion from 'int' to 'char*' [-fpermissive]

Which maps to this line:

#ifdef _GNU_SOURCE
    msg = strerror_r(err, buffer, size);

Taking a peak at the /usr/include/string.h on this platform reveals the following:

#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

Which appears that no matter what compiler environment is used, the only declared version of strerror_r is the POSIX version that returns an int. So that explains why the error occurred.

Without having to tell users that they can manually #undef _GNU_SOURCE or modify the source, how can I work around this such that the code can continue to be portable? Globally undefining _GNU_SOURCE is likely a non-starter because this is C++ (and as mentioned above, required by libstdc++). I'm trying to see if there would be another macro combination that I could test for, but I'm not able to come up with anything obvious.

like image 628
selbie Avatar asked Jan 31 '17 08:01

selbie


1 Answers

You can take advantage of C++ function overloading:

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;
}

And and get rid of conditional compilation in the usage:

char buffer[1000];
buffer[0] = '\0';
char* msg = check_error(strerror_r(err, buffer, sizeof buffer), buffer, err);
LogToFile(msg);
like image 71
Maxim Egorushkin Avatar answered Nov 08 '22 21:11

Maxim Egorushkin