讨论/技术交流/交流 | 从编译到运行,轻松学会gflags/
交流 | 从编译到运行,轻松学会gflags

导读:怎么定义一些变量,并且轻松地从命令行设置值,可以学习下 gflags 库。

gflags是Google开源的一个库,可以很方便地定义一些全局变量,并且可以从命令行设置他们的值,广泛应用于各个项目中以及自己平时的开发中。本期参考gflags的官方文档,简单直接介绍下怎么使用这个库。

构建依赖环境

在具体介绍怎么使用gflags库之前,先说下怎么基于CMakeLists.txt来构建依赖环境,方便大家跟着这期博文一起上手体验。

首先需要将gflags从github下载下来:

$ git clone git@github.com:gflags/gflags.git

此时,我的目录结构如下:

$ tree -L 2
.
└── demo
    ├── CMakeLists.txt 
    ├── build					 # 目前是空的,用于后面编译输出
    ├── gflags	       # 上面clone下来的代码
    └── main.cc			   # 目前是空的

其中,demo/CmakeLists.txt的构建代码如下:

cmake_minimum_required(VERSION 2.8.12)
project(gflags_demo)

add_subdirectory(gflags)                   # 添加子目录

add_executable(main main.cc)               # 生成目标文件 main
target_link_libraries(main gflags::gflags) # 链接到 gflags库

经过上述步骤,我们就可以在main.cc文件中,写gflags的demo了啦啦啦。

tutorials

gflags定义的是全局变量,目前支持以下7种基本类型:

  1. DEFINE_bool: bool类型
  2. DEFINE_int32: int32_t 整数类型
  3. DEFINE_int64: int64_t 整数类型
  4. DEFINE_uint32uint32_t整数类型
  5. DEFINE_uint64: uint64_t整数类型
  6. DEFINE_double: double浮点数类型
  7. DEFINE_string: STL的std::string类型

上述的DEFINE_XXX函数,本质上是宏,接口类型一致:DEFINE_XXX(name, val, txt)

  • name:变量名
  • val:变量的默认值
  • txt:这个变量的提示信息,在使用--help时会显示出来

下面通过一个简单的demo来介绍gflags的使用。

在下面的demo中,使用DEFINE_XXX函数定义了三个类型都全局变量:

#include <gflags/gflags.h>
#include <iostream>

DEFINE_bool(big_menu, 
            true, 
            "Include 'advance' option in the menu listing");
DEFINE_string(languages, 
              "english, french, chinese", 
              "comma-separated list of languages to offer in the 'lang' menu");
DEFINE_uint32(age, 
              10, 
              "age");

那怎么在程序中使用他们呢?

在程序中使用DEFINE_XXX函数定义的变量时,需要在每个变量前加上FLAGS_前缀。

比如,下面输出三个变量的默认值:

int main(int argc, char** argv) {
    std::cout<<std::boolalpha;		// 为了让bool类型输出true/false,而不是 1/0
    std::cout<<"big_menu: "<<FLAGS_big_menu<<'\n'
             <<"languages: "<<FLAGS_languages<<'\n'
             <<"age: "<<FLAGS_age<<std::endl;

    return 0;
}

编译运行:

$  cd build && cmake .. && make 	# 进入 demo/build 目录,再编译
$./main 
big_menu: true
languages: english, french, chinese
age: 10

从结果可以看出,输出的就是DEFINE_XXX函数中设置的默认值。

那这些变量的值,怎么通过命令行设置?

只需要在main函数后,添加::gflags::ParseCommandLineFlags函数,就能解析命令行,即解析argcargv参数。

int main(int argc, char** argv) {
    ::gflags::ParseCommandLineFlags(&argc, &argv, true);
    std::cout<<std::boolalpha;

    std::cout<<"big_menu: "<<FLAGS_big_menu<<'\n'
             <<"languages: "<<FLAGS_languages<<'\n'
             <<"age: "<<FLAGS_age<<std::endl;

    return 0;
}

此时,重新编译运行:

$  make 
$ ./main -age 100 -languages "123456"
big_menu: true
languages: 123456
age: 100

对于命令行输入格式,gflags提供了四种格式,以age变量为例:

  • ./main -age 100
  • ./main -ag=100
  • ./main --age 100
  • ./main --age=100

如果这个main.cc程序不是你写的,但是你知道他使用了gflags库,想看看它定义了哪些变量,则可以使用 --helpshort选项,就会显示这些变量的相关信息:

$ ./main --helpshort
main: Warning: SetUsageMessage() never called

Flags from /Users/self_study/Cpp/OpenSource/demo/main.cc:
  -age (age) type: uint32 default: 10
  -big_menu (Include 'advance' option in the menu listing) type: bool
    default: true
  -languages (comma-separated list of languages to offer in the 'lang' menu)
    type: string default: "english, french, chinese"

如果某个项目中使用了 gflags 定义了很多全局变量,分布在不同的文件中,你可以使用--helpfull选项来查看这些变量:

% ./main --helpfull 
main: Warning: SetUsageMessage() never called

  Flags from /Users/self_study/Cpp/OpenSource/demo/gflags/src/gflags.cc:
    -flagfile (load flags from file) type: string default: ""
    -fromenv (set flags from the environment [use 'export FLAGS_flag1=value'])
      type: string default: ""
    -tryfromenv (set flags from the environment if present) type: string
      default: ""
    -undefok (comma-separated list of flag names that it is okay to specify on
      the command line even if the program does not define a flag with that
      name.  IMPORTANT: flags in this list that have arguments MUST use the
      flag=value format) type: string default: ""

  Flags from /Users/self_study/Cpp/OpenSource/demo/gflags/src/gflags_completions.cc:
    -tab_completion_columns (Number of columns to use in output for tab
      completion) type: int32 default: 80
    -tab_completion_word (If non-empty, HandleCommandLineCompletions() will
      hijack the process and attempt to do bash-style command line flag
      completion on this value.) type: string default: ""

  Flags from /Users/self_study/Cpp/OpenSource/demo/gflags/src/gflags_reporting.cc:
    -help (show help on all flags [tip: all flags can have two dashes])
      type: bool default: false
    -helpfull (show help on all flags -- same as -help) type: bool
      default: false currently: true
    -helpmatch (show help on modules whose name contains the specified substr)
      type: string default: ""
    -helpon (show help on the modules named by this flag value) type: string
      default: ""
    -helppackage (show help on all modules in the main package) type: bool
      default: false
    -helpshort (show help on only the main module for this program) type: bool
      default: false
    -helpxml (produce an xml version of help) type: bool default: false
    -version (show version and build info and exit) type: bool default: false



  Flags from /Users/self_study/Cpp/OpenSource/demo/main.cc:
    -age (age) type: uint32 default: 10
    -big_menu (Include 'advance' option in the menu listing) type: bool
      default: true
    -languages (comma-separated list of languages to offer in the 'lang' menu)
      type: string default: "english, french, chinese"

好嘞,gflags库的使用就介绍到这。更多细节,自行阅读官方文档和源码。

reference

  1. https://gflags.github.io/gflags/

推荐阅读

  1. 踩坑记 (1) | std::sort函数隐藏着BUG,你知道吗
  2. 网络编程 (1) | 走近reactor设计模式,剖析高性能服务器框架核心
  3. 必先利其器(3) | 分享我所有的C++学习资源
  4. 必先利其器(2) | 手把手教你学会gdb,适应Linux调试环境
1
共 1 个回复

学会使用各种工具库,对入职后帮助很大,入职没那么多时间去学习了