【问题标题】:How to call C++ function from C?如何从 C 调用 C++ 函数?
【发布时间】:2021-11-09 00:43:01
【问题描述】:

我知道。

从 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

【问题讨论】:

标签: c++ c visual-c++ extern-c


【解决方案1】:

您需要创建一个 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);
 }

【讨论】:

  • 如何链接最终的可执行文件?你使用 C 还是 C++? (当我使用 C 时,链接器找不到 C++ 函数;当我使用 C++ 时,链接器找不到 std::cout。)
  • @Zack 如果您使用 C++ 编译器/链接器创建一个静态库,该库可传递地包含其依赖项(包括 C++ 标准库,它可能由您的链接器隐式链接),您应该能够链接使用 C 编译器/链接器针对 C 代码的静态库。
  • 您可以将模板从 C++ 导出到 C 吗?
  • 我有一个“混合”行为,因为我需要 C++ 和 C 代码调用我的库。在*.cpp file 中,我还需要将函数包装到#ifdef _cplusplus + extern "C" 中,以避免链接时出现“未定义的引用”错误。
【解决方案2】:

我会这样做:

(如果使用 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。引起注意

【讨论】:

  • 你也可以用gcc usecpp.c -L. -laaa_c_connector -Wl,-rpath,. -o c_aaa指定lib链接路径
  • usecpp.c 是什么?
  • 最后的编译行应该是:gcc main.c -L. -laaa_c_connector -laaa -lstdc++ -o c_aaa。注意-laaa-lstdc+++
  • @AlaaM。有人编辑了我的代码并将main.c 更改为usecpp.c。我刚刚恢复了它....关于其他评论,它也应该按原样工作。第二次编译使用-laaa,应该够用了(1.5年多以前写的,具体是怎么写的记不太清楚了,但发帖前我觉得每一行都测试过了)
  • 好的,将其添加为帖子末尾的注释。感谢关注!
【解决方案3】:

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

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

【讨论】:

  • 不完全... C++ 库需要重新编译。
  • 哦,不。 C++ API 完全面向对象。
  • @claws,请参阅我关于制作 OOP 风格的 C 代码的文章。并使用该风格和 C 接口创建一个包装库,但它是一个底层 C++ 实现。然后链接到C接口。
  • 您可能想看看像 swig、pyboost 等类似的包装器“工具”,它们可以做类似的事情(但不是针对 C...)
【解决方案4】:

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

【讨论】:

    【解决方案5】:

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

    【讨论】:

      【解决方案6】:
      #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;
      }
      

      【讨论】:

      • 请考虑为您的答案添加一些解释。
      • 对上述如何回答问题的简要说明,使答案对其他人更有用。
      【解决方案7】:

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

      extern "C" int Mycppfunction()

      {

      // 代码在这里

      返回 0;

      }

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多