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.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With