目录
一、Gflags简介
二、下载和安装
三、用CMake声明flags之间的依赖关系
四、定义flag
五、flag变量
六、在其他文件中调用flag变量
七、完整性检查——RegisterFlagValidator
八、flag与参数
九、命令行设置flag
十、更改flag默认值
一、Gflags简介Gflags是一种命令行解析工具,主要用于解析用命令行执行可执行文件时传入的参数。与getops()不同的是,在gflags中flag可以分散的定义在各个文件之中,而不用定义在一起,这就意味着在我们可以在一个单独的文件中只定义这个文件所需要用到的一些flag,链接了该文件应用都可以使用该文件中的flag,这样就能非常方便的实现代码的复用,如果不同的文件定义了相同的flag,则会产生错误,所以需要明确规范gflags的使用规范。
二、下载和安装具体可以参考github:
https://github.com/gflags/gflags/blob/master/INSTALL.mdgithub.com/gflags/gflags/blob/master/INSTALL.md
三、用CMake声明flags之间的依赖关系具体可以参考官方文档:
How To Use Gflags (formerly Google Commandline Flags)gflags.github.io/gflags/
四、定义flag使用gflags定义一个flag是非常简单的,只需要使用相应类型对应的宏定义就可以(该宏定义在gflags/gflags.h中),例如
#include
DEFINE_bool(big_menu, true, "Include 'advanced' options in the menu listing");
DEFINE_string(languages, "english,french,german",
"comma-separated list of languages to offer in the 'lang' menu");
其中DEFINE_bool用于定义一个bool类型的flag,gflags还支持其他类型的定义
DEFINE_string:C++string类型
DEFINE_int32:32位整形
DEFINE_int64:64位整形
DEFINE_uint64:64位无符号整形
DEFINE_double:double类型
DEFINE_bool:bool类型
我们在flag中定义简单类型的变量,对于那些复杂的变量,不要放在flag中进行定义和解析,复杂的类型需要用专门的解析工具来进行解析。
上述的DEFINE宏包含有三个参数:1、flag的命名;2、flag的默认值;3、该flag对应的一些提示性说明(当用户传入—help 标志的时候向用户展现提示性说明)。只可以在一个文件中对某个flag进行定义,一旦在一个文件中给出定义召之后,在其他文件中想要使用该flag,需要使用DECLARE宏来进行声明,通常在.cc文件中给出flag的定义,在.h文件中进行DECLARE声明,然后任何#include该.h文件的用户可以使用该flag
五、flag变量用DEFINE宏定义的flag都可以像普通的变量一样进行调用,定义的变量是以FLAGS_为前缀,如上述例子中定义了两个变量FLAGS_big_menu和FLAGS_languages,可以像普通的变量一样对其进行读写
if (FLAGS_consider_made_up_languages)
FLAGS_languages += ",klingon"; // implied by --consider_made_up_languages
if (FLAGS_languages.find("finnish") != string::npos)
HandleFinnish();
gflag.h头文件中也给出了具体的get、set函数进行读写
六、在其他文件中调用flag变量在另外一个文件中如果想要使用上述定义的big_menu,只需要在该文件的头部进行如下声明
DECLARE_bool(big_menu);
上述声明从功能上等价于
extern FLAGS_big_menu;
然而上述这种extern的声明方式表明了当前文件与flag定义文件之间的依赖关系,这种隐式的依赖关系不利于在大型工程项目中使用,所以官方给出了如下的使用规则:如果在.cc文件中定义了一个flag,要么不进行声明(除了.cc文件中没有其他文件使用该flag),要么只在测试需要用到的文件中或者在.h头文件中进行DECLARE进行声明。
如果其他文件需要使用定义好的flag,就在.h头文件中进行DECLARE声明,然后只需要#include该头文件即可,这样是一种显示的依赖关系,并且该flag成为一个全局变量
七、完整性检查——RegisterFlagValidator在定义一个flag之后,可以选择定义一个验证器来对该flag进行校验,每当利用 SetCommandLineOption()函数更改flag的值得时候,都会利用该验证器对flag的值进行合理性验证,当值是合理的时候,验证器会返回true;当值是不合理的时候,验证器会返回false并报错,具体可以参考如下例子
static bool ValidatePort(const char* flagname, int32 value) {
if (value > 0 && value < 32768) // value is ok
return true;
printf("Invalid value for --%s: %d\n", flagname, (int)value);
return false;
}
DEFINE_int32(port, 0, "What port to listen on");
DEFINE_validator(port, &ValidatePort);
代码最后一行利用DEFINE_validator()调用RegisterFlagValidator()来对validator进行注册,如果注册成功会返回true,如果注册失败的话会返回false,这里失败的原因可能有两点:1、第一个参数不是一个定义好的flag;2、该flag已经注册了其他的验证器。该验证器的命名方式为_validator_registered.
八、flag与参数定义好参数后,最后要告诉执行程序去处理命令行传入的参数,使得定义的FLAG参数得到正确赋值。通常利用以下方式进行调用
gflags::ParseCommandLineFlags(&argc, &argv, true);
前两个参数通常户会在main()函数中给出,最后一个参数是remove_flags,如果为true的话,ParseCommandLineFlags会移除相应的flag和对应的参数并且修改相应的argc,然后argv只会保留命令行参数;繁殖如果remove_flags为false的话,会保持argc不变,但是会调整argv中存储内容的顺序,并且把flag放在命令行参数的前面
九、命令行设置flag我们定义flag的原因就是能够通过命令行传递一个值并且赋给相应的flag,而不是定义一个常量。gflags为我们提供了很多形式来给flag传递参数,但是建议采用如下方式
对于非bool类型的flag
--variable=value
对于bool类型的flag
--variable/--novariable
十、更改flag默认值
如果需要在一个应用中给该flag的默认值,而在其他应用中不更改,可以在调用ParseCommandLineFlags():之前给flag赋一个新的值
DECLARE_bool(lib_verbose); // mylib has a lib_verbose flag, default is false
int main(int argc, char** argv) {
FLAGS_lib_verbose = true; // in my app, I want a verbose lib by default
ParseCommandLineFlags(...);
}