从 C 库调用 C++ 应用程序代码?

Calling C++ Application Code from C library?

伙计们,假设我有一个 C++ 应用程序/库 运行 实现了

/* Alloc API */
void* my_alloc(int size) {
    return malloc(sizeof(size));
}

这不在 "extern c".

之下

我有一个C动态库需要调用my_alloc,我可以直接调用那个API吗?

Like,

int test_my_alloc (int size) {
    int *x;

    x = (int*)my_alloc(size);
    if (x == NULL) {
        return 0;
    } else {
        return 1;
    }
}

您需要创建一个存根,例如

存根 header:

// stub.h

#ifdef __cplusplus
extern "C" {
#endif

void* my_alloc_c(int size);

#ifdef __cplusplus
}
#endif

存根实现:

// stub.cpp

#include "stub.h"
#include "header_where_my_alloc_is_declared.h"

void* my_alloc_c(int size)
{
    return my_alloc(size);
}

您的 C 代码(示例):

// my_code.c

#include "stub.h"

int main()
{
    void * p = my_alloc_c(42);
    return 0;
}

然后用你的 C 代码编译你的存根并link它:

g++ -Wall -c stub.cpp          # compile stub.cpp
gcc -Wall my_code.c stub.o     # compile your C code and link with stub.o

作为, you need a stub code. also, you'll better be sure that your C++ function does not throw exceptions (I guess that a C++ function, called from a C program and main, which throws an uncaught exception is having some undefined behavior). BTW, you should be sure that the constructor of static C++ data (like std::cout) is called "conceptually" before your main in C (so you better link your program with a C++ compiler, not a C one). See the GCC __attribute__(constructor)

实践中,至少Linux使用GCC (g++) or by Clang/LLVM (clang++), a C++ function has some mangled name编译的C++代码。

您可能会使用一些丑陋且不可移植的技巧来通过其损坏的名称调用函数。你敢编码:

 int main(int argc, char**argv) {
   extern void* _Z10my_alloc_ci(int size);
   void * p = _Z10my_alloc_ci(42);
   return 0;
 }

但我有点羞于提出这样愚蠢的建议。您甚至可以使用 asm labels 例如

extern void*f(int) asm ("_Z10my_alloc_ci");
p = f(42);

但是,我觉得你不应该有任何这样的方法,我很惊讶为什么你需要调用一个没有环绕 extern "C".

的 C++ 函数

请注意,理论上,C++ 函数(没有 extern "C")甚至可以具有与 C 函数不同且不兼容的 calling convention。我不知道有任何实现这样做。所以为了安全起见,你应该使用 extern "C"

用一些 C++ 包装来包装你的 C++ 函数

为避免未捕获的异常,您可以在 C++ 包装器中捕获所有异常:

#include <cstdio>
#include <cstdlib>
extern "C" void* my_alloc_c(int size)  {
  extern void* my_alloc(int);
  try {
    return my_alloc(size);
  } catch (...) {
     ::fprintf(::stderr, "got uncaught C++ exception for my_alloc(%d)\n", 
          size);
    ::fflush(nullptr);
    ::abort();
  }
 }

顺便说一句,如果您的 C++ 库很大,您或许可以尝试自动生成粘合代码。例如,您可以通过在 MELT 中编写扩展来自定义 GCC using MELT(一种用于扩展 GCC 的 Lispy 域特定语言),这将在编译 C++ 头文件时生成粘合代码。

您可能对 libffi 感兴趣,它使您能够(可移植地)调用任意签名的任何(C 和可能是 C++)函数。

根据我收集到的信息:

  1. 你有一个你无法控制的 C++ 库 (你不能添加外部 "C")
  2. 您有一个必须调用 C++ 库的 C 库
  3. 您可以修改您的 C 库以适应解决方案(您可以更改 makefile 并更改您要调用的函数的名称)

因此,如果您可以接受,另一种解决方案是使用用于该 C++ 库的相同 C++ 编译器编译您的 C 库。

注意:对于 C89,您可能需要修改部分代码(例如,void * 到 T * 的转换在 C++ 中不再是隐式的 ),但它可能比 100 多个包装器更容易。

注意 2:如果您正在使用某些 C99 功能(例如 VLA),那么该代码将无法在 C++ 中编译。