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