One of the challenges of writing portable code is dealing with variations of
APIs that are supposed to be standard. In this post I’ll talk about
friends which turned out to be particularly interesting to detect.
First, why not just use
which is defined in the C and POSIX standards?
one of the standards:
The strerror() function need not be thread-safe.
which is a bit of a non-starter. One might hope that standard libraries use thread-local storage to implement it, but there is no guarantee.
This limitation can be overcome by using
instead. But the problem is that there is not one, but two functions of the same name
with incompatible API, XSI-compliant:
and GNU-specific (thanks, wildebeest):
No problems, the correct variant of
strerror_r can be detected with a few lines
of CMake code. And this is a fine solution if you are
writing an application and have control over your build system. But if you are
writing a library distrubuted in source form that is supposed to be used with any
build system, you can’t rely on CMake.
A common solution to such problems is using macros. This is also the only solution
if you are using C and, if you are interfacing to C from another language via some
kind of an FFI, you are
totally out of luck.
The man page of
this beatiful condition that you can check to see if XSI-compliant version is provided:
The only problem is that this only works with glibc and on some platforms, so
you keep getting errors like this
and keep adding more checks to the
Fortunately, there is a better way. Instead of using macros, you can rely
on function overloading to detect if
strerror_r is available and,
whether it is XSI-compliant or GNU-specific.
So here’s the code that illustrates and tests the idea:
Instead of using system functions, I just created prototypes in my code enabled with macros
to simplify testing. The code is pretty straightforward, it simply calls
passes its result to the
check function which prints what version of
The only tricky part here is to provide our own overload of
that is used if the system doesn’t provide this function, and to make sure
that it doesn’t cause ambiguity. This is achieved by using varargs.
Let’s see how it works:
As expected, all cases are detected correctly without any use of preprocessor (other than for testing purposes).
This will be integrated in the C++ Format library very soon. If you are interested in a more high-level way to report system errors, check out my Reporting system errors in C++ made easy post.