如何正确混合 C++ 和 C

Posted

技术标签:

【中文标题】如何正确混合 C++ 和 C【英文标题】:How to mix C++ and C correctly 【发布时间】:2015-11-01 09:22:13 【问题描述】:

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

包装器.h

typedef struct Foo Foo;
Foo* create_foo();

包装器.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。我该如何调试(以及我做错了什么)?

【问题讨论】:

您告诉链接器通过指定选项-lshared_library_providing_create_foo 链接到libshared_library_providing_create_foo.so 显示您的wrapper.h(假设这是 C 程序包含的内容)。 @alk 是的,我将 so 目录添加到 LD_LIBRARY_PATH。 @melpomene 和我描述的完全一样,只有 typedef 和函数声明 @DavidHaim Err....?无论好坏,C 都是为低级代码接口建立的“通用语言”。您提出的替代方案是什么? 【参考方案1】:

这是一个工作示例:

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

希望对您有所帮助。

【讨论】:

【参考方案2】:

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

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

如果你有多个源文件,你应该从每个源构建一个 object 文件(使用 -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

不建议大型项目这样做。

【讨论】:

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

以上是关于如何正确混合 C++ 和 C的主要内容,如果未能解决你的问题,请参考以下文章

在 C 和 C++ 中,链接器如何找到具有已实现函数的正确文件?

在不丢失 .data 常量的情况下混合汇编和 C 的正确标志是啥

如何正确使用 QQuickItem::stackBefore() 重新排序 GridLayout 中的项目?

C ++中的函数名称:是否大写? [关闭]

如何正确缩进 PHP/HTML 混合代码? [关闭]

C++如何正确释放内存?