如何正确混合使用 C++ 和 C
How to mix C++ and C correctly
我遇到了一些问题:我需要为 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
。我该如何调试它(我做错了什么)?
您正在创建一个名为 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
不推荐用于大型项目。
这是一个工作示例:
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
希望对您有所帮助。
我遇到了一些问题:我需要为 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
。我该如何调试它(我做错了什么)?
您正在创建一个名为 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
不推荐用于大型项目。
这是一个工作示例:
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
希望对您有所帮助。