使用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没有新的/删除操作符。