如何正确地混合使用C++和C

4
我在这方面遇到了一些问题:我需要为一个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`。我该如何调试(或者说我做错了什么)?

你可能想在这里阅读一下:https://dev59.com/0Wcs5IYBdhLWcg3wym_D - alk
@alk 这个包装器?clang++ 'pkg-config --libs --cflags dependent-lib' -std=c++14 wrapper.cpp foo.h wrapper.h -shared -fPIC(我替换了反引号)使用包装器的程序是由编程语言编译和链接的,错误是@ cc。 - Zihemu
好的,等一下,让我给你最少的代码。 - Zihemu
好的,我已经编辑了帖子。 - Zihemu
2
@DavidHaim 嗯....?C语言,无论好坏,都是低级代码接口的既定“通用语言”。你有什么替代方案吗? - Kyle Strand
显示剩余16条评论
2个回答

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++ only)

#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
你正在创建一个名为 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编译标志),然后(如果您不发布静态库,请跳过此步骤)将对象组合成静态库,然后从先前构建的对象构建共享对象。
  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

这不适用于大型项目。


谢谢!但是另一个答案更完整,可能会帮助更多的人,尽管我明白整个问题都在于我如何构建库。再次感谢 :) - Zihemu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接