cprintfgnuglibcportability

How to use register_printf_specifier in a portable way?


I'm doing a collections library in C in which I implement several data structures that I want to be able to display directly with printf (for example I have a String struct that I want to be able to display with %S), and I saw that the register_printf_specifier function defined in the printf.h header just does that, the only problem is that register_printf_specifier and the printf.h header are a non-standard gnu extension, and I want everyone to be able to compile my library with the compiler that they want so I need a way to use register_printf_specifier only if it's available. Since it's a GNU extension is it enough to wrap the piece of code in a #ifdef __GNUC__ ? Or do I have to check for a specific extension with __has_extension ? The GNU official documentation just says that it is a GNU extension but doesn't say anything about how it should be guarded in ISO C compliant code.


Solution

  • How to use register_printf_specifier in a portable way?

    There is no portable use of a function of that name, because it is a GNU extension, not a standard function.

    I need a way to use register_printf_specifier only if it's available.

    No you, don't. If you want to provide a simple mechanism for printing your custom structure on systems that do not provide register_printf_specifier, then you need to provide an alternative that works on such systems. And if you have such an alternative then you don't need to use register_printf_specifier even where it is available.

    But if you want to do that anyway, perhaps to provide an additional feature for people who happen to be using an implementation that does provide register_printf_specifier, then ...

    Since it's a GNU extension is it enough to wrap the piece of code in a #ifdef __GNUC__?

    No. The __GNUC__ macro tells you about whether you are using the GNU compiler, and which version. What you need to know is whether you are using the GNU C library, and if so, whether it's a version that includes register_printf_specifier() (which is extremely new). GCC is most commonly built to use Glibc, but it doesn't have to be, and even if a particular one is built that way, that's not enough to ensure that register_printf_specifier is available.

    Or do I have to check for a specific extension with __has_extension?

    That would be neither effective nor appropriate. __has_extension is itself an extension. It originated in Clang, and some versions of GCC support it for compatibility, but the manual recommends against using it in new code. Even if you wanted to ignore the manual about that,

    1. although you can test for __has_extension itself, you need an alternative that works when it is not available. Neither absence nor presence of _has_extension itself speaks to the availability of register_printf_specifier().

    2. __has_extension tests against a controlled vocabulary of feature and extension names defined by Clang. It does not test for the availability of arbitrary functions. There does not appear to be defined extension name covering register_printf_specifier, and I wouldn't expect one because this mechanism is about compiler / language extensions, not library extensions.

    The GNU official documentation just says that it is a GNU extension but doesn't say anything about how it should be guarded in ISO C compliant code.

    You need to know about whether you're using Glibc, and if so, which version. Having included one of the standard library headers, such as stdio.h, you can test this by examining the macros __GLIBC__ and __GLIBC_MINOR__. The test would look something like this:

    // SEE NOTE BELOW
    #if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 42
        // use register_printf_specifier() ...
    #else
        // use something else ...
    #endif
    

    It is important to note, however, that not even the current release of Glibc as of this time (v2.41) documents register_printf_specifier(). The (oldish) documentation linked in the question refers to a different, albeit related, extension function, register_printf_function(), which will be deprecated in whatever future release of Glibc officially introduces register_printf_specifier().

    OVERALL

    When your objective is to write portable code, the best strategy by far is to avoid language and standard library extensions altogether. Generally speaking, portability is improved by also programming to the oldest language and standard library specifications you're willing to support, though in this you do have to be careful about backward incompatibilities introduced by later versions (a few exist, of varying significance). This may impact how you design your software.

    Among the best cases for extension usage are

    Neither of those applies to the particular situation you describe.