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

时间:2015-03-11 15:47:08

标签: c c++11 dynamic-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;
    }
}

3 个答案:

答案 0 :(得分:8)

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

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

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

答案 1 :(得分:5)

作为Paul R. answered,您需要一个存根代码。另外,你最好确保你的C ++函数没有抛出exceptions(我猜一个C ++函数,从C程序和main调用,抛出一个未捕获的异常就是有一些{{ 3}})。顺便说一句,你应该确保static C ++数据的构造函数(比如std::cout)在 C 中的main之前被“概念性地”调用(所以你更好用C ++编译器链接你的程序,而不是C编译器。请参阅undefined behavior

practice 中,至少在使用GCC __attribute__(constructor)g++)或GCCclang++)编译的C ++代码的Linux上, C ++函数有一些Clang/LLVM

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

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

但我对提出这样愚蠢的建议感到有点惭愧。你甚至可以使用mangled name例如

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

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

请注意,从理论上讲,C ++函数(没有extern "C")甚至可能具有与C函数不同且不兼容的asm labels。我不知道有任何实现这样做。因此,为了安全起见,您应该使用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 ++库很大,您可能会尝试自动生成粘合代码。例如,您可以使用calling convention(一种Lispy域特定语言来扩展GCC)来自定义GCC,方法是在MELT中编写扩展名,这将在编译C ++头文件时生成粘合代码。

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

答案 2 :(得分:1)

从我收集到的内容:

  1. 您有一个C ++库,您无法控制(您无法添加extern&#34; C&#34;)
  2. 您有一个必须调用C ++库的C库
  3. 您可以修改C库以容纳解决方案(您可以更改makefile并更改要调用的函数的名称)
  4. 因此,一个替代解决方案是使用与C ++库相同的C ++编译器编译C库,如果您可以接受的话。

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

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