illx10000

青春不是年华,而是心境

gflags学习

2018年12月22日 星期六, 发表于 深圳

如果你对本文有任何的建议或者疑问, 可以在 这里给我提 Issues, 谢谢! :)


gflags库是google开源的一套命令行参数处理库,可以用于便捷的构造一些小工具使用,在学习brpc中看到使用了gflags库,因此再仔细了解一下gfalgs库使用方式

gflags的官方使用文档gflags 使用
本文大部分也是参考该文档,进行实践,开发环境如下;

ubuntu@ubuntu:~/learning/gflags$ gcc -v
gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04) 

step1. 下载安装

gflags库在github上开源,可以直接使用git下载

 git clone https://github.com/gflags/gflags.git

下载之后参考上面的官方文档使用cmake安装,在git clone的目录下执行

cmake .
make 
make install

执行上面的安装操作后,可以看到gflags将编译好的库拷贝到系统库目录里面

Install the project...
-- Install configuration: "Release"
-- Installing: /usr/local/lib/libgflags.a
-- Installing: /usr/local/lib/libgflags_nothreads.a
-- Installing: /usr/local/include/gflags/gflags.h
-- Installing: /usr/local/include/gflags/gflags_declare.h
-- Installing: /usr/local/include/gflags/gflags_completions.h
-- Installing: /usr/local/include/gflags/gflags_gflags.h

如果没有安装cmake可以先安装cmake,ubuntu系统以root权限执行

apt install cmake

如果使用其他方式安装,可以参考官方文档;

step2. 使用gflags库

在step1中已经编译安装好了gflags库,看一下怎么使用gflags库,依然参考官方文档; 在官方文档中,我们看到使用gflags,需要有几个步骤,首先需要在程序中声明标签,然后在访问标签

step2.1 声明标签

声明标签非常容易,只需要给想用的标签使用合适的宏来声明即可,所有的宏都在 gflags/gflags.h 中声明,例如下面的例子

#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类型的标签,下面是gflags支持的标签:

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

gflags不支持复杂类型,例如list,在上面的例子中 languages标签,是定义为一个string,而不是 list of string,gfalgs库认为把string的解析放给业务使用者来做更合适

step2.2 访问标签

所有已定义的标签,可以在代码中当成一个正常变量来使用,只需要在前面加上 FLAGS_ 前缀,例如在step2.1中定义了两个标签,在下面的代码中访问

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 文件中定义的函数来获取和修改标签,不过使用的比较少

step2.3 跨文件使用标签

如果需要在其他文件中访问某一个文件中定义的标签,可以使用DECLARE_宏,类似于extern的作用,官方文档有一个建议:

如果在foo.cc中定义了一个标签,要么根本就不要声明它,只在仅需要它的测试案例中声明,要么只在foo.h中声明;

If you DEFINE a flag in foo.cc, either don't DECLARE it at all, only DECLARE it in tightly related tests, or only DECLARE it in foo.h.

step2.4 注册标签校验函数(RegisterFlagValidator)

定义完一个标签之后,可以注册一个标签检验函数(可选的)。如果定义了标签校验函数,当标签从命令行传入,或者任何调用SetCommandLineOption去改变标签值的时候,该函数会被调用,当标签合法的时候,标签函数返回true,更新标签为设置值,如果标签不合法,则标签函数返回false,并且将标签设置为默认值,ParseCommandLineFlags 会退出。

例如:

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");
DEFINE_validator(port, &ValidatePort);

DEFINE_validator 宏调用标签校验函数,当校验函数返回ture,标签才被成功注册,否则标签注册失败;

step2.5 整体流程

参考下面代码

 #include <iostream>
 #include <gflags/gflags.h>

DEFINE_string(message, "Hello World!", "The message to print");

static bool ValidateMessage(const char* flagname, const std::string &message)
{
  return !message.empty();
}
DEFINE_validator(message, ValidateMessage);

int main(int argc, char **argv)
{
  gflags::SetUsageMessage("Test CMake configuration of gflags library (gflags-config.cmake)");
  gflags::SetVersionString("0.1");
  gflags::ParseCommandLineFlags(&argc, &argv, true);
  std::cout << FLAGS_message << std::endl;
  gflags::ShutDownCommandLineFlags();
  return 0;
}

step2.6特殊变量

用到的情况不太多,详情参阅官方文档

--help	shows all flags from all files, sorted by file and then by name; shows the flagname, its default value, and its help string
--helpfull	same as -help, but unambiguously asks for all flags (in case -help changes in the future)
--helpshort	shows only flags for the file with the same name as the executable (usually the one containing main())
--helpxml	like --help, but output is in xml for easier parsing
--helpon=FILE  	shows only flags defined in FILE.*
--helpmatch=S	shows only flags defined in *S*.*
--helppackage	shows flags defined in files in same directory as main()
--version	prints version info for the executable
--fromenv
--tryfromenv
--flagfile