36

I am struggling with what appears to be an ambiguity in c++11 symbol resolution due to the GNU standard library implementation in this environment:

  • Arch Linux 4.2.5-1 (x86_64)
  • g++ 5.2.0
  • clang++ 3.7.0

Example:

#include <iostream>
#include <string>

struct version {

  unsigned major;
  unsigned minor;
  unsigned patch;

  version(unsigned major, unsigned minor, unsigned patch) :
    major(major), minor(minor), patch(patch) { }

  friend std::ostream & operator<<(std::ostream & out, version const& v) {
    out << v.major << ".";
    out << v.minor << ".";
    out << v.patch;
    return out;
  }

};

int main(int argc, char ** argv) {
  version v(1, 1, 0);
  std::cout << v << std::endl;
  return 0;
}

Compiler error:

error: member initializer 'gnu_dev_major' does not name a non-static data
  member or base class
error: member initializer 'gnu_dev_minor' does not name a non-static data
  member or base class

Command:

clang++ -std=c++11 -o test *.cpp

The scope resolution operator does not appear to be applicable in member initialization lists so I can't figure out how to resolve the ambiguity. This sample compiles fine without the c++11 flag.

Chris Hutchinson
  • 9,082
  • 3
  • 27
  • 33
  • It also compiles fine with MSVC 2015 – Chris Hutchinson Nov 12 '15 at 20:53
  • 4
    The same problem found here [major and minor macros defined in sys/sysmacros.h pulled in by ](http://stackoverflow.com/questions/22240973/major-and-minor-macros-defined-in-sys-sysmacros-h-pulled-in-by-iterator) – Bo Persson Nov 12 '15 at 21:05
  • @Myria they are macros defined by gcc – bolov Nov 12 '15 at 21:24
  • 2
    **great** Now I have to add gnu to the list of compilers that add stupid stupid macro definitions. First being of course Visual Studio with `min` and `max`, and everything like `#define CreateFile CreateFileW` which is pretty much everything. – bolov Nov 12 '15 at 21:26
  • 1
    Filed a glibc bug report, it is linked in my answer below. – Shafik Yaghmour Nov 13 '15 at 05:14

4 Answers4

40

Another way is to use braces:

version(unsigned major, unsigned minor, unsigned patch) :
  major{major}, minor{minor}, patch{patch} { }

Then the macros will not interfere because they are function-like macros and need parentheses to be invoked.

user703016
  • 37,307
  • 8
  • 87
  • 112
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • great answer! better solution than undefining macros – Chris Hutchinson Nov 12 '15 at 21:05
  • 7
    @ChrisHutchinson *"better solution than undefining"* I don't know about that; They should not be there in the first place as they are not implementation reserved identifiers. I like `#undef`ing them. (Upvoted this too, as it is a valid and clean solution for people who want to use those macros.) – Baum mit Augen Nov 12 '15 at 21:14
  • Interesting--that's an [unexpected benefit of brace-initialization.](http://stackoverflow.com/q/33444814/1858225) – Kyle Strand Nov 13 '15 at 00:18
  • 2
    @BaummitAugen `#undef`ing them means now you're specifically designing your code around non-standard definitions that "should not be there"; that's about as inelegant as you can get. – Alex Celeste Nov 13 '15 at 02:23
  • 1
    @Leushenko it's my experience, and the stories I've heard confirm it, that if you want your code to be portable in practice, you need to work around the odd little differences in various implementations, and that's simply never going to be elegant. These `#undef`s should be in a header used throughout your project for portability. Note, too, that this technique allows *initializing* these members, but it doesn't really probably any protection against macro substitution in other syntactic contexts. – Kyle Strand Nov 15 '15 at 16:47
19

From my own compilation attempt, it looks like glibc is doing something stupid and #defineing common lowercase words.

When I add the following after the #includes, it compiles.

#undef major
#undef minor
Myria
  • 3,372
  • 1
  • 24
  • 42
15

It looks like major and minor are macros defined in sys/sysmacros.h which is being brought in by <iostream>, which is problematic since these names are not reserved for the implementation. Note if the code is reduced and we solely include sys/sysmacros.h directly the same problem will occur.

When I compile this with clang on Wandbox I get this more informative error which allows us to see where the conflict is coming from:

 usr/include/x86_64-linux-gnu/sys/sysmacros.h:67:21: note: expanded from macro 'major'
 # define major(dev) gnu_dev_major (dev)
                     ^~~~~~~~~~~~~~~~~~~

 usr/include/x86_64-linux-gnu/sys/sysmacros.h:68:21: note: expanded from macro 'minor'
 # define minor(dev) gnu_dev_minor (dev)
                     ^~~~~~~~~~~~~~~~~~~

We can find this documented in the following bug report identifier major macro expanded into gnu_dev_major:

The problem is that g++ adds -D_GNU_SOURCE and major() is a macro in _GNU_SOURCE (or _BSD_SOURCE or when no feature set is requested).

You can always #undef major after including headers.

and:

makedev(), major() and minor() should be functions, not macros.

It looks they were introduced in GNUC >= 2 in sys/sysmacros.h for backward compatibility. It is not a good idea.

Fiddling with macros is dangerous since it pollutes user name space.

and so we can see undef the macros is one solution and it is unfortunately the recommended solution since this is a won't fix bug:

There will be no change. If some code does not like the macros, add #undefs after the appropriate #include. The macros are part of the API and removing them only causes problems.

We could use {} in the member intializer but this requires changes the types of the parameters since leaving them as int would be a narrowing conversion which is not allowed. Of course changing names is also a possible solution.

Update

I filed a glibc bug report. There is some questions about whether this is really a gcc or a glibc bug, the details are rather involved.

Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • If it doesn't already exist I would file a new bug report against glibc. This is a result of nasty interaction of multiple header files: `_GNU_SOURCE` causes `stdlib.h` to include `sys/types.h`, and it causes `sys/types.h` to include `sys/sysmacros.h`. The cascading of these two behaviors is completely unintentional and unwanted. – R.. GitHub STOP HELPING ICE Nov 12 '15 at 22:12
  • 3
    I concur with R..: file a new bug report (in the upstream bugzilla, not Red Hat's: https://sourceware.org/bugzilla/) The glibc maintainers' attitudes toward this sort of thing have changed considerably since 2004. – zwol Nov 12 '15 at 22:43
  • @R.. filed a bug report and it is linked in my answer – Shafik Yaghmour Nov 13 '15 at 05:13
  • I'm pleased to report that glibc has decided to remove these macros from `sys/types.h` in a future release. There's no exact schedule yet, though. – zwol Aug 03 '16 at 20:21
9

/usr/include/sys/sysmacros.h has following macros defined:

# define major(dev) gnu_dev_major (dev)
# define minor(dev) gnu_dev_minor (dev)
# define makedev(maj, min) gnu_dev_makedev (maj, min)

looks like you have to undef them or use other names

Slava
  • 43,454
  • 1
  • 47
  • 90