伙计们,假设我有一个正在运行的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++)函数。
从我收集到的:
- 您有一个无法控制的C++库(您无法添加外部"C")
- 您有一个必须调用C++库的 C 库
- 您可以修改 C 库以适应解决方案(您可以更改生成文件并更改要调用的函数的名称)
因此,另一种解决方案是使用与该C++库相同的C++编译器编译 C 库(如果您可以接受的话)。
注意:使用 C89,您可能需要修改代码位(例如,void * 到 T * 的转换在 C++ 中不再隐式),但它可能比 100+ 包装器更容易。
注意 2:如果您使用的是某些 C99 功能(例如 VLA),则该代码将不会以C++编译。