如何正确地混合C ++和C.

时间:2015-08-09 09:59:39

标签: c++ c ffi

我遇到了一些问题:我需要为C ++库编写一个C包装器。假设我有3个文件:

  • wrapper.h

    typedef struct Foo Foo;
    Foo* create_foo();
    
  • wrapper.cpp

    extern "C" {
        #include "wrapper.h"
    }
    #include "foo.h"
    
    Foo* create_foo() {
        return new Foo;
    }
    
  • foo.h中

    class Foo {
    public:
        Foo();
    };
    

编译好:

clang++ -std=c++14 wrapper.cpp foo.h wrapper.h -shared -fPIC

clang++ -shared -o libbindings.so a.out

但是在编译使用C包装器的程序时(它是编译器并通过使用包装器的编程语言链接 - Crystal),我得到一个未定义的create_foo()引用和链接器错误collect2: error: ld returned 1 exit status。我该如何调试(以及我做错了什么)?

2 个答案:

答案 0 :(得分:2)

这是一个有效的例子:

wrapper.h (可识别C& C ++)

#ifndef WRAPPER_H_
#define WRAPPER_H_

#ifdef __cplusplus
extern "C" {
#endif

typedef struct CPPClass CPPClass;

CPPClass* CPPClass_new();
void CPPClass_do_something(CPPClass* cppclass);
int CPPClass_get_state(CPPClass* cppclass);
void CPPClass_delete(CPPClass* cppclass);

#ifdef __cplusplus
}
#endif
#endif /* WRAPPER_H_ */

wrapper.cpp (仅限C ++)

#include "wrapper.h"

class CPPClass
{
    int state;
public:
    CPPClass(): state(0) {}
    void do_something() { ++state; }
    int get_state() const { return state; }
};

extern "C" CPPClass* CPPClass_new()
{
    return new CPPClass;
}

extern "C" void CPPClass_do_something(CPPClass* cppclass)
{
    cppclass->do_something();
}

extern "C" int CPPClass_get_state(CPPClass* cppclass)
{
    return cppclass->get_state();
}

extern "C" void CPPClass_delete(CPPClass* cppclass)
{
    delete cppclass;
}

use-wrapper.c (仅限C)

#include <stdio.h>
#include "wrapper.h"

int main(void)
{
    CPPClass* cppclass = CPPClass_new();

    if(!cppclass)
    {
        printf("ERROR: failed to create CPPClass:\n");
        return 1;
    }

    printf("state: %d\n", CPPClass_get_state(cppclass));
    CPPClass_do_something(cppclass);
    printf("state: %d\n", CPPClass_get_state(cppclass));

    CPPClass_delete(cppclass);
}

编译CPP

g++ -std=c++11 -shared -fPIC -o libwrapper.so wrapper.cpp

编译C

gcc -o use-wrapper use-wrapper.c -L. -lwrapper -lstdc++

<强>输出:

$ ./use-wrapper 
state: 0
state: 1

希望有所帮助。

答案 1 :(得分:1)

您正在创建名为a.out的共享对象,然后创建另一个名为libbindings.so的共享对象,该对象表面上链接到a.out,但不引用任何内容。现在,如果一组输入文件没有任何未定义的符号,则不会搜索任何库或将其添加到输出中。所以libbindings.so本质上是一个空库。验证:

 % nm a.out | grep create_foo
 00000000000006bc T create_foo
 % nm libbindings.so | grep create_foo
 %

如果你有几个源文件,你应该从每个源构建一个对象文件(使用-c compilation标志),(然后可选地将对象组合成一个静态库---跳过这一步如果你没有发布静态库),那么从以前构建的对象构建一个共享对象:

  clang++ -c -fPIC foo.cpp
  clang++ -c -fPIC bar.cpp
  clang++ -shared -o libfoobar.so foo.o bar.o

如果您只有一个源或很少的源文件,您可以轻松地一起编译,您可以一步构建共享库:

  clang++ -std=c++14 wrapper.cpp somethingelse.cpp -shared -fPIC -o libbindings.so

不推荐用于大型项目。