使用extern“C”包装C lib,除了内部C ++包括

时间:2014-05-13 08:27:49

标签: c++ c compiler-construction name-mangling

我有一个C库,我需要在C ++代码中使用,所以我需要用extern "C"块包装整个lib。问题是该库似乎包含一个C ++编译的代码,因此包装整个lib也会包装该C ++头。

includes

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内的任何函数时,我得到一个符号查找错误。

如何避免在extern "C"头文件中应用already_compiled_c++.h


修改

解决。这不是使用extern "C"的问题,将编译的c ++库与gyp正确链接是一个问题:Using shared library in Gyp in node-sqlite3

4 个答案:

答案 0 :(得分:4)

请注意,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 <already_compiled_c++.h>

#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并添加相应的条件。

答案 1 :(得分:2)

您无法从以普通C编译的文件中调用C ++。

如果lib_foo.h是普通的C,它不能直接使用already_compiled_c ++中的函数。您很可能需要为它创建一个C包装器。

这个答案可能会对您有所帮助:Elegantly call C++ from C

答案 2 :(得分:2)

如果您的图表准确无误,则lib_foo.h不应包含already_compiled_c++.h。编辑它以停止包括它。

如果在already_compiled_c++.h中声明的函数将其实现编译为使用C ++ ABI,则C程序无法链接它们。 (除了非常丑陋的黑客攻击)。

为了避免将来出现类似的问题,请在每个头文件中明确放置#ifdef __cplusplus extern "C" {防护,这些头文件将具有C实现,但可能在C ++程序中使用,反之亦然。< / p>

当前情况的解决方法是制作一个.cpp文件,其中包含您需要的功能。在extern "C"下发布您的thunk,并通过调用already_compiled_c++.h中的函数来实现它们。

答案 3 :(得分:1)

重新考虑你的设计。您不能包含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没有新的/删除操作符。