google gflags使用指南
google gflags是google使用的一个开源库,用于解析命令行标记。目前的gflags有C++和Python两个版本。本文主要介绍gflags的C++版本使用方法。
命令行标记是指用户在运行可执行程序时,在命令行中指定的参数。例如,对于命令:
fgrep -l -f /var/tmp/foo johannes brahms
则-l及-f /var/tmp/foo是两个命令行标记,而johnnes和brahms则是命令行参数。一个应用往往具有许多个命令行标记,对应这些标记,可以传入一些由用户指定的参数。例如,在上面的例子中,-l标记没有任何参数,而-f标记的参数则是/var/tmp/foo。
这些标记和参数存储在main函数的argc和argv参数中。一般而言,main函数总有如下的形式:
int main(int argc, char ** argv) {…}
其中argc指明了argv数组的长度,而argv数组则空格为分割,存储命令行后用户输入的参数。在GNU C Library中,这个工作主要是由getopt函数来完成的。以下是getopt函数的一个例子:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main (int argc, char **argv)
{
int aflag = 0;
int bflag = 0;
char *cvalue = NULL;
int index;
int c;
opterr = 0;
while ((c = getopt (argc, argv, "abc:")) != -1)
switch (c)
{
case 'a':
aflag = 1;
break;
case 'b':
bflag = 1;
break;
case 'c':
cvalue = optarg;
break;
case '?':
if (optopt == 'c')
fprintf (stderr, "Option -%c requires an argument.\n", optopt);
else if (isprint (optopt))
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf (stderr,
"Unknown option character `\\x%x'.\n",
optopt);
return 1;
default:
abort ();
}
printf ("aflag = %d, bflag = %d, cvalue = %s\n",
aflag, bflag, cvalue);
for (index = optind; index < argc; index++)
printf ("Non-option argument %s\n", argv[index]);
return 0;
}
在这个例子中可以看出,所有的标记的定义都必须要集中在一个地方处理(也就是switch代码段中)。而gflags则与getopt函数不同,在gflags中,标记的定义分散在源代码中,不需要列举在一个地方。这意味着单个的源代码文件可以定义他们自己所使用的标记,一切连接到这个文件的应用将获取这个标记,并且gflags将自动对这个标记进行适当的处理。这对于软件的灵活性及代码的重用是很有好处的。当然,这也产生了一定的风险,特别是,两个不同的文件可能会定义拥有同一个名字的命令行标记,在平常的情况下没有问题,但当这两个文件被同一个二进制程序连接的时候,就会出现错误。
gflags使用C++语言实现,其风格也是C++风格的。本文的其余部分介绍了如何使用gflags。
一、如何定义一个命令行标记
使用gflags需要引用gflags头文件:
#include <gflags/gflags.h>
在gflags.h文件的结尾,定义了一些标记定义宏,因此在引用了gflags.h后,定义一个命令行标记就变得十分简单了。如,在foo.cc文件的开头,我们引入如下的命令行标记的定义:
#include <gflags/gflags.h>
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");
在这段代码中,我们定义了两个命令行标记:bool型的big_menu,以及string型的language。所有的DEFINE宏都有三个参数:标记的名字,默认值,以及针对这个标记的说明。第三个参数是非常有用的,尤其是在构建大型程序的时候,在命令行中使用—help标记可以列出被gflags所定义的所有标记的类型、默认值及对其的说明(也就是宏第三个参数)。
DEFINE宏支持的类型包括:
o DEFINE_bool: boolean
o DEFINE_int32: 32-bit integer
o DEFINE_int64: 64-bit integer
o DEFINE_uint64: unsigned 64-bit integer
o DEFINE_double: double
o DEFINE_string: C++ string
可以在可执行程序的任何一个源文件中定义一个标记,而针对每个标记,只需要定义一次。如果想要在不同的源文件中访问该标记,可以使用DECLARE宏来声明这个标记(其实就是声明了一个extern变量的引用)。一个更好的方法是,对于一个标记,可以在foo.cc中定义它,并且在foo.h中声明它。这样,包含了foo.h的所有源文件都可以引用这个标记。
二、如何访问一个标记
访问标记较为简单。只要对标记进行了定义或者声明,那么只需要在使用它的地方加上前缀“FLAGS_”就可以引用这个标记了。如,
if (FLAGS_consider_made_up_languages)
FLAGS_languages += ",klingon"; // implied by --consider_made_up_language
if (FLAGS_languages.find("finnish") != string::npos)
HandleFinnish();
三、在命令行中使用标记
对于上面的foo.cc和foo.h,如果其编译后产生的二进制文件为app_containing_foo,则在命令行中设置这些标记的方法为:
app_containing_foo --nobig_menu -languages="chinese,japanese,korean" …
这个命令会把big_menu设置为false,并且将language设置为“chinese,japanese,Korean”。对于string、int、double等类型的标记,设置标记时使用如下形式:
o app_containing_foo --languages="chinese,japanese,korean"
o app_containing_foo -languages="chinese,japanese,korean"
o app_containing_foo --languages "chinese,japanese,korean"
o app_containing_foo -languages "chinese,japanese,korean"
这些方式是等价的。但注意gflags不支持短标记与合并标记。如ls –la这种类型,是不能够被gflags处理的。这和getopt函数有所不同。
需要注意的还有以下几点:
1,--连用会终止标记。如命令:foo -f1 1 -- -f2 2,则f2标记将不被处理;
2,bool型的标记,如big_menu,既可以使用—big_menu=true/false,也可以使用—big_menu/--nobig_menu这种方式。gflags都可以解析。
3,使用未被定义的标记将引发严重错误。
四、在程序中解析标记
在程序中解析标记可以使用如下函数:
google::ParseCommandLineFlags(&argc, &argv, true);
注意argc和argv均以地址方式传递,也就是该函数可以改变argc和argv的内容。通常这段代码会出现在main函数的开始。最后一个参数是移除标记,它指明了是否将被gflags成功解析的标记从参数列表中移除。例如,对于命令行:
foo –options=start file1 file2
在这个命令行中,options是一个命令行标记,将被gflags解析(如果它已经被定义了);而file1和file2则是命令行参数。如果设置remove标记位ture,则—options=start会在调用ParseCommandLineFlags后被移除,此时argc=2,而argv中则保存了file1和file2两个参数。如果remove标记位设为false,则—options=start将不会被移除,则argc不会改变,但argv会重新排列,将所有的标记排列在前面。