• gflags的使用实例(转载)


    参考:http://blog.csdn.net/lezardfu/article/details/23753741

    安装gflag
    从官方地址http://code.google.com/p/google-gflags/下载gflags并安装。比如我下载的是1.5版本。
    [yichi@yichi tmp]$ tar zxvf gflags-1.5.tar.gz
    [yichi@yichi tmp]$ cd gflags
    [yichi@yichi gflags-1.5]$ ./configure –prefix=$HOME/google-library
    [yichi@yichi gflags-1.5]$ make j=4 & make install

    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
    #include
    #include
    #include
     
    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 = %sn",
    aflag, bflag, cvalue);
     
    for (index = optind; index < argc; index++)
    printf ("Non-option argument %sn", 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
    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所定义的所有标记的类型、默认值及对其的说明(也就是宏第三个参数)。

    gflags主要支持的参数类型包括bool,int32, int64, uint64, double, string等,定义参数通过DEFINE_type宏实现,如下所示,分别定义了一个bool和一个string类型的参数,该宏的三个参数含义分别为命令行参数名,参数默认值,以及参数的帮助信息。

    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"); 

    gflag不支持列表,用户通过灵活借助string参数实现,比如上述的languages参数,可以类型为string,但可看作是以逗号分割的参数列表。

    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();

    仅在参数定义和访问在同一个文件(或是通过头文件包含)时,FLAGS_name才能访问到参数,如果要访问其他文件里定义的参数,则需要使用DECLARE_type。比如在foo.cc中DEFINE_string(color, "red", "the color you want to use"); 这是如果你需要在foo_test.cc中使用color这个参数,你需要加入DECLARE_string(color, "red", "the color you want to use");

    三、在命令行中使用标记

    对于上面的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,使用未被定义的标记将引发严重错误。

    编译成可执行文件之后,用户可以使用:executable --参数1=值1 --参数2=值2 ... 来为这些命令行参数赋值。

    ./mycmd --var1="test" --var2=3.141592654 --var3=32767 --mybool1=true --mybool2 --nomybool3
    
    
    
    这里值得注意的是bool类型命令行参数,除了可以使用--xxx=true/false之外,还可以使用--xxx和--noxxx后面不加等号的方式指定true和false

    四、在程序中解析标记

    在程序中解析标记可以使用如下函数:

     
    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会重新排列,将所有的标记排列在前面。

    argc和argv想必大家都很清楚了,说明以下第三个参数的作用:
    
    如果设为true,则该函数处理完成后,argv中只保留argv[0],argc会被设置为1。
    
    如果为false,则argv和argc会被保留,但是注意函数会调整argv中的顺序。

    参数检查 
    定义参数后,可以给参数注册一个检查函数(validator),当从命令行指定参数或通过SetCommandLineOption()指定参数时,检查函数就会被调用,两个参数分别为命令行参数名,以及设置的参数值。 

    static bool ValidatePort(const char* flagname, int32 value) { 
       if (value > 0 && value < 32768)   // value is ok 
         return true; 
       printf("Invalid value for --%s: %d
    ", flagname, (int)value); 
       return false; 
    } 
    DEFINE_int32(port, 0, "What port to listen on"); 
    static const bool port_dummy = RegisterFlagValidator(&FLAGS_port, &ValidatePort); 


    建议在定义参数后,立即注册检查函数。RegisterFlagValidator()在检查函数注册成功时返回true;如果参数已经注册了检查函数,或者检查函数类型不匹配,返回false。

    初始化参数 
    在引用程序的main()里通过 google::ParseCommandLineFlags(&argc, &argv, true); 即完成对gflags参数的初始,其中第三个参数为remove_flag,如果为true,gflags会移除parse过的参数,否则gflags就会保留这些参数,但可能会对参数顺序进行调整。 比如 "/bin/foo" "arg1" "-q" "arg2"  会被调整为 "/bin/foo", "-q", "arg1", "arg2",这样更好理解。



    gflags进阶使用:
      1. 在其他文件中使用定义的flags变量:有些时候需要在main之外的文件使用定义的flags变量,这时候可以使用宏定义DECLARE_xxx(变量名)声明一下(就和c++中全局变量的使用是一样的,extern一下一样)
        • DECLARE_bool: boolean
        • DECLARE_int32: 32-bit integer
        • DECLARE_int64: 64-bit integer
        • DECLARE_uint64: unsigned 64-bit integer
        • DECLARE_double: double
        • DECLARE_string: C++ string
          在gflags的doc中,推荐在对应的.h文件中进行DECLARE_xxx声明,需要使用的文件直接include就行了。
          
      2. 检验输入参数是否合法:gflags库支持定制自己的输入参数检查的函数,如下:
        static bool ValidatePort(const char* flagname, int32 value) {
           if (value > 0 && value < 32768)   // value is ok
             return true;
           printf("Invalid value for --%s: %d
        ", flagname, (int)value);
           return false;
        }
        DEFINE_int32(port, 0, "What port to listen on");
        static const bool port_dummy = RegisterFlagValidator(&FLAGS_port, &ValidatePort);
      3. 判断flags变量是否被用户使用:在gflags.h中,还定义了一些平常用不到的函数和结构体。这里举一个例子,判断参数port有没有被用户设定过
            google::CommandLineFlagInfo info;
            if(GetCommandLineFlagInfo("port" ,&info) && info.is_default) {
                FLAGS_port = 27015;
            }
      4. 定制你自己的help信息与version信息:(gflags里面已经定义了-h和--version,你可以通过以下方式定制它们的内容)
        • version信息:使用google::SetVersionString设定,使用google::VersionString访问
        • help信息:使用google::SetUsageMessage设定,使用google::ProgramUsage访问
        • 注意:google::SetUsageMessage和google::SetVersionString必须在google::ParseCommandLineFlags之前执

    特殊参数

    • --help 打印定义过的所有参数的帮助信息
    • --version 打印版本信息 通过google::SetVersionString()指定
    • --nodefok  但命令行中出现没有定义的参数时,并不退出(error-exit)
    • --fromenv 从环境变量读取参数值 --fromenv=foo,bar表明要从环境变量读取foo,bar两个参数的值。通过export FLAGS_foo=xxx; export FLAGS_bar=yyy 程序就可读到foo,bar的值分别为xxx,yyy。
    • --tryfromenv 与--fromenv类似,当参数的没有在环境变量定义时,不退出(fatal-exit)
    • --flagfile 从文件读取参数值,--flagfile=my.conf表明要从my.conf文件读取参数的值。在配置文件中指定参数值与在命令行方式类似,另外在flagfile里可进一步通过--flagfile来包含其他的文件。
  • 相关阅读:
    软考倒计时2天
    案例分析
    冒泡排序
    二分查找
    MySQL 错误 1366:1366 Incorrect integer value
    linux(centos) 添加系统环境变量
    php的opcache缓存扩展
    启动mysqld报 mysql the server quit without updating pid file
    mysql5.5 报Can't open and lock privilege tables: Table 'mysql.host' doesn't exist
    关于git CRLF LF结尾的问题
  • 原文地址:https://www.cnblogs.com/airlove/p/4689181.html
Copyright © 2020-2023  润新知