gflags使用介绍
GFlags(Google Flags)是一个开源的命令行参数解析库,由Google开发。它提供了一种简单、高效的方式来定义、解析和管理命令行参数,广泛应用于C++项目中。
特点
• 易于使⽤:gflags 提供了⼀套简单直观的 API 来定义和解析命令⾏标志,使得开发者可以轻松地为
应⽤程序添加新的参数。
• ⾃动帮助和⽂档:gflags 可以⾃动⽣成每个标志的帮助信息和⽂档,这有助于用户理解如何使⽤程
序及其参数。
• 类型安全:gflags ⽀持多种数据类型的标志,包括布尔值、整数、字符串等,并且提供了类型检查
和转换。
• 多平台⽀持:gflags 可以在多种操作系统上使⽤,包括 Windows、Linux 和 macOS。
• 可扩展性:gflags 允许开发者⾃定义标志的注册和解析逻辑,提供了强⼤的扩展性
定义参数
cpp
DEFINE_int32(port, 8080, "服务器监听的端口号"); // 定义一个叫port的整数按钮,默认8080,后面是说明
DEFINE_string(log_dir, "/tmp/logs", "日志存放的目录"); // 定义一个字符串按钮
DEFINE_bool(debug, false, "是否打开调试模式"); // 定义一个布尔开关
GFlags 会自动帮你把 --port、--log_dir、--debug 这些"按钮"创建好,并且生成 --help 文档。
访问参数
跨文件访问
我们可以在程序中通过FLAGS_port像正常变量一样访问命令行参数。
如果是想在另一个文件访问当前文件的参数以FLAGS_port为例,我们可以使用宏DECLARE_int32(port)来声明引入了这个参数。其实这个宏就相当于做了extern FLAGS_port,定义了外部链接属性。
命令行参数管家
当用户运行你的程序并输入 ./我的程序 --port=9000 --debug 时,GFlags 会自动: 识别出 --port 和
--debug 是你定义过的"按钮"。 把字符串 "9000" 转换成整数,把 --debug 后面隐含的 true 值取出。 把这些值存好,让你在代码里直接用 FLAGS_port、FLAGS_debug 来访问。
GFlags 就是一个帮你省事的"命令行参数管家"。
对你(程序员)来说:你不用再写一堆又臭又长的 if (argv[i] == "--port")
解析代码了。声明一下,直接用,省时省力,还规范。 对你的用户(使用者)来说:他们可以通过统一的 --help 查看所有选项,用标准的方式 --选项名=值 来配置程序,体验非常好。
初始化所有参数
当我们定义好参数后,需要告诉可执行程序去处理解析命令行传入的参数,使得FLAGS_*变量得到所有命令行传入的赋值,我们需要再在main函数中,调用下面的函数来解析命令行传入的所有参数
cpp
int main(int argc,char* argv[])
{
google::ParseCommandLineFlags(&argc,&argv,true);
return 0;
}
第三个参数被称为 remove_flags 。如果它为 true , 表⽰ ParseCommandLineFlags 会从argv 中移除标识和它们的参数,相应减少 argc 的值。如果它为false , ParseCommandLineFlags 会保留 argc 不变,但将会重新调整它们的顺序,使得标识再前⾯。
通过配置文件访问

cpp
./main -flagfile=main.conf
特殊参数标识
cpp
--help # 显⽰⽂件中所有标识的帮助信息
--helpfull # 和-help ⼀样, 帮助信息更全⾯⼀些
--helpshort # 只显⽰当前执⾏⽂件⾥的标志
--helpxml # 以 xml ⽅式打印,⽅便处理
--version # 打印版本信息,由 google::SetVersionString()设定
--flagfile -flagfile=f #从⽂件 f 中读取命令⾏参数
gtest使用介绍
GTest是⼀个跨平台的 C++单元测试框架,由google公司发布。gtest是为了在不同平台上为编写C++单元测试而⽣成的。它提供了丰富的断言、致命和非致命判断、参数化等等测试所需的宏,以及全局测试,单元测试组件。
断言宏
GTest中的断⾔的宏可以分为两类:
• ASSERT_系列:如果当前点检测失败则退出当前函数
• EXPECT_系列:如果当前点检测失败则继续往下执行
常用断言值介绍
cpp
// bool值检查
ASSERT_TRUE(参数),期待结果是true
ASSERT_FALSE(参数),期待结果是false
//数值型数据检查
ASSERT_EQ(参数1,参数2),传⼊的是需要⽐较的两个数 equal
ASSERT_NE(参数1,参数2),not equal,不等于才返回true
ASSERT_LT(参数1,参数2),less than,⼩于才返回true
ASSERT_GT(参数1,参数2),greater than,⼤于才返回true
ASSERT_LE(参数1,参数2),less equal,⼩于等于才返回true
ASSERT_GE(参数1,参数2),greater equal,⼤于等于才返回true
事件机制

事件机制的最⼤好处就是能够为我们各个测试⽤例提前准备好测试环境,并在测试完毕后⽤于销毁环境,这样有个好处就是如果我们有⼀端代码需要进⾏多种不同⽅法的测试,则可以通过测试机制在每个测试⽤例进⾏之前初始化测试环境和数据,并在测试完毕后清理测试造成的影响。
全局事件
cpp
/*
全局测试套件使用:
1.包含头文件
2.定义全局测试套件
2.1定义测试环境初始接口,会在所有用例执行前执行
2.2定义测试环境清理接口,会在所有用例执行后执行
3.定义测试用例
4.初始化测试环境
5.执行测试用例
*/
#include <gtest/gtest.h>
#include <iostream>
#include <unordered_map>
using namespace std;
// 全局事件:针对整个测试程序,提供全局事件机制,能够在测试之前配置测试环境数据,测试完毕后清理数据
// 先定义环境类,通过继承testing::Environment的派⽣类来完成
// 重写的虚函数接⼝SetUp会在测试之前被调⽤; TearDown会在测试完毕后调⽤.
std::unordered_map<std::string, std::string> dict;
class HashTestEnv : public testing::Environment {
public:
virtual void SetUp() override {
std::cout << "测试前:提前准备数据!!\n";
dict.insert(std::make_pair("Hello", "你好"));
dict.insert(std::make_pair("hello", "你好"));
dict.insert(std::make_pair("雷吼", "你好"));
}
virtual void TearDown() override {
std::cout << "测试结束后:清理数据!!\n";
dict.clear();
}
};
TEST(hash_case_test, find_test) {
auto it = dict.find("hello");
ASSERT_NE(it, dict.end());
}
TEST(hash_case_test, size_test) {
ASSERT_GT(dict.size(), 0);
}
int main(int argc, char *argv[]) {
testing::AddGlobalTestEnvironment(new HashTestEnv());
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
运行结果

TestSuite事件
针对⼀个个测试套件。测试套件的事件机制我们同样需要去创建⼀个类,继承⾃ testing::Test ,实现两个静态函数 SetUpTestCase 和 TearDownTestCase ,测试套件的事件机制不需要像全局事件机制⼀样在 main 注册,⽽是需要将我们平时使⽤的 TEST 宏改为 TEST_F 宏。
spdlog使用介绍
特点
- ⾼性能:spdlog 专为速度⽽设计,即使在⾼负载情况下也能保持良好的性能。
- 零配置:⽆需复杂的配置,只需包含头⽂件即可在项⽬中使⽤。
- 异步⽇志:⽀持异步⽇志记录,减少对主线程的影响。
- 格式化:⽀持⾃定义⽇志消息的格式化,包括时间戳、线程 ID、⽇志级别等。
- 多平台:跨平台兼容,⽀持 Windows、Linux、macOS 等操作系统。
- 丰富的 API:提供丰富的⽇志级别和操作符重载,⽅便记录各种类型的⽇志。
日志输出等级枚举

日志输出格式自定义
logger->set_pattern("%Y-%m-%d %H:%M:%S [%t] [%-7l] %v");
%t - 线程ID(Thread ID)。
%n - ⽇志器名称(Logger name)。
%l - ⽇志级别名称(Level name),如 INFO, DEBUG, ERROR 等。
%v - ⽇志内容(message)。
%Y - 年(Year)。
%m - ⽉(Month)。
%d - ⽇(Day)。
%H - ⼩时(24-hour format)。
%M - 分钟(Minute)。
%S - 秒(Second)。
异步日志类
为了异步记录⽇志,可以使⽤ spdlog::async_logger ,其总体的常规操作与同步⽇志器并⽆区别,区别仅在于⽇志数据的写⼊⽅式有所不同,异步⽇志器的使⽤会伴随创建⼀个异步线程池(默认只有⼀个线程)进⾏⽇志的实际写⼊操作。
cpp
class async_logger final : public logger {
async_logger(std::string logger_name,
sinks_init_list sinks_list,
std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy =
async_overflow_policy::block);
async_logger(std::string logger_name,
sink_ptr single_sink,
std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy =
async_overflow_policy::block);
//异步⽇志输出需要异步⼯作线程的⽀持,这⾥是线程池类
class SPDLOG_API thread_pool {
thread_pool(size_t q_max_items,
size_t threads_n,
std::function<void()> on_thread_start,
std::function<void()> on_thread_stop);
thread_pool(size_t q_max_items, size_t threads_n,
std::function<void()> on_thread_start);
thread_pool(size_t q_max_items, size_t threads_n);
};
}
std::shared_ptr<spdlog::details::thread_pool> thread_pool() {
return details::registry::instance().get_tp();
}
//默认线程池的初始化接⼝
inline void init_thread_pool(size_t q_size, size_t thread_count)
auto async_logger = spdlog::async_logger_mt("async_logger",
"logs/async_log.txt");
async_logger->info("This is an asynchronous info message");
spdlog二次封装
1.由于spdlog本身无法在进行日志输出的时候,输出错误位置:文件名和行号,
因此要通过编译器内置宏__FILE__,__LINE__来进行日志的二次封装:
logger->error("[{}:{}]: hello world-{}",__FILE,__LINE,i);
封装后:ERR(fmt,...) logger->error("[{}:{}]:'' + fmt,__FILE,__LINE,##VA_ARGS )
2.定义一个全局日志器,定义一个日志器参数结构体:(日志输出等级,输出格式,日志器类型,输出目标),定义一个全局日志器初始化接口