GFlags使用方法教程

GFlags: 方便的使用c++命令行参数

Posted by YuCong on August 17, 2020

GFlags提供了一种方便的使用c++命令行参数的解决方案。


Created 2020.08.17 by Cong Yu; Last modified: 2020.08.17-v1.0.2

Contact: windmillyucong@163.com

Copyleft! 2022 Cong Yu. Some rights reserved.


GFlags使用方法指南

References

1. 安装

1
2
3
4
5
6
7
8
git clone https://github.com/gflags/gflags

cd gflags
mkdir build
cd build
cmake ..
make -j 24
sudo make install

2. 使用

2.1 代码

头文件

1
#include <gflags/gflags.h>

定义需要使用命令行参数的变量

1
2
3
4
DEFINE_类型(变量名,默认值,描述);
DEFINE_string(img_path, "./test.png", "img to process");
DEFINE_bool(use_camera, false, "use camera or not");
DEFINE_int32(camera_id, 1, "camera id");

支持的所有类型:

gflags类型 描述
DEFINE_bool 布尔类型
DEFINE_int32 32位整数
DEFINE_int64 64位整数
DEFINE_uint64 无符号 64 位整数
DeFINE_double 浮点类型 double
DEFINE_string C++ string 类型

在main函数加入如下解析命令行参数,通常放在 main 开始位置。

在程序中使用时直接以FLAGS_ 前缀加参数名使用即可,类似于FLAGS_img_path。而且该变量也可以在程序运行时被自由修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
DEFINE_string(img_path, "./test.png", "img to process");
DEFINE_bool(use_camera, false, "use camera or not");
DEFINE_int32(camera_id, 1, "camera id");

int main(int argc, char **argv) {  
  google::InitGoogleLogging(argv[0]);  
  google::InstallFailureSignalHandler();  
  google::ParseCommandLineFlags(&argc, &argv, false);  

  std::cout << "imput image:" << FLAGS_img_path << std::endl;
  
  if (FLAGS_use_camera) {
    FLAGS_camera_id += 10; // 可以自由修改
    // balabala...
  }

}

2.2 编译

2.2.1 g++编译
1
➜ g++ hello_gflags.cpp -o hello_gflags -lgflags -lpthread
2.2.2 cmake
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
################################## helpers ##################################  
  
################## add_third_party()  
#  
# CMake function to define a named third party library interface, with included  
# headers and linked libraries.  
#  
# Parameters:  
# NAME: name of target  
# INCLUDES: list of include directories for the library  
# LINKS: list of other libraries to be linked in to the library  
# DEFINES: list of compile definitions  
# COMPILE_OPTIONS: list of compile options  
# )  
function(ADD_THIRD_PARTY)  
    cmake_parse_arguments(ADD_THIRD_PARTY  
            ""  
            "NAME"  
            "INCLUDES;LINKS;DEFINES;COMPILE_OPTIONS"  
            ${ARGN})  
  
    unset(${ADD_THIRD_PARTY_NAME}_LIB_DEPENDS CACHE)  
    add_library(${ADD_THIRD_PARTY_NAME} INTERFACE)  
    target_include_directories(${ADD_THIRD_PARTY_NAME} INTERFACE  
            ${ADD_THIRD_PARTY_INCLUDES})  
    target_link_libraries(${ADD_THIRD_PARTY_NAME} INTERFACE  
            ${ADD_THIRD_PARTY_LINKS})  
    target_compile_definitions(${ADD_THIRD_PARTY_NAME} INTERFACE  
            ${ADD_THIRD_PARTY_DEFINES})  
    target_compile_options(${ADD_THIRD_PARTY_NAME} INTERFACE  
            ${ADD_THIRD_PARTY_COMPILE_OPTIONS})  
endfunction()


# gflags: Google Commandline Flags  
find_package(gflags CONFIG HINTS ${CMAKE_INSTALL_PREFIX})  
if (gflags_FOUND)  
    add_third_party(NAME gflag LINKS gflags)  
else (gflags_FOUND)  
    MESSAGE(FATAL_ERROR "gflag not found")  
endif ()

add_executable(hello_gflags src/hello_gflags.cpp)  
target_link_libraries(hello_gflags  
        gflags
        )

2.3 运行

2.3.1 输入参数

运行程序时,如何输入参数?

  • 用户不指定参数时,使用代码里面的默认参数
  • 用户指定参数时,可以采用两种方式:
    1. 使用 = 赋值: –flag1=值1 –flag2=值2
    2. flag名称 空格 赋值:–flag1 值1 –flag2 值2
    3. 以上两种方式可以混合使用,但是使用=赋值的方式时,无法使用tab自动补全
1
./hello_gflags --img_path=../src/pic.png --camera_id 2
  • 使用 双横线– 或者 单横线 -都是可以的

每一种类型的定义和使用都跟上面的例子相似,但是注意 bool 参数。

2.3.2 当输入bool参数时

bool 参数在命令行可以不指定值也可以指定值

1
2
3
4
5
➜  ./hello_gflags -debug_bool 		# 这样就是 true
➜  ./hello_gflags -debug_bool=true	# 这样也是 true
➜  ./hello_gflags -debug_bool=1 	# 这样也是 true
➜  ./hello_gflags -debug_bool=false # 0是false
➜  ./hello_gflags -debug_bool=0     # 0是false

但是注意使用bool变量时,不能通过空格赋值,即以下写法是无效的

1
➜  ./hello_gflags -debug_bool false

2.4 help 信息

当用户运行程序时,可能并不知道代码里面定义了哪些参数,也不知道他们的默认值是多少,则可以通过打印help信息,获取代码里的gflags信息。

1
2
3
4
5
6
7
8
➜  ./hello_gflags -help

输出:
 Flags from /home/congyu/hello_gflags.cpp
    -camera_id (camera id when using camera) type: int64 default: 4
    -img_path (img path when using image) type: string default: "./test.png"
    -use_camera (true:use camera as input or false: use a image as input)
      type: bool default: false

3. 进阶使用

3.3.1. 跨文件调用

当项目变得复杂时,我们可能需要在多个文件中使用同一个gflags参数。GFlags提供了DECLARE宏来实现跨文件调用。

定义文件 (main.cpp):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <gflags/gflags.h>
#include <iostream>

// 定义参数
DEFINE_string(config_file, "./config.txt", "configuration file path");
DEFINE_int32(thread_num, 4, "number of threads");
DEFINE_bool(verbose, false, "enable verbose output");

// 声明外部函数
void process_data();

int main(int argc, char **argv) {
    google::ParseCommandLineFlags(&argc, &argv, true);
    
    std::cout << "Config file: " << FLAGS_config_file << std::endl;
    std::cout << "Thread number: " << FLAGS_thread_num << std::endl;
    
    process_data();
    return 0;
}

使用文件 (processor.cpp):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <gflags/gflags.h>
#include <iostream>

// 声明在其他文件中定义的参数
DECLARE_string(config_file);
DECLARE_int32(thread_num);
DECLARE_bool(verbose);

void process_data() {
    if (FLAGS_verbose) {
        std::cout << "Processing data with " << FLAGS_thread_num << " threads" << std::endl;
        std::cout << "Using config: " << FLAGS_config_file << std::endl;
    }
    
    // 实际的数据处理逻辑
    for (int i = 0; i < FLAGS_thread_num; ++i) {
        if (FLAGS_verbose) {
            std::cout << "Starting thread " << i << std::endl;
        }
        // 线程处理逻辑...
    }
}

编译:

1
g++ main.cpp processor.cpp -o myapp -lgflags -lpthread

3.3.2. 参数检查

GFlags提供了参数验证功能,可以在程序启动时检查参数的有效性。

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
#include <gflags/gflags.h>
#include <iostream>

DEFINE_int32(port, 8080, "server port");
DEFINE_string(host, "localhost", "server host");
DEFINE_double(ratio, 0.5, "processing ratio");

// 端口号验证函数
static bool ValidatePort(const char* flagname, int32_t value) {
    if (value > 0 && value < 65536) {
        return true;
    }
    std::cout << "Invalid value for --" << flagname << ": " << value << std::endl;
    return false;
}

// 比例验证函数
static bool ValidateRatio(const char* flagname, double value) {
    if (value >= 0.0 && value <= 1.0) {
        return true;
    }
    std::cout << "Invalid value for --" << flagname << ": " << value 
              << ". Must be between 0.0 and 1.0" << std::endl;
    return false;
}

// 注册验证器
DEFINE_validator(port, &ValidatePort);
DEFINE_validator(ratio, &ValidateRatio);

int main(int argc, char **argv) {
    google::ParseCommandLineFlags(&argc, &argv, true);
    
    std::cout << "Server will run on " << FLAGS_host << ":" << FLAGS_port << std::endl;
    std::cout << "Processing ratio: " << FLAGS_ratio << std::endl;
    
    return 0;
}

使用示例:

1
2
3
4
5
6
# 正确的参数
./myapp --port=8080 --ratio=0.8

# 错误的参数会被拒绝
./myapp --port=70000  # 错误:端口号超出范围
./myapp --ratio=1.5   # 错误:比例超出范围

3.3.3. flagfile 使用配置文件

当参数很多时,可以使用配置文件来管理参数,避免命令行过长。

创建配置文件 (config.flags):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 这是一个配置文件示例
# 以#开头的行是注释

# 服务器配置
--host=192.168.1.100
--port=9090

# 处理参数
--thread_num=8
--verbose=true

# 文件路径
--config_file=/path/to/real/config.txt
--log_dir=/var/log/myapp

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <gflags/gflags.h>
#include <iostream>

DEFINE_string(host, "localhost", "server host");
DEFINE_int32(port, 8080, "server port");
DEFINE_int32(thread_num, 4, "number of threads");
DEFINE_bool(verbose, false, "enable verbose output");
DEFINE_string(config_file, "./config.txt", "configuration file path");
DEFINE_string(log_dir, "./logs", "log directory");

int main(int argc, char **argv) {
    google::ParseCommandLineFlags(&argc, &argv, true);
    
    std::cout << "=== Configuration ===" << std::endl;
    std::cout << "Host: " << FLAGS_host << std::endl;
    std::cout << "Port: " << FLAGS_port << std::endl;
    std::cout << "Threads: " << FLAGS_thread_num << std::endl;
    std::cout << "Verbose: " << (FLAGS_verbose ? "true" : "false") << std::endl;
    std::cout << "Config file: " << FLAGS_config_file << std::endl;
    std::cout << "Log directory: " << FLAGS_log_dir << std::endl;
    
    return 0;
}

使用方式:

  1. 使用配置文件:
    1
    
    ./myapp --flagfile=config.flags
    
  2. 配置文件 + 命令行参数 (命令行参数优先级更高):
    1
    
    ./myapp --flagfile=config.flags --port=7777
    
  3. 多个配置文件:
    1
    
    ./myapp --flagfile=base.flags --flagfile=override.flags
    

注意事项:

  • 配置文件中的参数格式与命令行相同
  • # 开头的行被视为注释
  • 命令行参数的优先级高于配置文件
  • 可以同时使用多个配置文件,后面的会覆盖前面的同名参数

Contact

Feel free to contact me windmillyucong@163.com anytime for anything.

License

Creative Commons BY-SA 4.0

CC0