【问题标题】:Call a C function from C++ code从 C++ 代码调用 C 函数
【发布时间】:2013-05-26 21:41:07
【问题描述】:

我有一个想从 C++ 调用的 C 函数。我不能使用“extern "C" void foo()”这种方法,因为 C 函数无法使用 g++ 编译。但它使用 gcc 编译得很好。任何想法如何从 C++ 调用函数?

【问题讨论】:

  • 能否请您写一些示例代码和g++ 错误消息
  • 如果你用 C++ 编译器编译它,它就是 C++。 C 代码不必使用 C++ 编译器进行编译。它们是不同的语言。您的代码不是有效的 C++,因此不能使用 C++ 编译器进行编译。
  • @MatthieuRouget void valid_in_C_but_not_in_CPlusPlus(size_t size) { char variable_length_array[size]; }
  • 我的尝试:void f(void *pv) { int *pi = pv; *pi = 42; } ^^
  • 这应该保持打开状态,特别是因为它有很好的答案指出如何将 C(而不是 C++)编译器用于 C 代码。

标签: c++ c linux extern-c


【解决方案1】:

像这样编译 C 代码:

gcc -c -o somecode.o somecode.c

然后是这样的C++代码:

g++ -c -o othercode.o othercode.cpp

然后使用 C++ 链接器将它们链接在一起:

g++ -o yourprogram somecode.o othercode.o

当你包含 C 函数的声明时,你还必须告诉 C++ 编译器一个 C 头文件即将到来。所以othercode.cpp 开头是:

extern "C" {
#include "somecode.h"
}

somecode.h 应包含以下内容:

 #ifndef SOMECODE_H_
 #define SOMECODE_H_

 void foo();

 #endif


(我在这个例子中使用了gcc,但是任何编译器的原理都是一样的。分别编译为C和C++,然后将它们链接在一起。)

【讨论】:

  • @Arne 好点。有些人通过在标头中用#ifdef __cplusplus 包装extern "C" 来在他们的C 中使用一些C++。
  • @Arne 请参阅下面的答案。放松如你所见,我是其中之一 ;)
  • 非常感谢!这对我很有用:)
  • 我收到以下错误:错误:#337:链接规范与以前的“foo”不兼容(在第 1 行声明)现在可以编译了。谁能解释一下?
  • @FaizanRabbani,还有很多细节。
【解决方案2】:

让我从其他答案和 cmets 中收集点点滴滴,为您提供一个清晰分离的 C 和 C++ 代码示例:

C 部分:

foo.h

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"

void foo(void)
{
    /* ... */
}

gcc -c -o foo.o foo.c编译它。

C++ 部分:

bar.cpp

extern "C" {
  #include "foo.h" //a C header, so wrap it in extern "C" 
}

void bar() {
  foo();
}

g++ -c -o bar.o bar.cpp编译它

然后将它们链接在一起:

g++ -o myfoobar foo.o bar.o

理由: C 代码应该是纯 C 代码,没有 #ifdefs 表示“也许有一天我会用另一种语言调用它”。如果某些 C++ 程序员调用了您的 C 函数,那么他们如何做到这一点是他们的问题,而不是你的问题。如果您是 C++ 程序员,那么 C 标头可能不是您的,您不应该更改它,因此未损坏的函数名称(即 extern "C")的处理属于您的 C++ 代码。

当然,您可以自己编写一个方便的 C++ 标头,除了将 C 标头包装到 extern "C" 声明中之外什么都不做。

【讨论】:

  • 似乎合法。 +1 理由
  • 终于有一个完全清楚的解释了。非常感谢!
【解决方案3】:

我同意Prof. Falken's answer,但是在Arne Mertz 的评论之后我想举一个完整的例子(最重要的部分是#ifdef __cplusplus):

somecode.h

#ifndef H_SOMECODE
#define H_SOMECODE

#ifdef __cplusplus
extern "C" {
#endif

void foo(void);

#ifdef __cplusplus
}
#endif

#endif /* H_SOMECODE */

somecode.c

#include "somecode.h"

void foo(void)
{
    /* ... */
}

othercode.hpp

#ifndef HPP_OTHERCODE
#define HPP_OTHERCODE

void bar();

#endif /* HPP_OTHERCODE */

othercode.cpp

#include "othercode.hpp"
#include "somecode.h"

void bar()
{
    foo(); // call C function
    // ...
}

然后您按照 Falken 教授的说明进行编译和链接。

之所以可行,是因为使用gcc编译时,宏__cplusplus没有定义,所以somecode.c中包含的标头somecode.h经过预处理后是这样的:

void foo(void);

当使用g++编译时,__cplusplus定义的,所以othercode.cpp中包含的标头现在是这样的:

extern "C" {

void foo(void);

}

【讨论】:

  • thb,我不喜欢 C 代码中的 #ifdef __cplusplus。 C 代码是较低级别的,如果有一天它可能会从 C++ 代码中调用,它应该不必费心。如果您想为用 C++ 编写的库提供 C 绑定头文件,我认为 #ifdef 仅在 C++ 代码中使用,而不是相反。
  • @Prof.Falken 当然,但它的定义是为了能够提供 C++ 代码的“向下”兼容性,而不是 C 代码。
【解决方案4】:

这个答案的灵感来自一个 Arne 的理由是正确的案例。供应商编写了一个曾经同时支持 C 和 C++ 的库;但是,最新版本仅支持 C。代码中留下的以下残留指令具有误导性:

#ifdef __cplusplus
extern "C" {
#endif

这花了我几个小时尝试用 C++ 编译。简单地从 C++ 调用 C 就容易多了。

ifdef __cplusplus 约定违反了单一责任原则。使用此约定的代码试图同时做两件事:

  • (1) 在 C 中执行函数 -- 和 --
  • (2) 在 C++ 中执行相同的函数

这就像同时尝试用美式英语和英式英语写作一样。这会不必要地抛出 #ifdef __thequeensenglish 扳手 #elif __yankeeenglish 扳手 #else 一个无用的工具,它会使代码更难将 #endif 读入代码中。

对于简单的代码和小型库,ifdef __cplusplus 约定可能有效;然而,对于复杂的库,最好选择一种或另一种语言并坚持使用。与同时支持两种语言相比,支持其中一种语言所需的维护更少。

这是我为在 Ubuntu Linux 上编译而对 Arne 的代码所做的修改的记录。

foo.h

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

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

void foo(void)
{
     // modified to verify the code was called
     printf("This Hello World was called in C++ and written in C\n");
}

bar.cpp

extern "C" {
    #include "foo.h" //a C header, so wrap it in extern "C" 
}

int main() {
  foo();
  return(0);
}

生成文件

# -*- MakeFile -*-
# dont forget to use tabs, not spaces for indents
# to use simple copy this file in the same directory and type 'make'

myfoobar: bar.o foo.o
    g++ -o myfoobar foo.o bar.o 

bar.o: bar.cpp
    g++ -c -o bar.o bar.cpp

foo.o: foo.c
    gcc -c -o foo.o foo.c

【讨论】:

    猜你喜欢
    • 2011-10-20
    • 2018-02-10
    • 1970-01-01
    • 1970-01-01
    • 2013-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多