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


84

我知道这个。

从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




Answers:


95

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

即使C不是面向对象的,您的C API也可以选择遵循面向对象的样式。例如:

 // *.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);
 }


1
您如何链接最终的可执行文件?您使用C还是C ++?(当我使用C时,链接器找不到C ++函数;当我使用C ++时,链接器找不到std :: cout。)
Zack

1
@Zack如果您使用可传递地包含其依赖性的C ++编译器/链接器创建静态库(包括C ++标准库,该链接器可能会隐式链接),则您应该能够使用C编译器/链接器。
Michael Aaron Safyan '16

您可以将模板从C ++导出到C吗?
MarcusJ

我有一种“混合”行为,因为我需要同时使用C ++和C代码来调用我的库。在*.cpp fileI中,我还需要将函数包装为#ifdef _cplusplus+,extern "C"以避免链接时出现“未定义的引用”错误。
Coconop

41

我将通过以下方式进行操作:

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

假设我有一个名为AAA的C ++类,在文件aaa.h,aaa.cpp中定义,并且该AAA类具有一个名为sayHi(con​​st 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(con​​st 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。引起注意


1
您也可以使用gcc usecpp.c -L. -laaa_c_connector -Wl,-rpath,. -o c_aaa
Metaphox '16

什么usecpp.c
Alaa M.

1
最后的编译行应为:gcc main.c -L. -laaa_c_connector -laaa -lstdc++ -o c_aaa。请注意-laaa-lstdc+++
Alaa M.

@AlaaM。有人编辑我的代码,并改变main.cusecpp.c。我刚刚还原了它。...关于另一条评论,它也应该照常工作。第二个编译器使用了-laaa它,应该足够了(我在1.5年前写了它,我不太记得自己做了什么,但是我认为我在发布之前测试了每一行)
SomethingSomething,2016年

1
好的,将其添加为帖子末尾的注释。感谢您的关注!
某物

6

如果要执行此操作,则必须为C ++中的C写一个包装器。C ++向后兼容,但C不向前兼容。


5

假设C ++ API与C兼容(没有类,模板等),则可以将其包装在 extern "C" { ... },就像您采用另一种方法一样。

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


不太... C ++库需要重新编译。
Michael Aaron Safyan

不好了。C ++ API是完全面向对象的。
爪子

2
@claws,请参阅我的文章,以制作OOP风格的C代码..并使用带有C接口但基本的C ++实现的样式创建包装器库。然后链接到C接口。
Michael Aaron Safyan'4

您可能需要看一看包装器'工具',例如swig,pyboost,...,它们做的等价的事情(但还不面向C ...)
xtofl 2010年

2

将C ++函数导出为外部“ C”(也称为C样式符号),或者在创建C ++库时使用.def文件格式为C ++链接器定义未经修饰的导出符号,那么C链接器在读取时应该没有问题


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

2
请考虑在回答中添加一些解释。
HMD

简要说明如何上面回答了这个问题,使解答更有用他人。
SOS

-3

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

extern“ C” int Mycppfunction()

{

//代码在这里

返回0;

}

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


三个下注而不是一个单一的解释。SO社区一如既往地很棒。
Zimano
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.