使用extern“C”包装C lib,除了内部C ++包括
我有一个C库,我需要在C ++代码中使用,所以我需要用extern "C"
块包装整个lib。 问题是该库似乎包含一个C ++编译的代码,因此包装整个lib也会包装该C ++头。
在lib.h
里面我只包含了我要公开的所有内部头文件,如下所示:
#ifndef LIB_H #define LIB_H #include "lib_foo.h" #include "lib_bar.h" #include "lib_baz.h" #endif
因此客户端只需要包含lib.h
即可使用lib。
在我的第一次尝试中,我做到了这一点:
#ifndef LIB_H #define LIB_H extern "C" { #include "lib_foo.h" #include "lib_bar.h" #include "lib_baz.h" } #endif
但是当我在already_compiled_c++.h
执行任何函数时,我得到一个符号查找错误。
如何避免在already_compiled_c++.h
头文件中应用extern "C"
?
编辑:
解决了。 这不是使用extern "C"
的问题,将编译的c ++库与gyp正确链接是一个问题: 在node-sqlite3中使用Gyp中的共享库
请注意, extern C
不是合法的C,因此它只能包含在编译为C ++的情况下。
already_compiled_c ++ .h标头可能包含针对多个包含的警戒,因此请先包含它:
#ifndef LIB_H #define LIB_H # This include added so that it won't get marked extern "C" when included by lob_foo.h. #include #ifdef __cplusplus extern "C" { #endif #include "lib_foo.h" #include "lib_bar.h" #include "lib_baz.h" #ifdef __cplusplus } #endif #endif
注意:您需要检查是否有条件地包含already_compiled_c++.h
并添加相应的条件。
你不能从在简单C中编译的文件中调用C ++。
如果lib_foo.h是普通的C,它不能直接使用already_compiled_c ++中的函数。 您很可能需要为它创建一个C包装器。
这个答案可能会对你有所帮助: 优雅地从C调用C ++
如果你的图表是准确的,那么lib_foo.h
不应该包括already_compiled_c++.h
。 编辑它以停止包括它。
如果在already_compiled_c++.h
声明的函数将其实现编译为使用C ++ ABI,则C程序无法链接它们。 (没有非常丑陋的黑客行为)。
为了避免将来出现类似的问题,请在每个头文件中明确地放置#ifdef __cplusplus
extern "C" {
guards,它将具有C实现,但可能在C ++程序中使用,反之亦然。
针对当前情况的解决方法是创建一个.cpp
文件,该文件具有您需要的function的thunk。 在extern "C"
下发布你的thunk,并通过调用already_compiled_c++.h
的函数来实现它们。
重新思考你的设计。 您不能包含C中的C ++标头,只能反过来。 一些建议:
1)虽然’extern“C”’背后的原始想法是包含来自C ++文件的C头文件,如果你有可以C访问但是用C ++实现的头文件,通常的方法是将声明包装在其中而不是包括。 (使用必需的#ifdef __cplusplus,因此C代码总是将标题视为C,不会阻塞不需要的C ++ – ism’extern“C”’)
2)不要将C ++公开为库的公共API。 C ++通常不提供二进制稳定的ABI。 如果你添加一个方法并且它不在最后,或者你将一个实例变量添加到一个类中,或者如果你在一个模板化的类中更改某些东西,那么所有的客户端都必须重新编译(通常会发生静态库,但是这是dylibs / DLLs / frameworks的一个大问题。 因此,一般来说,最好的想法是将C ++作为实现细节,并仅从库中公开C函数。
3)如果需要从库中公开C ++,请使用Pimpl(私有实现)模式而不使用模板,并将其放在主C头未包含的单独头中,因此C客户端可能不包含它。 这样一个单独的头对#2也很有用,因为你的库的模块实现文件(用C ++编写)可以包含它,从而使用它中的类。
4)对于C ++,指向结构Foo的指针和指向类Foo的指针是相同的。 因此,如果您需要从C ++中幕后实现的C-only API返回C ++类,那么您可以做的只是在头文件中使用struct Foo *(C可以正确理解),并为C ++提供C包装函数。类的方法如:
extern "C" int FooGetCount( struct Foo* thisFoo ) { return thisFoo->GetCount(); }
这样他们就可以保留指向C ++对象的指针并访问它们的属性,但不需要实际使用C ++。 当然,您还需要提供类似的包装器来创建/删除对象,因为C没有新的/删除操作符。