【问题标题】:Linking C from C++ in OS X Mavericks在 OS X Mavericks 中从 C++ 链接 C
【发布时间】:2013-10-28 20:49:06
【问题描述】:

在转换到 OS X Mavericks 和 XCode 5.0.1 后,我不能再优雅地将已编译的 C 文件(从 gcc 输出)链接到 C++ 项目(从 g++ 输出)。

我的 makefile 产生的有问题的命令对是:

gcc `pkg-config --cflags glib-2.0` -g -Wall -O3 `pkg-config --cflags flann`   -c -o vec.o vec.c
g++ `pkg-config --cflags glib-2.0` -g -Wall -O3   -stdlib=libstdc++ -lstdc++  layoutquality.cpp vec.o  `pkg-config --libs glib-2.0`  -L/usr/local/Cellar/flann/1.8.4/lib -lflann -o layoutquality

链接器抱怨的对象:

架构 x86_64 的未定义符号: “load_dmat(char const*)”,引用自: _main in layoutquality-I8HOqy.o ld:未找到架构 x86_64 的符号

load_dmat 只是文件 vec.c 中的一个函数。如果我在第一行将gcc 替换为g++,那么一切都可以编译和链接,但是clang 说:

clang:警告:在 C++ 模式下将“c”输入视为“c++”,此行为已弃用

有没有一种无害的、不被弃用的编译和链接方式?在我升级到 OS X Mavericks 和新的命令行工具之前,将g++ 与来自gcc 的目标文件链接在一起工作得很好。任何关于发生了什么变化以及如何前进的见解都会很棒,谢谢。

【问题讨论】:

  • 在编译成 C++ 模块的load_dmat 的声明中是否使用extern "C"?如果不是,C++ 编译器会期望一个错误的名称。
  • 不,我不知道,它应该去哪里?在标头 vec.h 或源 vec​​.c 中?不过,我对编辑 vec.* 犹豫不决,因为它已被其他 C 项目使用。
  • 在标题中,附上您的声明,如:extern "C" { void load_dmat(char const*); }。使用#ifdef __cplusplus(如下面的答案)保持 C 项目不受影响。
  • 您可能希望将“gcc”替换为“clang”,将“g++”替换为“clang++”,以养成不使用已弃用的符号链接到 clang 的习惯。
  • 不要认为这是名称篡改。查看我对@Inspired 的回复

标签: c++ c gcc clang osx-mavericks


【解决方案1】:

在“vec.c”之前添加一个“-x c”(不带引号)应该可以解决它。

如果您在同一行中编译多个 .c/.cpp 文件,您可以在每个 C 或 C++ 文件名列表之前使用“-x c”或“-x c++”来适当地切换上下文。例如:

g++ -x c  alpha.c beta.c  -x c++  gamma.cpp

【讨论】:

  • 感谢 -x c 和 -x c++ 提示,非常有帮助。但我需要提供 -std=c++11 才能将 C++11 与我的 .cpp 文件一起使用。我现在收到error: invalid argument '-std=c++11' not allowed with 'C/ObjC'。有没有办法用 c++ 和 c 文件进行单行构建,指定 c++11 标准?
  • 不,我不知道有什么方法可以使用 C 文件和 C++ 11 文件进行单行 g++ 构建,尽管编译成中间 .o 文件然后链接它们应该可以工作。
【解决方案2】:

这是一个示例Makefile,让我们可以在C 程序中使用C++ 代码/函数。

CC=clang
CXX=clang++
CFLAGS=-Wall -g
CXXFLAGS=-Wall -g -std=c++11 -I.

DEPS=CPP.h
OBJ=main.o CPP.o

RM=rm -f

# compile only, C source
%.o: %.c
    $(CC) -c -o $@ $< $(CFLAGS)

# compile only, C++ source
%.o: %.cpp $(DEPS)
    $(CXX) -c -o $@ $< $(CXXFLAGS)

# link
main: $(OBJ)
    $(CXX) -o $@ $^ $(CXXFLAGS)

clean:
    $(RM) $(OBJ)

如您所见,我们在对编译器的两次单独调用中分别使用CXXFLAGSCFLAGS 生成我们的对象。在 Mac 使用 Xcode 的 clang 的上下文中,clang (CC) 和 clang++ (CXX) 实际上是一回事。只有不同的标志很重要。我只是在上面的例子Makefile 中说明CCCXX 的定义是迂腐的。

生成目标文件后,我们可以将它们链接在一起。

但是请注意,您必须额外执行一个步骤才能使您的 C++ 代码可供 C 程序使用。

在此示例中的CPP.h 中,您必须显式使用extern "C" 来为C++ 代码指定链接以供C 使用。

例如,像这样:

#ifdef __cplusplus
extern "C" {
#endif

double timesTwo(double number);

#ifdef __cplusplus
}
#endif

预处理器宏#ifdef __cplusplus#endif 是为了确保我们的头文件不会导致C 模式编译错误,并且仅在C++ 模式编译期间有效。

这个完整的例子只包含 4 个文件。

  • 生成文件
  • main.c
  • CPP.h
  • CPP.cpp

Makefile 源和CPP.h 已在上面解释。

为了全面了解,我在这里也包括main.cCPP.cpp

main.c:

#include <stdio.h>
#include "CPP.h"

int main()
{
    printf("Running main.c\n");
    double ans = timesTwo(3.0);
    printf("The answer from our C++ timesTwo function, when given 3.0, is %f\n", ans);

    return 0;
}

CPP.cpp:

#include "CPP.h"

double timesTwo(double number)
{
    return 2 * number;
}

我相信这个解释和示例阐明了我们如何设置 Makefile、指定 #ifdef __cplusplus 预处理器宏和 extern "C" 链接声明以允许 C++ 到 C 互操作,并且在我们运行 make。

【讨论】:

    【解决方案3】:

    您很可能是Name mangling 的受害者。为避免 C++ 中的名称混淆,请在声明周围使用 extern "C",例如:

    #ifdef __cplusplus 
    extern "C" {
    #endif
        void load_dmat(char const*);
    #ifdef __cplusplus
    }
    #endif
    

    【讨论】:

    • 如果这能解决问题,那么我很好奇源代码之前是如何构建和链接的。
    • 感谢指向名称修改的指针,但不幸的是,这并不能解决问题。我将编辑添加到我的 vec.h 并且链接器错误仍然存​​在。
    猜你喜欢
    • 2014-04-12
    • 1970-01-01
    • 2021-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多