【问题标题】:Compatible definitions of inline functions for C99 and C++C99 和 C++ 的内联函数的兼容定义
【发布时间】:2012-12-31 14:02:31
【问题描述】:

我有一个 C++11 应用程序代码使用的 C99 代码实用程序库。一些内联函数以 C99 风格声明,并在翻译单元中显式生成代码,例如:

// buffer.h
inline bool has_remaining(void* obj) {
...
}

// buffer.c
extern inline bool has_remaining(void * obj);

但是,当我尝试在 C++ 应用程序中使用 has_remaining 时,我在链接时收到有关多个定义的错误。尽管有 extern "C" header guards 说明符,g++ 似乎正在实例化库中已经存在的内联代码。

有没有办法强制 g++ 使用这种类型的定义?

看起来如果我 #ifdef __cplusplus 一个带有 gnu_inline 属性的外部定义,正确的事情将会发生,但肯定有一种更便携的方法来保持现代 C 头文件与现代 C++ 兼容?

-- 编辑:工作示例--

buffer.h:

#ifndef BUFF_H
#define BUFF_H

#include <stdbool.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

inline bool has_remaining(void const* const obj) {
    return (obj != NULL);
}

#ifdef __cplusplus
}
#endif

#endif /* BUFF_H */

buffer.c:

#include "buffer.h"

extern inline bool has_remaining(void const* const obj);

app.cpp:

#include <stdlib.h>
#include <stdio.h>

#include "buffer.h"

int main(int argc, char** argv) {
  char const* str = "okay";
  printf(str);

  has_remaining(str);

  return (0);
}

编译:

$ gcc -std=gnu99 -o buffer.o -c buffer.c
$ g++ -std=gnu++11 -o app.o -c app.cpp
$ g++ -Wl,--subsystem,console -o app.exe app.o buffer.o

buffer.o:buffer.c:(.text+0x0): multiple definition of `has_remaining'
app.o:app.cpp:(.text$has_remaining[_has_remaining]+0x0): first defined here
collect2.exe: error: ld returned 1 exit status

--编辑 2-- __gnu_inline__ 属性确实解决了多个定义的问题。我仍然希望看到一种(更)可移植的方法或一些决定性的推理为什么不存在。

#if defined(__cplusplus) && defined(NOTBROKEN)
#define EXTERN_INLINE extern inline __attribute__((__gnu_inline__))
#else
#define EXTERN_INLINE inline
#endif

EXTERN_INLINE bool has_remaining(void const* const obj) {
  return (obj != NULL);
}

【问题讨论】:

  • 为什么要有extern 声明?包含没有它的标题不起作用?
  • extern "C" 和标头保护无关。为什么在.c 文件中声明extern inline(呵呵)函数...?
  • 我不太确定如何调用 #ifdef __cplusplus 块来保护 C-mangled 函数(如果不是“标题保护”)。有关 extern inline 的说明,请参阅此处,greenend.org.uk/rjk/tech/inline.html——它强制编译器在特定的翻译单元中发出符号。
  • 但是标准解决方案呢:定义任何预处理器常量并使用#ifdef 使每个 C++ 文件包含的 buffer.h 不超过 1 次?
  • 我没有看到多个定义。在 C++ 中,它们很弱,并被链接器合并。这是什么平台?您有完整的简短示例,以便我们重现它吗?

标签: c++ c gcc c++11 c99


【解决方案1】:

C++11 标准状态 (3.2.3),即:

每个程序都应包含该程序中 odr 使用的每个非内联函数或变量的准确定义;无需诊断。定义可以显式出现在程序中,可以在标准或用户定义库中找到,或者(在适当时)隐式定义(参见 12.1、12.4 和 12.8)。 内联函数应在使用它的每个翻译单元中定义。

C++ 也知道 extern+inline,但将其理解为“具有外部链接的内联函数在所有翻译单元中应具有相同的地址”(7.1.2)

所以你使用的 extern+inline 是纯 C99 特性,你必须有足够的东西来做类似的东西:

#ifdef __cplusplus
#define C99_PROTOTYPE(x)
#else
#define C99_PROTOTYPE(x) x
#endif

并在buffer.c中引用:

// buffer.c
C99_PROTOTYPE(extern inline bool has_remaining(void * obj);)

C++11 标头中的内联函数没问题,并且在没有 C99 样式原型的情况下应该可以正常工作。

【讨论】:

  • g++ 从不解析 buffer.c。编辑中的工作示例可能有助于解释这一点。
  • 不同意。试试g++ test.c -S --verbose,你会看到 cc1plus 调用源代码。对于 gcc,正确的语法是 gcc -x c++ test.c --verbose
【解决方案2】:

staticinline 一起使用应该可以解决“多重定义”问题。即使对于无法自行决定不应为“内联”函数生成符号的编译器。

【讨论】:

    【解决方案3】:

    这已报告给 gcc: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56066 在从这里开始讨论之后: http://gcc.gnu.org/ml/gcc-help/2013-01/msg00152.html

    在 linux 上,gcc 为内联函数发出弱符号,而为 extern 内联函数发出一个强符号。在链接时,弱者被丢弃以支持强者。显然,在 Windows 上,事情的处理方式不同。我对 windows 没有任何经验,所以我不知道那里发生了什么。

    【讨论】:

    • Windows 在其目标文件中没有“弱符号”支持(或者至少它没有在 GNU 工具中实现)。这是一个超出 C 和 C++ 语言范围的功能,因此定义了实现。
    • windows上如何处理C++内联函数的合并?
    • 我想你得到一个符号的多重定义错误。我不知道__gnu_inline__ 的工作原理。
    • 通常,gnu_inline 允许 C89 代码使用 C99 外部内联,方法是使定义仅可用于内联并防止生成符号。
    • 我正在查看 c++ 对象的 objdump -x 输出,我注意到内联函数有一个单独的部分。它似乎用 COMDAT 字段值 4 进行注释。根据skyfree.org/linux/references/coff.pdf,值 4 对应于 IMAGE_COMDAT_SELECT_EXACT_MATCH:“链接器在此符号的定义中选择任意部分。如果所有定义都将发出多重定义符号错误不完全匹配。” C99 符号没有 COMDAT 选择字段。
    猜你喜欢
    • 2020-11-28
    • 1970-01-01
    • 2017-05-23
    • 1970-01-01
    • 2012-03-13
    • 1970-01-01
    相关资源
    最近更新 更多