从OSX平台转换并使用gcc和llvm时生成失败



我继承了一个用C和C++编写的相当大且复杂的软件包,该软件包使用gcc:在Mac OSX Lion上成功构建

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

我的任务是使用命令行工具附带的默认Mac编译器在OSX Mavericks上构建相同的包。

$> 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

OSX Lion构建完成,但OSX Mavericks构建失败,出现以下错误:

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

我已经追踪到错误的来源是一个头文件和以下代码块:

#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 elseinline语句来修改上面的代码块,使其看起来如下所示,那么构建将在两个平台上完成:

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);

因此,在OSX Lion上,ifdef __cplusplus语句似乎会被触发,从而产生所需的行为。在小牛队,else语句被触发,最终导致错误。

如果这是一个非常基本的问题,请原谅,但C和C++超出了我的专业领域。这是怎么回事?#ifdef __cplusplus是什么意思?为什么一个版本的gcc会被它触发,而另一个版本则不会?

在编译器正确介入之前,预处理器会执行许多任务。以散列('#')开头的行是预处理器指令,包括文件包含(#include <stdio.h>)、宏变量定义(#define DEBUG 1)、宏函数定义(#define LENGTH(array) (sizeof(array) / sizeof(0[array])))和条件编译(#if ... #endif)等任务,如代码片段所示。

宏允许在编译时通过将宏名称替换为其值来重写源代码。它们是实现许多任务的早期方法,但总体上不尊重C/C++语法,因此在许多方面被(例如)inline说明符、模板、特征类和nullptr所取代,后者更好地尊重语法&语义。

有些宏是在标头中定义的(有关某些示例,请参阅"limites.h",在C++中用numeric_limits替换)。其他由预处理器定义;__cplusplus就是其中之一,它应该在编译器处理C++文件时定义(由文件扩展名或可能的命令行参数决定,如GCC的"-x")。您可以通过使用"-dD"或"-dM"选项(如gcc手册页和在线文档中所述)以及预处理后停止的"-E"选项来指示gcc列出宏。Nadeau软件咨询公司发布了一条关于如何列出各种编译器的预定义宏的提示。根据它的说法,clang应该接受与gcc相同的论点,所以在小牛队上尝试以下内容:

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

__cplusplus是C++标准所要求的(C++11中的§16.8 1)。狮子队和小牛队建造的gcc之间的一大区别是后者使用了clang前端,而前者使用了gcc。在网上搜索"clang __cplusplus"表明clang应该支持它,所以如果没有定义__cplusplus,那么clang很可能会将文件编译为C。你可以尝试"-x C++"选项来强制C++。您也可以尝试"-std=gnu++11"选项,尽管我怀疑它在这里不会有什么不同,因为它是为支持非标准GnuC++扩展而设计的,而不是提供完全的GCC兼容性。

最新更新