Your Location is: Home > Linux

How to force g++ to respect #define _POSIX_C_SOURCE 200809L

From: San View: 3032 Ingo 

Question

I need to use strerror_r to translate error numbers into human readable messages compiled with g++ on Linux Debian Bullseye. The man page notes:

int strerror_r(int errnum, char *buf, size_t buflen);
           /* XSI-compliant */

char *strerror_r(int errnum, char *buf, size_t buflen);
           /* GNU-specific */

strerror_r():
   The XSI-compliant version is provided if:
   (_POSIX_C_SOURCE >= 200112L) && !  _GNU_SOURCE
   Otherwise, the GNU-specific version is provided.

We have two different types of the return value: int or char* depending on the version defined by _POSIX_C_SOURCE. I have this small test program:

~$ cat strerror_r.c
#include <string.h>
#include <stdio.h>

// #define _POSIX_C_SOURCE 200112L
// #undef _GNU_SOURCE

#define ERROR_BUFFER_LEN (size_t)256

int main(int argc, char **argv)
{
#if _POSIX_C_SOURCE < 200112L
    char* ret;
#else
    int ret;
#endif
    char errorBuffer[ERROR_BUFFER_LEN];
    int errno;

    errno = 0;
    ret = strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
    fprintf(stderr, "Error message by pointer = '%s'\n", ret);
    fprintf(stderr, "Content of errorBuffer =  '%s'\n", errorBuffer);

    return 0;
}

If I compile it with gcc everything is as expected:

$ gcc strerror_r.c && ./a.out; rm a.out
Error message by pointer = '(null)'
Content of errorBuffer =  'Success'

If I compile it with g++ I get this:

$ g++ strerror_r.c && ./a.out; rm a.out
strerror_r.c: In function ‘int main(int, char**)’:
strerror_r.c:24:21: error: invalid conversion from ‘char*’ to ‘int’ [-fpermissive]
   24 |     ret = strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
      |           ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                     |
      |                     char*
rm: cannot remove 'a.out': No such file or directory

If I try to force the needed version by uncommenting

#define _POSIX_C_SOURCE 200112L
#undef _GNU_SOURCE

I get:

$ g++ strerror_r.c && ./a.out; rm a.out
strerror_r.c:7: warning: "_POSIX_C_SOURCE" redefined
    7 | #define _POSIX_C_SOURCE 200112L
      |
In file included from /usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33,
                 from /usr/include/string.h:26,
                 from strerror_r.c:3:
/usr/include/features.h:281: note: this is the location of the previous definition
  281 | # define _POSIX_C_SOURCE 200809L
      |
strerror_r.c: In function ‘int main(int, char**)’:
strerror_r.c:24:21: error: invalid conversion from ‘char*’ to ‘int’ [-fpermissive]
   24 |     ret = strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
      |           ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                     |
      |                     char*
rm: cannot remove 'a.out': No such file or directory

What I'm missing here? Why g++ does not compile the default thread save version of strerror_r? I need that version. How can I fix it?


Reference

Best answer

You need to specify the #define and #undef directives before you include any header files, so the first few lines should look like this:

#define _POSIX_C_SOURCE 200112L
#undef _GNU_SOURCE

#include <string.h>
#include <stdio.h>

That's because those header files or internal header files they include need those values defined to choose the proper variant. If you define them after including the headers, the headers don't see the right values and they don't include the version you want.

Often people specify these values on the command like with the -D and -U arguments so they are always specified before header files are included.