【问题标题】:Build failure when transitioning from OSX platforms and using gcc and llvm从 OSX 平台转换并使用 gcc 和 llvm 时构建失败
【发布时间】:2015-02-13 20:47:16
【问题描述】:

我继承了一个用 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 语句似乎被触发,从而产生了所需的行为。在 Mavericks 上,else 语句被触发,最终导致错误。

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

【问题讨论】:

    标签: c++ c macos gcc


    【解决方案1】:

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

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

    一些宏是在头文件中定义的(参见“limits.h”,在 C++ 中被 numeric_limits 替换,一些例子)。其他由预处理器定义; __cplusplus 是其中之一,应该在编译器处理 C++ 文件时定义(由文件扩展名或可能的命令行参数决定,例如 GCC 的“-x”)。您可以使用'-dD' 或'-dM' 选项(如gcc man 页面和online docs 中所述)指示gcc 列出宏,以及在预处理后停止的'-E' 选项。 Nadeau Software Consulting 发布了关于如何为各种编译器列出 predefined macros 的提示。根据它,clang 应该接受与 gcc 相同的参数,所以在 Mavericks 上尝试以下操作:

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

    __cplusplus 是 C++ 标准所要求的(C++11 中的第 16.8 节 1)。 Lion 和 Mavericks 构建的 gcc 之间的一大区别是后者使用 clang 前端,而前者使用 GCC。网络搜索“clang __cplusplus”表明clang 应该支持它,所以如果__cplusplus 没有被定义,那么clang 可能会将文件编译为C。您可以尝试使用'-x c++' 选项来强制使用C++。您也可以尝试 '-std=gnu++11' 选项,但我怀疑它在这里不会有什么不同,因为它旨在支持非标准 Gnu C++ extensions,而不是提供完整的 GCC 兼容性。

    【讨论】:

      猜你喜欢
      • 2021-11-14
      • 1970-01-01
      • 1970-01-01
      • 2015-05-29
      • 2023-02-11
      • 1970-01-01
      • 2011-10-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多