clang-tidy:如何抑制C头文件中的C++警告?

9
我有一个.h文件,被C和C++源文件都包含。它的内容被包裹在HTML标签`

`中。

#ifdef __cplusplus
extern "C" {
#endif

...

#ifdef __cplusplus
}
#endif

然而,当我将它包含在.cpp文件中时,clang-tidy会发出C++特定的消息,例如:

  • 警告:在C ++中包含'stdbool.h'没有任何效果;考虑删除它 [hicpp-deprecated-headers,modernize-deprecated-headers]
  • 警告:包含已弃用的C ++头文件'stdlib.h';考虑使用'cstdlib'代替 [hicpp-deprecated-headers,modernize-deprecated-headers]
  • 警告:使用'typedef'之前,应该使用'using' [modernize-use-using]

我喜欢这些检查,并希望在我的clang-tidy配置中保持其活动状态,但当然,仅适用于C ++代码。 我无法更改头文件以使用using而不是typedef<cstdlib>而不是<stdlib.h>,因为它也被C源包含,因此需要extern "C"

有没有办法告诉clang-tidy将extern "C"中的代码视为C而不是C ++,即使从.cpp文件包含?

clang-tidy版本为12.0.0。


你可以尝试使用内联抑制来禁用那些警告。 - icebp
1
@icebp 他可能会这样做,但是他所犯的“生态”错误不应该被忽视。标准头文件包含永远不应该在extern块内部进行,并且不建议包含C版本的头文件(正式上是未定义行为)。 - Swift - Friday Pie
2个回答

5

Clang-Tidy可以利用特殊的NOLINTNOLINTNEXTLINE注释来抑制特定行的警告。这正是为您的使用情况而设计的:

  • 一些行包含传统或不太好的C++代码
  • 有一个很好的理由这样做 - 这里的代码必须能够被C编译器解析。

使用它的更高风险是滥用它并在可能改进编码的地方消除警告。但是,当您需要一个头文件用于C和C++源,并且您已经仔细阅读了两次NOLINTed行时,至少在我看来,这是完全可以接受的。此外,甚至可以指示要消除的警告:

#ifdef __cplusplus
extern "C" {
#endif
// NOLINTNEXTLINE(hicpp-deprecated-headers,modernize-deprecated-headers) C compatible code
#include <stdbool.h>
#include <stdlib.h>     // NOLINT C code requires that header
...

#ifdef __cplusplus
}
#endif

-1

那些 C 和 C++ 的头文件严格来说并不等同于 C++。这些警告是合法的,表明代码不符合 C++ 标准。

extern "C" 只声明了函数和变量的链接类型为全局的,并且没有名称修饰,名称使用 C 格式。它不能影响使用 C++ 特性声明的对象和函数。它与语言生态系统无关。此外,如果一个 C++ 头文件出现在这些括号中,如果在那里声明的函数最初具有 C++ 链接,则可能会出现链接错误。

对于 C++,您必须使用 #ifdef __cplusplus 来包含 C++ 替代方案,否则包括 C 的替代方案。

而不是:

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>
#include <stdlib.h>

// your declarations

#ifdef __cplusplus
}
#endif

您将拥有以下结构的标题

// headers that are universally compatible (3rd party)

#ifdef __cplusplus
#   include <cstdlib>
// other C++ headers
// Your C++ - only declarations (incompatible with C rules) 

extern "C" {
#else
#   include <stdbool.h>
#   include <stdlib.h>
// other C headers
//  C-only declarations (incompatible with C++ rules)

#endif

// your compatible declarations available for both

#ifdef __cplusplus  
}  // extern "C"
#endif

在某些情况下,由于头文件包含的顺序或声明的依赖关系,可能需要调整此结构。盲目更改库头文件中的声明链接是一个失误,因为您没有编写这些头文件,也无法保证它们使用什么命名约定和链接类型,这可能导致ODR违规或链接失败。
typedef问题很烦人,您可以使用NOLINT或命令行选项option来抑制modernize-use-using作为临时措施。通常,对于可移植的头文件,至少应该通过宏定义来定义结构类型。存在一个问题,即在某些情况下,typedef和别名声明不能通过标准手段进行转换,例如考虑函数指针类型的声明。

1
@HolyBlackCat,你是在说extern "C" { #include <stdlib.h> }是等同于OP所暗示的标准兼容的#include <cstdlib>吗?这并不是非法的,取决于实现方式,如果cstdlib只包含同时包含C++和C代码的stdlib.h,并且某些符号的运行时名称不同。 cstdlib有时会从其他位置包含一些头文件或具有自己的实现。这没有定义。 - Swift - Friday Pie
@KamilCuk“是一个实现细节”,是的,这就是我要说的。在C++代码中使用extern "C" { #include <stdlib.h> },它是否会链接,也取决于具体的实现,当我使用一些专有编译器时,遇到了链接问题。这与std命名空间无关,而是与头文件的实现特定内容有关,其中声明了一些供编译器使用的内容。 - Swift - Friday Pie
1
我假设OP是一个明智的人,将他们的包含放在extern "C" {之上。那么它就是有效的。如果你把它放在下面,那么是可能会出现未定义行为的。 - HolyBlackCat
@HolyBlackCat,我不知道如何解释“其(标题)内容包含在其中”的语句。我以前见过这种情况,甚至在非常昂贵的GIS系统的标题中也有类似的东西,例如#define max #define data等。 - Swift - Friday Pie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接