4
votes

I was investigating porting some code from older C++ to a more modern C++ version (e.g. C++ 11 or higher). And then I noticed some networking code not compiling when I added the compiler flag -std=c++11

Simplest MVCE example. I'm on the latest Cygwin (literally installed today). g++ --version indicates version 7.3.0.

Take the following source file stripped down to almost nothing, but enough to show the issue I am about to explain.

#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>

int some_networking_code()
{
   addrinfo* addr = NULL;
   int flags = AI_NUMERICHOST;
   return 0;
}

On a fresh install of Cygwin, the following command works just fine:

g++ foo.cpp -c

But now switch to compiling with C++11.

g++ foo.cpp -c -std=c++11

And the resulting compiler output is:

foo.cpp: In function ‘int some_networking_code()’:
foo.cpp:8:4: error: ‘addrinfo’ was not declared in this scope
    addrinfo* addr = NULL;
    ^~~~~~~~
foo.cpp:8:4: note: suggested alternative: ‘addr_t’
    addrinfo* addr = NULL;
    ^~~~~~~~
    addr_t
foo.cpp:8:14: error: ‘addr’ was not declared in this scope
    addrinfo* addr = NULL;
              ^~~~
foo.cpp:8:14: note: suggested alternative: ‘addr_t’
    addrinfo* addr = NULL;
              ^~~~
              addr_t
foo.cpp:9:16: error: ‘AI_NUMERICHOST’ was not declared in this scope
    int flags = AI_NUMERICHOST;
                ^~~~~~~~~~~~~~

Debugging this, I take a peek at /usr/include/netdb.h and I see that the definition for addrinfo expects a macro called __POSIX_VISIBLE to be greater than or equal to 200112. There definition for AI_NUMERICONLY is wrapped within a similar block.

#if __POSIX_VISIBLE >= 200112 && !defined(__INSIDE_CYGWIN_NET__)
struct addrinfo {
  int             ai_flags;             /* input flags */
  … <deleted for brevity>
#endif

Using the -dM -E switches, we can ask the compiler for what macros it's pulling in and filter to the POSIX_VISIBLE

$ g++ foo.cpp -c -dM -E | grep POSIX_VIS
#define __POSIX_VISIBLE 200809

Ok, that looks correct for traditional C++. Let's inspect that for C++11 building:

$ g++ foo.cpp -c -std=c++11 -dM -E | grep POSIX_VIS
#define __POSIX_VISIBLE 0

__POSIX_VISIBLE is defined to be 0. And that explains why we have the errors above.

It appears that POSIX_VISIBLE is getting defined by the header file at /usr/include/sys/features.h based on other defined macros. But that's where I start to get confused.

Is this a bug in the Cygwin distro or its header files? The way the compiler was built? Or something else? What's the appropriate workaround that won't impact building on Linux, Mac, BSD, and everywhere else?

2
You should ask on the cygwin mailing list. In case of bug it should be correctmatzeri

2 Answers

2
votes

I had a discussion on the Cygwin mailing list. I had assumed that the absence of specifying -std on the command line would limit my build to not have access to modern C++ features. And that specifying -std=c++11 or higher would enable these newer features. That is actually incorrect. Newer versions of g++ default to something newer:

$ g++ -c foo.cpp -dM -E | grep cplus
#define __cplusplus 201402L

And using -std not only limits compiler feature behavior, but it disables the GNU built-in defines. And Cygwin headers will hide several POSIX functions when GNU_SOURCE is not defined, including getaddrinfo. I'm not sure if I agree with this configuration, but that's just how it works on Cygwin. Avoiding -std is a better fix.

Update

While avoiding the use of -std is a great fix, earlier versions of g++ prior to 6.0 default to C++ 98 unless -std is used. And there's still a lot of folks using pre 6.0 versions of g++. There were a lot of Makefile hacks I investigated to sniff for C++ 11 support or compiler version such that -std=c++11 is used conditionally, none of which are pretty. The workaround that seems to be universal to g++, Cygwin, and Clang is to use -std=gnu++11.

That is:

g++ foo.cpp -c -std=gnu++11
1
votes

Judging by https://cygwin.com/cygwin-ug-net/ov-new.html, cygwin seems to aim at feature test macro compatibility with glibc. struct addrinfo is used by getaddrinfo, which on glibc requires _POSIX_C_SOURCE >= 200112L.

Indeed, the code compiles if you define the corresponding macro:

g++ -D_POSIX_C_SOURCE=200112L foo.cpp -c -std=c++11

or, alternatively, define the give-me-everything _GNU_SOURCE.

g++ -D_GNU_SOURCE foo.cpp -c -std=c++11