0

I have inherited a fairly large and complex software package written in both C and C++ which successfully builds on Mac OSX Lion using gcc:

$> gcc --version
i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)

I have been tasked with getting this same package to build on OSX Mavericks using the default Mac compiler that comes with Command Line Tools.

$> gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.1.0
Thread model: posix

The OSX Lion build finishes to completion but the OSX Mavericks build fails with the following error:

Undefined symbols for architecture x86_64:
"_MRIsetVoxVal", referenced from ...

I have traced source of the error to a header file with the following code block:

#ifdef __cplusplus
float  MRIgetVoxVal( const MRI *mri, int c, int r, int s, int f);
int    MRIsetVoxVal(MRI *mri, int c, int r, int s, int f, float voxval);
void   MRIdbl2ptr(double v, void *pmric, int mritype);
double MRIptr2dbl(void *pmric, int mritype);
#else
inline float  MRIgetVoxVal(const MRI *mri, int c, int r, int s, int f);
inline int    MRIsetVoxVal(MRI *mri, int c, int r, int s, int f, float voxval);
inline void   MRIdbl2ptr(double v, void *pmric, int mritype);
inline double MRIptr2dbl(void *pmric, int mritype);
#endif

If I modify the above code block by simply removing the if else and inline statements so that it looks like the following, then the build finishes to completion on both platforms:

float  MRIgetVoxVal( const MRI *mri, int c, int r, int s, int f);
int    MRIsetVoxVal(MRI *mri, int c, int r, int s, int f, float voxval);
void   MRIdbl2ptr(double v, void *pmric, int mritype);
double MRIptr2dbl(void *pmric, int mritype);

So it appears that on OSX Lion, the ifdef __cplusplus statement is triggered which produces the desired behavior. And on Mavericks the else statement is triggered which ultimately results in a error.

Pardon me if this is a very basic question, but C and C++ are outside my area of expertise. What is going on here? What does #ifdef __cplusplus even mean, and why does one version of gcc get triggered by it and the other does not?

Z K
  • 71
  • 6

1 Answers1

1

Before the compiler proper steps in, the preprocessor performs a number of tasks. Lines beginning with a hash ('#') are preprocessor directives, and include tasks such as file inclusion (#include <stdio.h>), macro variable definition (#define DEBUG 1), macro function definition (#define LENGTH(array) (sizeof(array) / sizeof(0[array]))), and conditional compilation (#if ... #endif), as seen in your code fragment.

Macros allow source code to be rewritten at compile time by replacing the macro name with its value. They're an early way of achieving a number of tasks, but don't by and large respect C/C++ syntax and have thus been replaced in a number of ways by (e.g.) the inline specifier, templates, trait classes and nullptr, which better respect syntax & semantics.

Some macros are defined in headers (see "limits.h", replaced in C++ by numeric_limits, for some examples). Others are defined by the preprocessor; __cplusplus is one of these, which should be defined when the compiler is processing a C++ file (as determined by the file extension or possibly command-line arguments, such as GCC's '-x'). You can direct gcc to list macros by using the '-dD' or '-dM' options (as mentioned in the gcc man page and online docs), along with the '-E' option to stop after preprocessing. Nadeau Software Consulting posted a tip on how to list predefined macros for various compilers. According to it, clang should accept the same arguments as gcc, so try the following on Mavericks:

gcc -dM -E -x c++ /dev/null | less

__cplusplus is required by the C++ standards (ยง 16.8 1 in C++11). One big difference between the Lion and Mavericks builds of gcc is the latter uses the clang frontend while the former uses GCC. A web search for "clang __cplusplus" suggests that clang should support it, so if __cplusplus isn't being defined, then clang is likely compiling the file as C. You can try the '-x c++' option to force C++. You could also try the '-std=gnu++11' option, though I suspect it won't make a difference here, as it's designed to support non-standard Gnu C++ extensions rather than providing full GCC compatibility.

Community
  • 1
  • 1
outis
  • 75,655
  • 22
  • 151
  • 221