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



伙计们,假设我有一个正在运行的c ++应用程序/库,它实现了说

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

这不在"外部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;
    }
}

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

存根标头:

// 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 代码链接:

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

正如Paul R.回答的那样,你需要一个存根代码。 此外,您最好确保您的 C++ 函数不会引发异常(我猜从 C 程序调用的 C++ 函数和 main ,抛出未捕获的异常具有一些未定义的行为)。顺便说一句,你应该确保static C++数据的构造函数(如std::cout)在你用 C main之前被"概念上"调用(所以你最好将你的程序与C++编译器链接,而不是 C 编译器)。请参阅海湾合作委员会__attribute__(constructor)

实际上,至少在Linux上,C++代码由GCC(g++)或Clang/LLVM(clang++)编译,C++函数有一些被篡改的名称。

你可能会使用一些丑陋且不可移植的技巧来调用函数,因为它被破坏的名称。你可以敢于编码:

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

但我有点羞于给出这种愚蠢的建议。您甚至可以使用 asm 标签,例如

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

但是,我觉得你不应该有任何这样的方法,我很奇怪为什么你需要调用一个没有包裹extern "C"的C++函数。

请注意,理论上,C++函数(不带extern "C")甚至可以具有与 C 函数不同的且不兼容的调用约定。我不知道有任何实现这样做。 因此,为了安全起见,您应该使用一些C++包装来包装您的C++函数extern "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 的 Lispy 领域特定语言)来自定义 GCC,方法是在 MELT 中编写扩展,这将在编译C++头文件时生成胶水代码。

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

从我收集到的:

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

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

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

注意 2:如果您使用的是某些 C99 功能(例如 VLA),则该代码将不会以C++编译。

相关内容

  • 没有找到相关文章

最新更新