【问题标题】:Wrapping a C lib with extern "C" except an internal C++ include用外部“C”包装一个 C 库,除了一个内部 C++ 包括
【发布时间】:2014-05-13 08:27:49
【问题描述】:

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

lib.h 中,我只包含我想要公开的所有内部标头,如下所示:

#ifndef LIB_H
#define LIB_H

#include "lib_foo.h"
#include "lib_bar.h"
#include "lib_baz.h"

#endif

因此客户端只需要包含lib.h 即可使用该库。

在我的第一次尝试中,我已经这样做了:

#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 的问题:Using shared library in Gyp in node-sqlite3

【问题讨论】:

  • 此类问题通常表明您使用的 C 库缺乏良好的设计。您能否编辑lib_*.h 文件并用extern "C" 而不是#include "already_compilerd_c++.h" 包围这些标识符?您也可以将此作为补丁发送给 C lib 的原作者。
  • 是的,这就是我所做的,但现在我收到一个错误:error: expected identifier or ‘(’ before string constant。我只是将原型包装在头文件中。
  • .h 文件如何“已编译”?
  • @MattMcNabb 编译成.so文件。

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


【解决方案1】:

请注意,extern C 不是合法的 C,因此只能在编译为 C++ 时包含它。

hass_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是否被条件包含并添加相应的条件。

【讨论】:

  • 其实c++头的include是:#include &lt;already_compiled_c++.h&gt;
  • 我将接受这个答案,因为我还必须包装所有包含。
【解决方案2】:

您不能从用纯 C 编译的文件调用 C++。

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

这个答案可能会进一步帮助你:Elegantly call C++ from C

【讨论】:

    【解决方案3】:

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

    如果already_compiled_c++.h 中声明的函数将其实现编译为使用 C++ ABI,则您的 C 程序无法链接到它们。 (没有极其丑陋的黑客行为)。

    为避免将来出现此类问题,请在每个将具有 C 实现但可能用于 C++ 程序的头文件中明确放置 #ifdef __cplusplus extern "C" { 保护,反之亦然。

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

    【讨论】:

      【解决方案4】:

      重新考虑您的设计。您不能包含来自 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++,指向 struct Foo 的指针和指向 Foo 类的指针是一回事。因此,如果您需要从 C++ 幕后实现的纯 C API 返回 C++ 类,您可以做的就是始终在标头中使用 struct Foo*(C 可以正确理解),并为类的方法如:

      extern "C" int FooGetCount( struct Foo* thisFoo )
      {
          return thisFoo->GetCount();
      }
      

      这样他们可以保留指向 C++ 对象的指针并访问它们的属性,但不需要实际使用 C++。当然,您还需要为创建/删除对象提供类似的包装器,因为 C 没有 new/delete 运算符。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-22
        • 1970-01-01
        • 2021-02-20
        • 1970-01-01
        • 1970-01-01
        • 2017-03-18
        相关资源
        最近更新 更多