gflags是google开源的一个解析命令行参数的工具。
最简单的demo
1 #include <iostream> 2 #include <gflags/gflags.h> 3 4 using namespace std; 5 6 DEFINE_string(confPath, "../conf/setup.ini", "program configure file."); 7 DEFINE_int32(port, 9090, "program listen port"); 8 DEFINE_bool(daemon, true, "run daemon mode"); 9 10 int main(int argc, char** argv) 11 { 12 gflags::ParseCommandLineFlags(&argc, &argv, true); 13 14 cout << "confPath = " << FLAGS_confPath << endl; 15 cout << "port = " << FLAGS_port << endl; 16 17 if (FLAGS_daemon) { 18 cout << "run background ..." << endl; 19 } 20 else { 21 cout << "run foreground ..." << endl; 22 } 23 24 cout << "good luck and good bye!" << endl; 25 26 gflags::ShutDownCommandLineFlags(); 27 return 0; 28 }
直接运行结果如下:
[amcool@leoox build]$ ./demo
confPath = ../conf/setup.ini
port = 9090
run background ...
good luck and good bye!
设定命令行参数
前面直接运行我们没有输入参数,所以用的就是上面写的那些默认参数。下面来看一下传入命令行参数的情况,主要有3种情况:
1、设定参数值
i)可以用 –参数名=参数值 或者 -参数名=参数值 的方式来设定参数值。
ii)对于bool类型的参数,除了上述方式外,还可以用 –参数名 的方式设定为true(即不带值), 使用 –no参数名 的方式设定为false。为了统一,建议都使用上面的 第 i 种方法来设定参数。
加入参数值运行一下:
[amcool@leoox build]$ ./demo --port=8888 --confPath=./setup.ini --daemon=true
confPath = ./setup.ini
port = 8888
run background ...
good luck and good bye!
[amcool@leoox build]$ ./demo -port=8888 -confPath=./setup.ini -daemon=false
confPath = ./setup.ini
port = 8888
run foreground ...
good luck and good bye!
[amcool@leoox build]$ ./demo -port=8888 -confPath=./setup.ini -daemon【对于bool型最好还是和上面统一直接指定值,不要用这种特殊方式】
confPath = ./setup.ini
port = 8888
run background ...
good luck and good bye!
[amcool@leoox build]$ ./demo -port=8888 -confPath=./setup.ini -nodaemon【对于bool型最好还是和上面统一直接指定值,不要用这种特殊方式】
confPath = ./setup.ini
port = 8888
run foreground ...
good luck and good bye!
[amcool@leoox build]$
2、从文件读入“命令行”参数
如果命令行参数很多,可以用 –flagfile=命令行文件 的方式传入命令行文件。
[amcool@leoox build]$ vi param.cmd
--port=8888
--confPath=./setup.ini
--daemon=true
[amcool@leoox build]$ ./demo --flagfile=param.cmd
confPath = ./setup.ini
port = 8888
run background ...
good luck and good bye!
[amcool@leoox build]$
3、从环境变量读入参数值
gflags另外还提供了 –fromenv 和 –tryfromenv 参数,通过这两个参数,程序可以从环境变量中获取到具体的值。
–fromenv 从环境变量读取参数值 –fromenv=port,confPath 表明要从环境变量读取port,confPath两个参数的值。但是当无法从环境变量中获取到的时候,会报错,同时程序退出。【注意:gflags的变量名是 FLAGS_我们定义的参数名】
–tryfromenv 与–fromenv类似,当参数的没有在环境变量定义时,不退出。
这种方式并不太常用,知道就可以了。
[amcool@leoox build]$ ./demo --fromenv=port,confPath
ERROR: FLAGS_confPath not found in environment
ERROR: FLAGS_port not found in environment
[amcool@leoox build]$ ./demo --tryfromenv=port,confPath
confPath = ../conf/setup.ini
port = 9090
run background ...
good luck and good bye!
[amcool@leoox build]$ export FLAGS_confPath=./loveyou.ini
[amcool@leoox build]$ export FLAGS_port=36888
[amcool@leoox build]$ env | grep FLAGS
FLAGS_port=36888
FLAGS_confPath=./loveyou.ini
[amcool@leoox build]$
[amcool@leoox build]$ ./demo --fromenv=port,confPath
confPath = ./loveyou.ini
port = 36888
run background ...
good luck and good bye!
[amcool@leoox build]$
支持的参数类型
使用gflags提供的宏:DEFINE_xxx(变量名,默认值,help_string)。这些变量需要在全局范围内定义。变量支持以下类型:
定义 | 类型 |
---|---|
DEFINE_bool | 布尔型 |
DEFINE_int32 | 32位整形 |
DEFINE_int64 | 64位整形 |
DEFINE_uint64 | 64位无符号整形 |
DEFINE_double | double型 |
DEFINE_string | C++中string类型 |
针对定义的宏进行一下参数解释:
1 DEFINE_string(confPath, "../conf/setup.ini", "program configure file.");
第一个字段 confPath就是命令行里要输入的参数名,比如 –confPath=./love.ini
第二个字段”../conf/setup.ini”,就是如果命令行里没指定这个参数,那默认值就是 ../conf/setup.ini
第三个字段”program configure file.”,就是这个参数的帮助说明信息,当用户输入 –hlep 的时候,会显示出来。
然后在代码中使用的变量就是:FLAGS_confPath
解析命令行参数
gflags是使用ParseCommandLineFlags这个方法来完成命令行参数的解析的。具体如下:
1 gflags::ParseCommandLineFlags(&argc, &argv, true);
一目了然,唯一值得注意的就是第三个参数了。如果设置为true,gflags就会移除解析过的参数。即argc, argv就会变了。否则gflags还会保持这些参数继续留在argc,argv中。但是参数的顺序有可能会发生变化。下面来看一个例子就清楚了:
1 #include <iostream> 2 #include <gflags/gflags.h> 3 4 using namespace std; 5 6 DEFINE_string(confPath, "../conf/setup.ini", "program configure file."); 7 DEFINE_int32(port, 9090, "program listen port"); 8 DEFINE_bool(daemon, true, "run daemon mode"); 9 10 int main(int argc, char** argv) 11 { 12 for (int i = 0; i < argc; i++) { 13 printf("argv[%d] = %s ", i, argv[i]); 14 } 15 printf("---------------here-------------- "); 16 17 gflags::SetVersionString("1.0.0.0"); 18 gflags::SetUsageMessage("Usage : ./demo "); 19 gflags::ParseCommandLineFlags(&argc, &argv, true); 20 21 for (int i = 0; i < argc; i++) { 22 printf("argv[%d] = %s ", i, argv[i]); 23 } 24 printf("---------------there-------------- "); 25 26 cout << "confPath = " << FLAGS_confPath << endl; 27 cout << "port = " << FLAGS_port << endl; 28 29 if (FLAGS_daemon) { 30 cout << "run background ..." << endl; 31 } 32 else { 33 cout << "run foreground ..." << endl; 34 } 35 36 cout << "good luck and good bye!" << endl; 37 38 gflags::ShutDownCommandLineFlags(); 39 return 0; 40 }
当第三个参数为true时,运行结果如下:
[amcool@leoox build]$ ./demo --port=8888 --confPath=./happy.ini --daemon
argv[0] = ./demo
argv[1] = --port=8888
argv[2] = --confPath=./happy.ini
argv[3] = --daemon
---------------here--------------
argv[0] = ./demo
---------------there--------------
confPath = ./happy.ini
port = 8888
run background ...
good luck and good bye!
当第三个参数为false时,运行结果如下:
[amcool@leoox build]$ ./demo --port=8888 --confPath=./happy.ini --daemon
argv[0] = ./demo
argv[1] = --port=8888
argv[2] = --confPath=./happy.ini
argv[3] = --daemon
---------------here--------------
argv[0] = ./demo
argv[1] = --port=8888
argv[2] = --confPath=./happy.ini
argv[3] = --daemon
---------------there--------------
confPath = ./happy.ini
port = 8888
run background ...
good luck and good bye!
参数检查
按照以前的习惯,我们可以获取到所有参数的值后,再在代码里面进行判断这个参数是否是我们想要的。比如,我们需要端口是36800 到 36888之间的,那我们可以这样检查。
1 if (FLAGS_port < 36800 || FLAGS_port > 36888) { 2 printf("port must [36800, 36888] "); 3 return -1; 4 }
gflags里面建议使用 RegisterFlagValidator 这个方法来做参数检查。参数不通过的时候,程序是启动失败的。什么时候会调用这个参数检查函数呢?
如果采用 static 全局变量来确保检查函数会在 main 开始时被注册,可以保证注册会在 ParseCommandLineFlags 函数之前。如果默认值检查失败,那么 ParseCommandLineFlag将会使程序退出。如果之后使用 SetCommandLineOption() 来改变参数的值,那么检查函数也会被调用,但是如果验证失败,只会返回 false,然后参数保持原来的值,程序不会结束。
1 #include <stdint.h> 2 #include <stdio.h> 3 #include <iostream> 4 5 #include <gflags/gflags.h> 6 7 // 定义对 FLAGS_port 的检查函数 8 static bool ValidatePort(const char* name, int32_t value) { 9 if (value > 0 && value < 32768) { 10 return true; 11 } 12 printf("Invalid value for --%s: %d ", name, (int)value); 13 return false; 14 } 15 16 /** 17 * 设置命令行参数变量 18 * 默认的主机地址为 127.0.0.1,变量解释为 'the server host' 19 * 默认的端口为 12306,变量解释为 'the server port' 20 */ 21 DEFINE_string(host, "127.0.0.1", "the server host"); 22 DEFINE_int32(port, 12306, "the server port"); 23 24 // 使用全局 static 变量来注册函数,static 变量会在 main 函数开始时就调用 25 static const bool port_dummy = gflags::RegisterFlagValidator(&FLAGS_port, &ValidatePort); 26 27 int main(int argc, char** argv) { 28 // 解析命令行参数,一般都放在 main 函数中开始位置 29 gflags::ParseCommandLineFlags(&argc, &argv, true); 30 std::cout << "The server host is: " << FLAGS_host 31 << ", the server port is: " << FLAGS_port << std::endl; 32 33 // 使用 SetCommandLineOption 函数对参数进行设置才会调用检查函数 34 gflags::SetCommandLineOption("port", "-2"); 35 std::cout << "The server host is: " << FLAGS_host 36 << ", the server port is: " << FLAGS_port << std::endl; 37 return 0; 38 }
运行结果如下:
#命令行指定非法值,程序解析参数时直接退出
➜ test ./gflags_test -port -2
Invalid value for --port: -2
ERROR: failed validation of new value '-2' for flag 'port'
# 这里参数默认值合法,但是 SetCommandLineOption 指定的值不合法,程序不退出,参数保持原来的值
➜ test ./gflags_test
The server host is: 127.0.0.1, the server port is: 12306
Invalid value for --port: -2
The server host is: 127.0.0.1, the server port is: 12306
其他代码文件使用参数变量
正常来说,代码可能不只有1个cpp,还会有很多模块。而每个模块可能会使用到不同的参数值。所以我们之前在demo.cpp定义的参数变量(比如FLAGS_port),在其他模块怎么引用和使用呢?so easy,与DEFINE相对应的有DECLARE。声明一下,就可以使用了。
1 DECLARE_bool: boolean 2 DECLARE_int32: 32-bit integer 3 DECLARE_int64: 64-bit integer 4 DECLARE_uint64: unsigned 64-bit integer 5 DECLARE_double: double 6 DECLARE_string: C++ string
判断flags变量是否被用户使用
在gflags.h中,还定义了一些平常用不到的函数和结构体。这里举一个例子,判断参数port有没有被用户设定过。
1 google::CommandLineFlagInfo info; 2 if(GetCommandLineFlagInfo("port" ,&info) && info.is_default) { 3 FLAGS_port = 27015; 4 }
版本号和帮助信息
在使用程序的时候,都离不开两个参数 –version 和 –help。来看看上面实现的demo能否支持呢?
[amcool@leoox build]$ ./demo --version
demo
[amcool@leoox build]$ ./demo --help
demo: Warning: SetUsageMessage() never called
Flags from /home/thrift/program/gflags/demo/demo.cpp:
-confPath (program configure file.) type: string
default: "../conf/setup.ini"
-daemon (run daemon mode) type: bool default: true
-port (program listen port) type: int32 default: 9090
help支持了,但是version没支持,而且help信息里面还有waring。可以用 SetVersionString() 和 SetUsageMessage() 方法来满足需求。
注意:SetVersionString() 和 SetUsageMessage() 一定要在 ParseCommandLineFlags() 之前设定。
修改后的代码如下:
1 #include <iostream> 2 #include <gflags/gflags.h> 3 4 using namespace std; 5 6 DEFINE_string(confPath, "../conf/setup.ini", "program configure file."); 7 DEFINE_int32(port, 9090, "program listen port"); 8 DEFINE_bool(daemon, true, "run daemon mode"); 9 10 int main(int argc, char** argv) 11 { 12 gflags::SetVersionString("1.0.0.0"); 13 gflags::SetUsageMessage("Usage : ./demo "); 14 gflags::ParseCommandLineFlags(&argc, &argv, true); 15 16 cout << "confPath = " << FLAGS_confPath << endl; 17 cout << "port = " << FLAGS_port << endl; 18 19 if (FLAGS_daemon) { 20 cout << "run background ..." << endl; 21 } 22 else { 23 cout << "run foreground ..." << endl; 24 } 25 26 cout << "good luck and good bye!" << endl; 27 28 gflags::ShutDownCommandLineFlags(); 29 return 0; 30 }
运行结果如下:
[amcool@leoox build]$ ./demo --version
demo version 1.0.0.0
[amcool@leoox build]$ ./demo --help
demo: Usage : ./demo
Flags from /home/amcool/program/gflags/demo/demo.cpp:
-confPath (program configure file.) type: string
default: "../conf/setup.ini"
-daemon (run daemon mode) type: bool default: true
-port (program listen port) type: int32 default: 9090
Flags from /home/amcool/soft/gflags-2.1.1/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 /home/amcool/soft/gflags-2.1.1/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 /home/amcool/soft/gflags-2.1.1/src/gflags_reporting.cc:
-help (show help on all flags [tip: all flags can have two dashes])
type: bool default: false currently: true
-helpfull (show help on all flags -- same as -help) type: bool
default: false
-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
[amcool@leoox build]$
本文参考自:
http://blog.csdn.net/u013407923/article/details/53084076
http://blog.csdn.net/jcjc918/article/details/50876613
http://blog.csdn.net/lezardfu/article/details/23753741
http://www.leoox.com/?p=270
http://www.leoox.com/?p=275