Google Commandline Flags使用说明

命令行flags是在运行命令行时用户指定的标志,在命令fgrep -l -f /var/tmp/foo johannes brahms中,-l-f /var/tmp/foo为命令行flags,不带横线johannesbrahms为命令行参数

Gflags是Google的命令行flags库,与getopt()不同, 其flag定义可分散在源码中,而不是只能在例如main()函数中,这意味着一个代码文件中的定义和使用的flags对其他文件也其作用。二和玲姐到该源码文件的应用都能得到flags,glags库会自动地处理flags

这在灵活性方面具有很重要意义,可减少代码的重用。但是,当两个文件都定义了相同flag,当二者链接时会显示错误

定义

定义:对你想要的flag类型使用适当的宏,支持的类型包括:

  • DEFINE_bool: boolean
  • DEFINE_int32: 32-bit integer
  • DEFINE_int64: 64-bit integer
  • DEFINE_uint64: unsigned 64-bit integer
  • DEFINE_double: double
  • DEFINE_string: C++ 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");

没有像目录类似的复杂类型,languages的flag只是字符串列表,只是定义为字符串。最好只使用简单的类型作为flags

**所有的DEFINE宏都使用相同的三个参数:flag名字,默认值,以及描述其作用的help说明。当用户运行--help flag时,help字符串内容就会显示

只能定义flag一次,如果想要在多个源码文件中使用flags,则在一个文件中DEFINE,其他文件DECLARE。更好的,可在foo.cc中定义,foo.h中声明DECLARE,则所用include其头文件的代码,都可使用其flags

如果想更新库中flags默认值,可使用flag validator更新无效的默认值

获取Flag

所有已定义的flags对于程序而言只是普通变量而已,使用前缀FLAGS_。在上述领子中,宏定义了两个变量FLAGS_big_menu(a bool)与FLAGS_languages(a C++string)

类似其他变量,读写其他变量:

        if (FLAGS_consider_made_up_languages)
            FLAGS_languages += ",klingon";  // implied by --consider_made_up_languages
        if (FLAGS_languages.find("finnish") != string::npos)
            HandleFinnish();

声明:在其他文件中使用Flag

在bar.cc文件中,使用刚才定义的big_menu变量,即DECLARE_bool(big_menu),其与extern FLAGS_big_menu作用相同

这种外部声明方式表明了二者文件之间的依赖性,但是这种隐含的依赖在大型项目中是很难管理的,因此推荐使用以下方式:如果在foo.cc中定义了flag,则只在foo.h中DECLARE,或者在紧密相关的测试中DECLARE

RegisterFlagValidator:检查Flag值

在定义了flag之后,可通过flag来register validator函数。当flag从命令行中读取后,无论使用SetCommandLineOption()改变其值,validator函数可调用作为参数的最新值,如果该flag有效,则返回true,错误则相反。如果这函数对新设置的flag报错,则flag保持当前值

        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");
        static const bool port_dummy = RegisterFlagValidator(&FLAGS_port, &ValidatePort);

在使用全局初始化下register,必须保证在registration完成

合并:怎么是建立Flags

其使用与getopt()相似,但是更方便。主要使用一个函数 gflags::ParseCommandLineFlags(&argc, &argv, true)完成命令行参数。第三个参数表示remove_flags,如果设置为true,则ParseCommandLineFlags从argv中移除从flags与 其参数,且适当修改argc。在这例子中,在调用函数之后,argv只有命令行参数,没有命令行flag。

另一方面,如果remove_flags为false,ParseCOmmandLineFlags会使argc保持不变,但是重新排列在argv中的参数让flags都在开始位置。

命令行设置Flags

创建flag而不是编译时保持不变,是为了让用户在命令行可指定非默认值。有一种非典型的方式设置boolean flag:在其名字之前放置“no”。有许多种方可指定flags:

  • app_containing_foo --languages="chinese,japanese,korean"
  • app_containing_foo -languages="chinese,japanese,korean"
  • app_containing_foo --languages "chinese,japanese,korean"
  • app_containing_foo -languages "chinese,japanese,korean"

对于boolean flags有一些不同:

  • app_containing_foo --big_menu
  • app_containing_foo --nobig_menu
  • app_containing_foo --big_menu=true
  • app_containing_foo --big_menu=false

**尽管有许多方式,但是推荐只使用一种形式:非boolean flags--variable=value与boolean flags--variable/--novariable。统一性会使代码更可读,且更符合要求

getopt()相同,--会终止flags处理。因此,foo -f 1 -- -f2 2,f1会被视为flag,而f2不是

如果一个flag在命令行中重复中指定了多次,则只使用最后一个,其他的都被忽略

修改默认Flag值

有时flag在库中被定义,如果想要在一个应用中改变其默认值,但在其他应用中不改变其默认值。只要在main()中在调用ParseCommmandLineFlags()之前对其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(...);
           }

在此例中,用户仍然可在命令行中设置flag值,但是如果不设置,则flag默认值为true

特殊Flags

有一些flags在命令行模块中已定义,可对所有应用使用这些commandlineflags,主要有三类。第一类为reporting flags,使应用打印自身相关信息,如--help,第二类为影响其他flags解析的flags,如--undefok,第三类为recursive flags,来设置其他flags值,如-fromenv