97

我知道这个。

从 C++ 调用 C 函数:

如果我的应用程序是用 C++ 编写的,并且我必须从用 C 编写的库中调用函数。那么我会使用

//main.cpp

extern "C" void C_library_function(int x, int y);//prototype
C_library_function(2,4);// directly using it.

这不会破坏名称C_library_function,链接器会在其输入 *.lib 文件中找到相同的名称,问题就解决了。

从 C 调用 C++ 函数???

但是在这里我扩展了一个用 C 编写的大型应用程序,我需要使用一个用 C++ 编写的库。C++ 的名称修改在这里造成了麻烦。链接器正在抱怨未解析的符号。好吧,我不能在我的 C 项目上使用 C++ 编译器,因为那会破坏很多其他东西。出路是什么?

顺便说一句,我正在使用 MSVC

4

7 回答 7

115

您需要创建一个 C API 来公开 C++ 代码的功能。基本上,您将需要编写声明为 extern "C" 并且具有包装 C++ 库的纯 C API(例如,不使用类)的 C++ 代码。然后使用您创建的纯 C 包装库。

您的 C API 可以选择遵循面向对象的风格,即使 C 不是面向对象的。前任:

 // *.h file
 // ...
 #ifdef __cplusplus
 #define EXTERNC extern "C"
 #else
 #define EXTERNC
 #endif

 typedef void* mylibrary_mytype_t;

 EXTERNC mylibrary_mytype_t mylibrary_mytype_init();
 EXTERNC void mylibrary_mytype_destroy(mylibrary_mytype_t mytype);
 EXTERNC void mylibrary_mytype_doit(mylibrary_mytype_t self, int param);

 #undef EXTERNC
 // ...


 // *.cpp file
 mylibrary_mytype_t mylibrary_mytype_init() {
   return new MyType;
 }

 void mylibrary_mytype_destroy(mylibrary_mytype_t untyped_ptr) {
    MyType* typed_ptr = static_cast<MyType*>(untyped_ptr);
    delete typed_ptr;
 }

 void mylibrary_mytype_doit(mylibrary_mytype_t untyped_self, int param) {
    MyType* typed_self = static_cast<MyType*>(untyped_self);
    typed_self->doIt(param);
 }
于 2010-04-30T11:41:00.407 回答
55

我会通过以下方式做到这一点:

(如果使用 MSVC,请忽略 GCC 编译命令)

假设我有一个名为AAA的 C++ 类,在文件aaa.h、aaa.cpp中定义,并且AAA类有一个名为sayHi(const char *name)的方法,我想为 C 代码启用该方法。

AAA级的C++代码——纯C++,我不修改:

aaa.h

#ifndef AAA_H
#define AAA_H

class AAA {
    public:
        AAA();
        void sayHi(const char *name);
};

#endif

aaa.cpp

#include <iostream>

#include "aaa.h"

AAA::AAA() {
}

void AAA::sayHi(const char *name) {
    std::cout << "Hi " << name << std::endl;
}

按照 C++ 的常规编译此类。该代码“不知道”它将被 C 代码使用。使用命令:

g++ -fpic -shared aaa.cpp -o libaaa.so

现在,同样在 C++ 中,创建一个 C 连接器:

在文件aaa_c_connector.h、aaa_c_connector.cpp中定义它。此连接器将定义一个名为AAA_sayHi(cosnt char *name)的 C 函数,它将使用AAA的实例并调用其方法:

aaa_c_connector.h

#ifndef AAA_C_CONNECTOR_H 
#define AAA_C_CONNECTOR_H 

#ifdef __cplusplus
extern "C" {
#endif
 
void AAA_sayHi(const char *name);

#ifdef __cplusplus
}
#endif


#endif

aaa_c_connector.cpp

#include <cstdlib>

#include "aaa_c_connector.h"
#include "aaa.h"

#ifdef __cplusplus
extern "C" {
#endif

// Inside this "extern C" block, I can implement functions in C++, which will externally 
//   appear as C functions (which means that the function IDs will be their names, unlike
//   the regular C++ behavior, which allows defining multiple functions with the same name
//   (overloading) and hence uses function signature hashing to enforce unique IDs),


static AAA *AAA_instance = NULL;

void lazyAAA() {
    if (AAA_instance == NULL) {
        AAA_instance = new AAA();
    }
}

void AAA_sayHi(const char *name) {
    lazyAAA();
    AAA_instance->sayHi(name);
}

#ifdef __cplusplus
}
#endif

再次使用常规 C++ 编译命令对其进行编译:

g++ -fpic -shared aaa_c_connector.cpp -L. -laaa -o libaaa_c_connector.so

现在我有一个共享库 (libaaa_c_connector.so),它实现了 C 函数AAA_sayHi(const char *name)。我现在可以创建一个 C 主文件并一起编译它:

main.c

#include "aaa_c_connector.h"

int main() {
    AAA_sayHi("David");
    AAA_sayHi("James");

    return 0;
}

使用 C 编译命令对其进行编译:

gcc main.c -L. -laaa_c_connector -o c_aaa

我需要将 LD_LIBRARY_PATH 设置为包含 $PWD,如果我运行可执行文件./c_aaa,我会得到我期望的输出:

Hi David
Hi James

编辑:

在某些 linux 发行版上,最后一个编译命令-laaa-lstdc++可能需要。感谢@AlaaM。为了引起注意

于 2015-03-22T09:37:23.833 回答
6

假设 C++ API 是 C 兼容的(没有类、模板等),您可以将其包装在 中extern "C" { ... },就像您在使用其他方式时所做的那样。

如果你想公开对象和其他可爱的 C++ 东西,你必须编写一个包装 API。

于 2010-04-30T11:41:10.453 回答
6

如果你想这样做,你必须在 C++ 中为 C 编写一个包装器。C++ 向后兼容,但 C 不向前兼容。

于 2010-04-30T11:43:31.590 回答
2

将您的 C++ 函数导出为 extern "C"(也称为 C 样式符号),或者在创建 C++ 库时使用 .def 文件格式为 C++ 链接器定义未修饰的导出符号,那么 C 链接器应该可以轻松读取它

于 2010-04-30T11:40:10.093 回答
0
#include <iostream>

//////////////
// C++ code //
//////////////
struct A
{
  int i;
  int j;

  A() {i=1; j=2; std::cout << "class A created\n";}
  void dump() {std::cout << "class A dumped: " << i << ":" << j << std::endl;}
  ~A() {std::cout << "class A destroyed\n";}
};

extern "C" {
  // this is the C code interface to the class A
  static void *createA (void)
  {
    // create a handle to the A class
    return (void *)(new A);
  }
  static void dumpA (void *thisPtr)
  {
    // call A->dump ()
    if (thisPtr != NULL) // I'm an anal retentive programmer
    {
      A *classPtr = static_cast<A *>(thisPtr);
      classPtr->dump ();
    }
  }
  static void *deleteA (void *thisPtr)
  {
    // destroy the A class
    if (thisPtr != NULL)
    {
      delete (static_cast<A *>(thisPtr));
    }
  }
}

////////////////////////////////////
// this can be compiled as C code //
////////////////////////////////////
int main (int argc, char **argv)
{
  void *handle = createA();

  dumpA (handle);
  deleteA (handle);

  return 0;
}
于 2018-04-13T01:10:33.277 回答
-4

您可以在函数声明前加上 extern “C” 关键字,例如

extern “C” int Mycppfunction()

{

// 代码在这里

返回0;

}

有关更多示例,您可以在 Google 上搜索有关“extern”关键字的更多信息。你需要做更多的事情,但你会从谷歌得到很多例子并不难。

于 2019-09-11T09:02:19.410 回答