GFlags使用文档
GFlags是Google开源的一个命令行flag(区别于参数)库。和 getopt() 之类的库不同,flag的定义可以散布在各个源码中,而不用放在一起。一个源码文件可以定义一些它自己的flag,链接了该文件的应用都能使用这些flag。这样就能非常方便地复用代码。如果不同的文件定义了相同的flag,链接时会报错。
GFlags是一个C++库,同时也有一个Python移植,使用完全相同的接口。
目录
使用CMake链接GFlags
最新版本的GFlags已经可以支持CMake了。安装GFlags之后,可以在CMake中以下面方式使用:
find_package (gflags REQUIRED) include_directories (${gflags_INCLUDE_DIR})add_executable (foo main.cc) target_link_libraries (foo gflags)
如果你和我一样是从包管理器安装的,可以准备一个 FindGFlags.cmake 放到CMake的模块路径中,然后类似地使用(注意大小写有些区别,下面的更符合CMake的命名习惯):
find_package (GFlags REQUIRED) include_directories (${GFLAGS_INCLUDE_DIR})add_executable (foo main.cc) target_link_libraries (foo gflags)
DEFINE: 在程序中定义flag
定义flag只需使用你想要的类型的对应的宏即可,这些宏定义在 gflags/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");
支持的类型有:
- 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
没有列表之类的复杂类型,因此例子中的“languages”flag定义为string类型,而不是string列表之类的。这样保证了库的设计的简单。
DEFINE 宏有三个参数:flag名、默认值、描述使用方法的帮助。帮助会在执行 --help flag时显示。
可以在任何源文件中定义flag,但是每个只能定义一次。如果需要在多处使用,那么在一个文件中 DEFINE ,在其他文件中 DECLARE 。比较好的方法是在 .cc 文件中 DEFINE ,在 .h 文件中 DECLARE ,这样包含头文件即可使用flag了。
在库中定义flag很好用,但是也有些问题。比如一个库可能没有flag的合适的默认值。解决办法是可以使用flag验证器在没有有效flag值的时候给出提示。
注意: DEFINE_foo 和 DECLARE_foo 是全局命名空间的。
使用flag
定义的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();
也可以使用 gflags.h 中的特殊函数读写flag,不过不太常用。
DECLARE: 在不同文件中使用flag
上面的方法只能在同一文件中前面定义了flag的情况下使用flag,否则会报“unknown variable”错误。
在不同文件中使用flag可以通过 DECLARE_type 宏来做到。比如,如果想在 bar.cc 文件中使用 big_menu flag,可以在文件开始加上:
DECLARE_bool(big_menu);
这和 extern FLAGS_big_menu 是等效的。
问题是这会在两个文件间加上依赖关系,对于较大的项目这会导致管理困难。所以这里有个原则:如果在 foo.cc 中 DEFINE 了一个flag,那么或者不 DECLARE 它,或者只在对应的测试中 DECLARE ,或者只在 foo.h 中 DECLARE 。
RegisterFlagValidator: 验证flag值
你可能想给定义的flag注册一个验证函数。这样当flag从命令行解析,或者值被修改(通过调用 SetCommandLineOption() ),验证函数都会被调用。验证函数应该在flag值有效时返回true,否则返回false。如果对新设置的值返回false,flag保持当前值;如果对默认值返回false, ParseCommandLineFlags 会失败。
举个例子:
static bool ValidatePort(const char* flagname, int32 value) {if (value > 0 && value < 32768) // value is okreturn 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);
在全局初始化时注册( DEFINE 之后),这样就在解析命令行之前执行。
注册成功 RegisterFlagValidator() 返回true。否则返回false:a) 第一个参数不是命令行flag,b) 已经注册了另一个验证器。
生成flag
最后,还需要解析命令行。和 getopt() 类似,但是简单得多:
google::ParseCommandLineFlags(&argc, &argv, true);
通常把它放在 main() 的开始处,传入的 argc 和 argv 参数可能被修改。
最后一个参数如果为true, ParseCommandLineFlags 会从 argv 删除flag,修改 argc ,最后只剩下命令行参数。
相反如果为false, argc 不会修改, argv 会被重新排列,flag在前,参数在后, ParseCommandLineFlags 会返回 argv 中第一个命令行参数的位置,即最后一个flag的后一个位置。
根据命令行的解析,修改 FLAGS_* 变量。
设置命令行flag
一般使用flag的原因是为了能在命令行指定一个非默认值。以 foo.cc 为例,可能的用法是:
app_containing_foo --nobig_menu -languages="chinese,japanese,korean" ...
执行 ParseCommandLineFlags 会设置 FLAGS_big_menu = false , FLAGS_languages = "chinese,japanese,korean" 。
注意这种在名字前面加“no”的设置布尔flag为false的语法。
设置“languages”flag的方法有:
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"
布尔flag稍有不同:
app_containing_foo --big_menu app_containing_foo --nobig_menu app_containing_foo --big_menu=true app_containing_foo --big_menu=false还包括以上这些的单短线的变种
建议只使用一种形式:非布尔flag, --variable=value ;布尔flag, --variable/--novariable 。保持一致性有一定的好处。
在命令行使用未定义的flag会在执行时失败。如果需要允许未定义的flag,可以使用 --undefok 来去掉报错。
和 getopt() 一样, -- 可以用于结束flag。
重复指定flag使用最后的一个。
不支持单字母的形式的flag,也不支持单短线后的flag合并,像 ls -la 那样。
更改默认的flag值
对于定义在库中的flag,有时我们想要在单独一个应用中改变它的默认值。很简单,只要在 ParseCommandLineFlags() 前面设定一个新的值即可:
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 defaultParseCommandLineFlags(...); }
对于上面的应用中,flag的默认值被改为true。
特殊flag
GFlags中默认定义了一些flag。有三类,第一类是“报告”flag,用于打印一些信息然后退出。
--help | 显示所有文件的所有flag,按文件、名称排序,显示flag名、默认值和帮助 |
--helpfull | 和 --help 相同,显示全部flag |
--helpshort | 只显示执行文件中包含的flag,通常是 main() 所在文件 |
--helpxml | 类似 --help,但输出为xml |
--helpon=FILE | 只显示定义在 FILE.* 中得flag |
--helpmatch=S | 只显示定义在 *S*.* 中的flag |
--helppackage | 显示和 main() 在相同目录的文件中的flag |
--version | 打印执行文件的版本信息 |
第二类是可以影响其他flag的。
--undefok=flagname,flagname,... --undefok 后面列出的flag名,可以在无定义的情况下忽略而不报错
第三类是“递归”flag,可以用来设置其他flag: --fromenv, --tryfromenv, --flagfile 。
--fromenv
--fromenv=foo,bar 表示从环境变量中读取 foo 和 bar flag。需要在环境中预先设置对应的值:
export FLAGS_foo=xxx; export FLAGS_bar=yyy # sh setenv FLAGS_foo xxx; setenv FLAGS_bar yyy # tcsh
等价于在命令行指定 --foo=xxx --bar=yyy 。
如果在应用中没有定义 foo ,或者环境变量中没有定义 FLAGS_foo ,使用 --fromenv=foo 会导致失败。
--tryfromenv
--tryfromenv 和 --fromenv 类似,区别是在环境变量中没有定义 FLAGS_foo 时, --tryfromenv=foo 不会导致失败,这时会使用定义时指定的默认值。但是应用中没有定义 foo 仍会导致失败。
--flagfile
--flagfile=f 表示从文件 f 中读取flag。
对于简单形式,文件 f 中每行一个flag。在flagfile文件中flag需要使用等号。如:
# /tmp/myflags --nobig_menus --languages=english,french
以下两种方式是等价的:
./myapp --foo --nobig_menus --languages=english,french --bar ./myapp --foo --flagfile=/tmp/myflags --bar
注意在flagfile中很多类型的错误会被忽略掉,比如不能识别的flag,没有指定值的flag。
一般形式的flagfile要复杂一些。写成一组文件名,每行一个,后面加上一组flag,每行一个的形式,可以有多组。文件名可以使用通配符( * 和 ? ),只有当前可执行模块名和其中一个文件名匹配时才会处理文件名后的flag。flagfile可以直接以一组flag开始,这时这些flag对应到当前可执行模块。
以 # 开头的行作为注释被忽略,前导空白和空行也都会被忽略。
flagfile中还可以使用 --flagfile flag来包含另一个flagfile。
flag会按顺序执行。从命令行开始,遇到flagfile时,执行文件,执行完再继续命令行中后面的flag。
其他一些细节
除以上的方法,还可以直接通过API来读取flag,以及它的默认值和帮助等信息。 FlagSaver 可以用来修改flag和自动撤销修改。还有一些读取 argv 的方法, SetUsageMessage() 和 SetVersionString 等等。可以参考 gflags.h。
如果加上:
#define STRIP_FLAG_HELP 1 // this must go before the #include! #include <gflags/gflags.h>
可以去掉帮助信息。
http://www.yeolar.com/note/2014/12/14/gflags/
GFlags使用文档相关推荐
- makefile:中文版最权威的makefile文档( 转载 )
跟我一起写 Makefile 陈皓 (CSDN) 概述 -- 什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个 ...
- 导出swagger2生成的文档
百度了好多篇用法,没法用.特此记录一下 一.下载项目 下载https://github.com/Swagger2Markup/spring-swagger2markup-demo下的项目,保存,注意文 ...
- README 规范和项目文档规范
1. README 规范 我们直接通过一个 README 模板,来看一下 README 规范中的内容: # 项目名称<!-- 写一段简短的话描述项目 -->## 功能特性<!-- 描 ...
- FastAPI 自动生成的docs文档没法使用
FastAPI 自动生成的docs文档没法使用,当展开路径时候一直在转圈,具体就是这样 这个是由于swagger-ui 3.30.1 中的bug导致,具体bug可以看这里 我们可以通过在FastAPI ...
- 【软件工程】VB版机房文档总结
前言: 软工视频+软工文档+UML视频+UML图的学习过程图! 这部分的知识很厚,只是知道了个大概!最开始 慢悠悠的像个老爷爷走进度,后来遇到点什么事,妈呀,管不了那么多了,赶紧弄完在说,拖了多久了都 ...
- 智能文档理解:通用文档预训练模型
预训练模型到底是什么,它是如何被应用在产品里,未来又有哪些机会和挑战? 预训练模型把迁移学习很好地用起来了,让我们感到眼前一亮.这和小孩子读书一样,一开始语文.数学.化学都学,读书.网上游戏等,在脑子 ...
- 基于javaGUI的文档识别工具制作
基于javaGUI的文档识别工具制作 对于某些文本,其中富含了一些标志,需要去排除,以及去获得段落字数,以下是我个人写的一个比较简单的文档识别工具,含导入文件.导出文件以及一个简单的识别功能. 1.功 ...
- 从单一图像中提取文档图像:ICCV2019论文解读
从单一图像中提取文档图像:ICCV2019论文解读 DewarpNet: Single-Image Document Unwarping With Stacked 3D and 2D Regressi ...
- 函数小知识点(文档字符串,闭包等)
1 文档字符串(Documentation Strings) 一般被称为docstring,一款你应当使用的重要工具,它能够帮助你更好地记录程序并让其更加易于理解.令人惊叹的是,当程序实际运行时,我们 ...
- Spring Boot 集成Swagger2生成RESTful API文档
Swagger2可以在写代码的同时生成对应的RESTful API文档,方便开发人员参考,另外Swagger2也提供了强大的页面测试功能来调试每个RESTful API. 使用Spring Boot可 ...
最新文章
- 区块链技术是否会终结开源时代?
- ASP.NET 2.0的异步页面刷新真给劲
- 高斯--塞德尔迭代法求方程组的解(Python实现)
- SpringBoot的构造方法中使用@AutoWird注入的类会报错null
- 上下位机通讯协议_嵌入式中自定义协议的一些典型例子
- 云智慧压测实战分享之JMeter脚本录制实例
- HTML只言片语网站导航模板
- java http 双向认证_HttpsURLConnection使用,并实现双向认证
- 20190830:(leetcode习题)二叉树的最大深度
- C++中有了malloc/free 为什么还要new/delete?
- 基于HMM的连续小词量语音识别 - 模拟技术 - 电子发烧友网
- css中visibility与display的区别
- K3 Cloud BOS设计 增加表单按钮 修改状态
- 本周大新闻|Elbit推飞行员专属AR头盔,苹果第二代MR将分高低配
- vab EXCEL 日历制作
- Java实现推箱子小游戏
- Android SDK的下载安装
- 音乐格式转换器哪个好
- 微信打疼的不只是马云
- android中计算机源代码,Android源代码查看途径