tags:

views:

283

answers:

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

A: 

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
I am in fact using the command line compiler, more precisely g++ -o test test.cpp
ggambett
Did you try to rename "macros.h" to "macros.hpp"?
neverlord
@neverlord - yep. Same error :(
ggambett
A: 

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

-I/usr/include/gfc2/
Alexandros Gezerlis
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.
ggambett
(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
+1  A: 

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
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.
ggambett
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
A: 

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.

remicles2
I didn't know -E. In this case the output of g++ -E is the same as cpp.
ggambett
+6  A: 

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
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!
ggambett
See also http://www.cocoabuilder.com/archive/xcode/247374-headers-in-usr-include.html#247468
ggambett
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
I guess you never stop learning obscure facts... :) Thank you so much, you made my day!
ggambett
A: 

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