我正在重构一些C代码并对因子部分进行单元测试(使用Google Test)。在循环中多次使用了一个片段,因此为了将其暴露给测试,我将其作为头文件inline
中的demo.h
函数进行了考虑,其中还包含其他一些非inline
的声明。 1}}功能。简化版如下:
#ifndef DEMO_H_
#define DEMO_H_
#ifdef __cplusplus
extern "C" {
#endif
inline void print_line(FILE* dest, const double * data, int length) {
for (int s = 0; s < length; s++)
fprintf(dest, "%lf ", data[s]);
fprintf(dest, "\n");
}
#ifdef __cplusplus
}
#endif
#endif /* MK_H_ */
我的测试代码
#include "gtest/gtest.h"
#include "demo.h"
#include <memory>
#include <array>
#include <fstream>
TEST (demo, print_line) {
std::array<double,4> test_data = {0.1, 1.4, -0.05, 3.612};
const char* testfile = "print_line_test.txt";
{
auto output_file = std::unique_ptr<FILE, decltype(fclose)*>{
fopen(testfile, "w"), fclose };
print_line(output_file.get(), test_data.data(), test_data.size());
}
std::ifstream input(testfile);
double dval;
for(const auto& v: subsequence_data) {
input >> dval;
EXPECT_EQ (v, dval);
}
EXPECT_FALSE (input >> dval) << "No further data";
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
此代码使用-std=gnu++0x
在MinGW g ++ 4.8.1下编译并运行正常。
然后原始C代码使用此功能。简化版本如下:
#include "demo.h"
void process_data(const char* fname, double ** lines, int num_lines, int line_length) {
FILE* output_file = fopen(fname, "w");
for (int i=0; i<num_lines; ++i) {
print_line(output_file, lines[i], line_length);
}
}
但是,当我尝试使用带有-std=c99
的MinGW GCC 4.8.1编译我的C代码时,我收到以下警告:
警告:&#39; fprintf&#39;是静态的,但用于内联函数&#39; print_line&#39; 这不是静态的[默认启用]
我还得到一个后续错误,可能是相关的:
对'print_line&#39;
的未定义引用
将标题中的签名更改为static inline void print_line ...
似乎可以解决问题。但是,我不想理解问题的原因。为什么缺少static
不会影响C ++测试?关于fprintf
的错误究竟是什么意思?
答案 0 :(得分:7)
如果没有static
,您允许C99编译器创建具有外部链接的函数(在单个位置定义),但在包含该文件的每个翻译单元中也包含内联代码。它可以使用它喜欢的任何功能,除非您在static
或extern
之间明确决定。
C99 Draft 6.7.4.3中可以看到这些功能的一个要求:
具有外部链接的函数的内联定义不应包含具有静态存储持续时间的可修改对象的定义,并且不应包含对具有内部链接的标识符的引用。
这是有道理的,因为编译器希望此函数的行为相同,无论它选择如何实现它。
因此,在这种情况下,编译器抱怨你的非静态内联函数正在调用static
的不同函数,并且它不确定这个其他函数(fprintf
)不会改变静态存储。
答案 1 :(得分:4)
首先,inline
的行为,特别是涉及所涉及符号的链接,在C到C ++中是不同的。 (ISO C和GNU C之间也有所不同)。
您可以阅读有关C版here的信息。
如果您尝试将函数体放在C和C ++中包含的标题中(在同一个项目中),那么您将打开一堆真正的蠕虫。任何一种语言标准都没有涵盖这种情况。实际上,我会将其视为ODR violation,因为该函数的C版本与C ++版本不同。
安全的做法是在头文件中仅包含函数原型,并在其中一个非头文件源文件中包含函数体。