C++ 开发随笔

文件读写

正确的文件写入方式

  • 错误的文件写入方式

    • 创建文件或使用原有的文件
    • 往文件中写入数据
    • 关闭文件
  • 正确的文件写入方式

    • 创建临时文件(如果存在原有的文件,则需要拷贝原文件的数据),比如文件以 .tmp 后缀结尾
    • 往临时文件中写入数据
    • 关闭临时文件
    • 将临时文件重命名为正式的文件,比如文件去除 .tmp 后缀

正确的文件写入方式有点类似 “写时复制” 机制,可以避免文件还没写入完成,就被其他程序读取到,从而导致其他程序读取了不完整的数据,影响后续业务处理的正确性。

构建工具

CMake 无法引入第三方库的头文件

这里以 GoogleTest 库为例子,讲述 CMake 为什么无法正常引入项目里的第三方库的头文件,其中 GoogleTest 库在项目里的目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
minder
├── CMakeLists.txt
├── include
├── src
└── thirdparty
└── googletest
├── gmock
│ ├── include
│ │ └── gmock
│ │ ├── gmock-actions.h
│ │ ├── gmock-cardinalities.h
│ │ ├── gmock-function-mocker.h
│ │ ├── gmock-generated-actions.h
│ │ ├── gmock-generated-actions.h.pump
│ │ ├── gmock-generated-function-mockers.h
│ │ ├── gmock-generated-function-mockers.h.pump
│ │ ├── gmock-generated-matchers.h
│ │ ├── gmock-generated-matchers.h.pump
│ │ ├── gmock.h
│ │ ├── gmock-matchers.h
│ │ ├── gmock-more-actions.h
│ │ ├── gmock-more-matchers.h
│ │ ├── gmock-nice-strict.h
│ │ ├── gmock-spec-builders.h
│ │ └── internal
│ │ ├── custom
│ │ │ ├── gmock-generated-actions.h
│ │ │ ├── gmock-generated-actions.h.pump
│ │ │ ├── gmock-matchers.h
│ │ │ ├── gmock-port.h
│ │ │ └── README.md
│ │ ├── gmock-internal-utils.h
│ │ ├── gmock-port.h
│ │ └── gmock-pp.h
│ └── lib
│ ├── libgmock_main.so
│ └── libgmock.so
└── gtest
├── include
│ └── gtest
│ ├── gtest-death-test.h
│ ├── gtest.h
│ ├── gtest-matchers.h
│ ├── gtest-message.h
│ ├── gtest-param-test.h
│ ├── gtest_pred_impl.h
│ ├── gtest-printers.h
│ ├── gtest_prod.h
│ ├── gtest-spi.h
│ ├── gtest-test-part.h
│ ├── gtest-typed-test.h
│ └── internal
│ ├── custom
│ │ ├── gtest.h
│ │ ├── gtest-port.h
│ │ ├── gtest-printers.h
│ │ └── README.md
│ ├── gtest-death-test-internal.h
│ ├── gtest-filepath.h
│ ├── gtest-internal.h
│ ├── gtest-param-util.h
│ ├── gtest-port-arch.h
│ ├── gtest-port.h
│ ├── gtest-string.h
│ ├── gtest-type-util.h
│ └── gtest-type-util.h.pump
└── lib
├── libgtest_main.so
└── libgtest.so

特别注意

在上面的项目结构中,gtest 的头文件所在的目录是 thirdparty/googletest/gtest/include/gtest/,而不是 thirdparty/googletest/gtest/include/。因此在 C++ 源文件中引入 gtest 头文件的正确写法是 #include <gtest/gtest.h>,即头文件的路径是 include 目录下的 gtest/gtest.h。这一点必须注意,否则会经常导致 CMake 无法正常引入第三方库的头文件。简单一句话概况,如果在 C++ 源文件中,头文件的引入方式是 #include <gtest/gtest.h>,那么在项目里的第三方库的 include 目录下必然要有一个 gtest 子目录。

  • C++ 的示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <gtest/gtest.h>

using namespace std;

TEST( COutputPopLimitStrategyTest, PositiveNos )
{
EXPECT_EQ(true, true);
}

int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
  • CMake 的示例配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 定义 GoogleTest 库的目录路径
set(PATH_TO_GOOGLE_TEST thirdparty/googletest/gtest)
set(PATH_TO_GOOGLE_MOCK thirdparty/googletest/gmock)

# 引入 GoogleTest 库的头文件
include_directories(${PATH_TO_GOOGLE_TEST}/include ${PATH_TO_GOOGLE_MOCK}/include)

# 指定 GoogleTest 动态链接库所在的目录
link_directories(${PATH_TO_GOOGLE_TEST}/lib ${PATH_TO_GOOGLE_MOCK}/lib)

# 指定编译参数
set(CMAKE_CXX_FLAGS "-lpthread")

# 链接 GoogleTest 的动态链接库
target_link_libraries(${PROJECT_NAME} gtest_main.so gtest.so gmock_main.so gmock.so)