6

I'm experiencing an extremely weird problem in a fresh OSX 10.4.11 + Xcode 2.5 installation. I've reduced it to a minimal test case. Here's test.cpp:

#include "macros.h"

int main (void)
{
    return 1;
}

And here's macros.h:

#ifndef __JUST_TESTING__
#define __JUST_TESTING__

template<typename T> void swap (T& pT1, T& pT2)
{
    T pTmp = pT1;
    pT1 = pT2;
    pT2 = pTmp;
}

#endif //__JUST_TESTING__

This compiles and works just fine if both files are in the same directory. HOWEVER, if I put macros.h in /usr/include/gfc2 (it's part of a custom library I use) and change the #include in test.cpp, compilation fails with this error :

/usr/include/gfc2/macros.h:4: error: template with C linkage

I researched that error and most of the comments point to a "dangling extern C", which doesn't seem to be the case at all.

I'm at a complete loss here. Is g++ for some reason assuming everything in /usr/include/gfc2 is C even though it's included from a .cpp file that doesn't say extern "C" anywhere?

Any ideas?

EDIT : It does compile if I use the full path in the #include, ie #include "/usr/include/gfc2/macros.h"

EDIT2 : It's not including the wrong header. I've verified this using cpp, g++ -E, and renaming macros.h to foobarmacros.h

George Stocker
  • 57,289
  • 29
  • 176
  • 237
ggambetta
  • 3,312
  • 6
  • 32
  • 42
  • This is a total, hopeless, uninformed million-to-one-shot-in-the-dark, but try removing the "c" from the directory name. :) – datageist Feb 24 '10 at 17:36
  • Try posting the results of "set | grep -i include" here. – Arthur Kalliokoski Feb 24 '10 at 17:40
  • @datageist - Thanks, but it didn't make a difference. Oddly enough, it does compile if I #include it using its full path. Hmmm... – ggambetta Feb 24 '10 at 17:44
  • 3
    If it works when you use the full path, that might point to it picking up some other "macros.h" in your include path [which actually is a c header]. – datageist Feb 24 '10 at 17:54
  • You might try truss'ing your compile (for open) to watch exactly which macros.h it's compiling. There's probably another one it's picking up. Edit: Also there may be ANOTHER header that's including macros.h and when it's in the library directory your version is picked up instead of the intended one. Try renaming it to like my_macros.h and see if that helps. – Mark B Feb 24 '10 at 18:06
  • As a side note, you should avoid using a double underscore for your identifier. Read more about it in [this questions](http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier/228797#228797) – Francesco Feb 24 '10 at 18:09
  • create a minimal test case, then you should basically do a compile with -E to get the preprocessed output and see what is including what from where. – Evan Teran Feb 24 '10 at 18:09

6 Answers6

7

G++ may well indeed be assuming that everything in /usr/include is C. Try compiling your code with -E and studying the line markers in the preprocessor output:

g++ -E test.cpp | grep '^#'

You'll likely see things like

# 1 "/usr/include/gfc2/macros.h" 1 3 4

The 4 is the preprocessor hinting to G++ that it should wrap everything in extern "C", on the supposition that your platform's ancient header files in /usr/include predate C++. See Preprocessor Output in the CPP manual.

These days G++ mostly ignores this hint, because most platforms' C headers are no longer ancient. See the NO_IMPLICIT_EXTERN_C target macro in the GCC Internals manual. But it may be that this old version of Xcode has GCC configured without NO_IMPLICIT_EXTERN_C and thus is listening to the preprocessor's hint. (This is set when GCC itself is built -- I don't think there's a command-line switch to override it.)

You may be able to work around this by wrapping the contents of your header file in extern "C++".

John Marshall
  • 6,815
  • 1
  • 28
  • 38
  • Coincidentally I had just found this thread, which says essentially the same you say : http://www.pixelglow.com/lists/archive/macstl-dev/2005-February/000015.html So it looks like it's a compiler "feature" after all. It's a relief, I though I was going insane! – ggambetta Feb 24 '10 at 18:39
  • See also http://www.cocoabuilder.com/archive/xcode/247374-headers-in-usr-include.html#247468 – ggambetta Feb 24 '10 at 18:41
  • That http://www.cocoabuilder.com/archive/xcode/247374-headers-in-usr-include.html link is interesting: he has had success with `extern "C++"` (and it is indeed standard: see 7.5/3); and when he commented out all `# XX` references, what he was of course really doing was erasing all the preprocessor's `4` hints. This actually is a useful *feature* when porting GCC to a clueless vendor's platform with broken C-only headers. Yes, I speak from bitter experience! However the real answer is to fix the system headers and switch on `NO_IMPLICIT_EXTERN_C`, as Apple appears to have since done. – John Marshall Feb 24 '10 at 19:15
  • I guess you never stop learning obscure facts... :) Thank you so much, you made my day! – ggambetta Feb 24 '10 at 19:21
1

This is a shot in the dark, but is there another file named macros.h somewhere under /usr/include or in your GCC installation? GCC has a facility for wrapping headers, called #include_next, which might be the cause of your problem.

One thing you can do to disambiguate your macros.h from any other macros.h in the include path is to include it as gfc2/macros.h. This way, the compiler will search every directory in the include path for a subdirectory named gfc2 containing a file named macros.h, reducing the chance of a collision. It also prevents you from having to add /usr/include/gfc2 to the include path.

BTW, #include "file.h" searches the current directory first. To skip that and go straight to the include path, use #include <file.h>:

#include <stdio.h>
#include <gfc2/macros.h>

Another approach is to choose a filename that is more likely to be unique, like gfc2macros.h.

bk1e
  • 23,871
  • 6
  • 54
  • 65
  • I thought about that, too, but the output of cpp confirms g++ is pulling the correct macros.h (besides the fact that the error message contains the full path to my file). There are no other macros.h in /usr/include. – ggambetta Feb 24 '10 at 18:06
  • Did you run `cpp` or did you run `g++ -E`? g++ might add directories to the default include path, so the result might not be the same. – bk1e Feb 24 '10 at 18:11
0

Well, it really looks weird...

How does XCode calls g++? I don't think g++ spontaneously decides that an include file has C linkage just because it's in a different directory. Did you try to compile your project by hand? Try "g++ main.cpp -I/usr/include/gfc2/". If this solves your problem than it's not g++. Maybe does XCode precompile headers?

neverlord
  • 890
  • 6
  • 12
0

Have you tried not changing the test.cpp file at all, but instead when you compile also say:

-I/usr/include/gfc2/
Alexandros Gezerlis
  • 2,221
  • 16
  • 21
  • Thanks! That works, but I'd put it in the "workarounds" category. I rather fix the root cause, since this exact code has been working forever in Linux, Mac and Windows, so I'm pretty sure it's a problem with this particular installation. – ggambetta Feb 24 '10 at 18:03
  • (For completeness,) The reason why this works is that the header is now coming from the ordinary directory named in this option, rather than from the special built-in `/usr/include`. Because the header is not from a special directory, the preprocessor doesn't put `4` flags on its line markers, so G++ is not tempted to wrap it in `extern "C"`. (See my answer above for an explanation of these flags.) – John Marshall Apr 06 '10 at 22:17
0

You can see where g++ is looking for includes with the verbose flag:

g++ -v -o test test.cpp

And this will just run the preprocessor and show what is actually included in the file and compiled:

g++ -E test.cpp | less

If the wrong files are getting included (or your header is getting wrapped in another, as bk1e suggests) you'll be able to find out with that output.

remcycles
  • 1,246
  • 13
  • 14
0

I just ran into this issue as well when compiling a C++ project that we normally build on 10.5 and 10.6 (Xcode 3.0+) on a 10.4 PPC machine with Xcode 2.5 installed. It looks as if the preprocessor treats anything added to the gcc include path with '-isystem' as if it should be "extern C". Changing '-isystem' to '-I' resolved the issue.

Grant Limberg
  • 20,913
  • 11
  • 63
  • 84