C++命令行解析包gflags的使用教程

前言

gflags 是 Google 提供的一个命令行参数处理的开源库,目前已经独立开源,比传统的 getopt() 功能更加强大,可以将不同的参数定义分布到各个源码文件中,不需要集中管理。

提供了 C++ 和 Python 两个版本,这里仅详细介绍 C++ 版本的使用方式。

简介

配置参数分开还是集中管理没有严格的约束,关键要看项目里的统一规范,只是,gflags 可以支持这两种方式,允许用户更加灵活的使用。

当将参数分布到各个源码文件中时,如果定义了相同的参数,那么在编译的时候会直接报错。

安装

很多发行版本会有自己相关的开发库,这里简单介绍使用 CMake 从源码进行编译,源码可以从 GitHub gflags Releases 中选择相关的版本。

如下命令以最新的 2.2.2 版本为例。

$ tar xzf gflags-2.2.2.tar.gz
$ cd gflags-2.2.2
$ mkdir build && cd build
$ cmake -DCMAKE_INSTALL_PREFIX=/usr ..
$ make
$ make test  # 单元测试,执行cmake时需要增加-DBUILD_TESTING=true参数
# make install # 安装,一般需要root用户执行

默认会安装到 /usr/local 目录下,需要配置动态库、头文件路径等,通过上述的 -DCMAKE_INSTALL_PREFIX=/usr 参数修改该路径,使用系统默认路径,此时会安装如下的文件。

/usr/lib/libgflags.a
/usr/lib/libgflags_nothreads.a
/usr/include/gflags/gflags.h
/usr/include/gflags/gflags_declare.h
/usr/include/gflags/gflags_completions.h
/usr/include/gflags/gflags_gflags.h
/usr/lib/cmake/gflags/gflags-config.cmake
/usr/lib/cmake/gflags/gflags-config-version.cmake
/usr/lib/cmake/gflags/gflags-targets.cmake
/usr/lib/cmake/gflags/gflags-targets-release.cmake
/usr/lib/cmake/gflags/gflags-nonamespace-targets.cmake
/usr/lib/cmake/gflags/gflags-nonamespace-targets-release.cmake
/usr/bin/gflags_completions.sh
/usr/lib/pkgconfig/gflags.pc

详细的安装可以参考gflags install.md中的介绍,可以使用 ccmake 选择配置项,或者使用上述的 cmake + 参数的方式配置。

示例

假设有个网络客户端代码,需要指定服务端的地址和端口,希望有默认参数,同时允许用户通过命令行来指定不同的值。

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

DEFINE_string(host, "localhost", "Server host address");
DEFINE_int32(port, 8080, "Server port");

int main(int argc, char **argv)
{
  gflags::ParseCommandLineFlags(&argc, &argv, true);
  std::cout << "Got '" << FLAGS_host << ":" << FLAGS_port << "'." << std::endl;
  return 0;
}

在代码开头通过 DEFINE_XXX 定义参数,包括了变量名、默认值、参数介绍等;主程序中使用 gflags::ParseCommandLineFlags() 函数解析参数;使用时,在变量名称前添加 FLAGS_ 头即可。

通过如下命令行进行编译。

g++ main.cc -std=c++11 -o gflags -lgflags -lpthread

默认是需要 pthread 线程库的,暂时还不太确定没有使用多线程时,如何关闭该参数。

然后,可以通过如下方式指定参数。

----- 不指定参数,使用默认值
$ ./gflags
Got 'localhost:8080'.

----- 可以选择指定一个参数,或者多个参数
$ ./gflags -host www.foobar.com
Got 'www.foobar.com:8080'.
$ ./gflags -port 80
Got 'localhost:80'.
$ ./gflags -host www.foobar.com -port 80
Got 'www.foobar.com:80'.

----- 同时支持不同的参数指定方式
$ ./gflags --host www.foobar.com --port 80
Got 'www.foobar.com:80'.
$ ./gflags --host=www.foobar.com=--port 80
Got 'www.foobar.com:80'.

同时也可以使用 --help 参数查看帮助信息,包含了 gflags 库提供的参数,以及用户提供的参数,如下是输出的用户参数信息。

 Flags from main.cc:
 -host (Server host address) type: string default: "localhost"
 -port (Server port) type: int32 default: 8080

接着看看详细使用方式。

使用详解

包含了如何定义、解析等使用场景。

定义参数

在如上的示例中定义了两种类型的参数,分别为字符串 string 和整型 int32 ,包括了变量名、默认值、参数介绍三个入参,三个参数都是必须的。

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

可以定义到某个 NameSpace 下,这样在使用时也必须要带着 NameSpace 前缀。如果在不同的文件中定义,那么可以在某个集中的头文件中通过 DECLARE_XXX(VAR) 进行声明。

注意,不要定义相同名称的参数,即使在不同的 NameSpace 也不可以;还有几个保留参数,包括了 flagfile fromenv tryfromenv undefok 等等。

参数解析

在上述的示例中,通过 ParseCommandLineFlags() 函数解析参数,另外还有不带帮助文档的解析方式,两个函数的声明如下。

uint32 ParseCommandLineFlags(int* argc, char*** argv, bool remove_flags);
uint32 ParseCommandLineNonHelpFlags(int* argc, char*** argv, bool remove_flags);

其中 remove_flags 标识指定参数的处理方式,如果为 true ,那么解析时会将 flag 以及 flag 对应的值从 argv 中删除,并修改 argc ,也就是说,最后存放的是不包含 flag 的参数;如果为 false 则仅对参数重排,标志位参数放最前面。

参数校验

为了检查参数的值是否合法,可以针对某个参数注册一个验证函数,当参数解析或者修改 (调用 SetCommandLineOption() 时) 该验证函数都会被调用,返回 true 表示校验成功,否则失败。

如下是对于 port 参数的校验。

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

DEFINE_string(host, "localhost", "Server host address.");
DEFINE_int32(port, 8080, "Server port.");

static bool ValidatePort(const char *flag, gflags::int32 value)
{
  if (value > 0 && value < 32768)
    return true;
  std::cerr << "Invalid value for --" << flag << ": " << value << std::endl;
  return false;
}
static const bool port_dummy = gflags::RegisterFlagValidator(&FLAGS_port, &ValidatePort);

int main(int argc, char **argv)
{
  gflags::ParseCommandLineFlags(&argc, &argv, true);
  std::cout << "Got '" << FLAGS_host << ":" << FLAGS_port << "'" << std::endl;
  return 0;
}

如果超过了指定的范围则会报错。

使用参数

提供了默认的 --help 查看帮助信息,指定参数可以使用 - 或者 -- 符号,参数和值的分割可以使用 ` ` 或者 = ,如上的示例。

对于布尔类型,还可以使用 --foobar --nofoobar --foobar=true --foobar false 的方式指定。

检查参数是否设置

在通过 ParseCommandLineFlags() 函数解析完参数之后,可以通过如下方法检查对应的变量是否被设置。

gflags::CommandLineFlagInfo info;
if (gflags::GetCommandLineFlagInfo("port", &info) && info.is_default) {
  std::cout << "port is not set." << std::endl;
} else {
  std::cout << "port is set." << std::endl;
}

这里不是直接比对的设置值与默认值相同,即使指定了与默认值相同的值,也会被认为参数被修改了。

文件引入

可以通过 --flagfile=FileName 指定参数文件名,文件名也可以使用通配符 * 以及 ?,在文件中每行标识一个参数,例如:

$ cat flags.txt
# This is the test server.
--host=www.foobar.com
--port=80
$ ./gflags --flagfile flags.txt
Got 'www.foobar.com:80'.

以 # 开头的为注释,也可以再次使用 --flagfile=FileName 包含一个参数配置文件。

环境变量引入

可以使用 --fromenv 或者 --tryfromenv 从环境变量中引入参数,也可通过 --fromenv=foo,bar 指定读取的参数,当然,需要先设置好环境变量。

export FLAGS_foo=xxx; export FLAGS_bar=yyy # sh
setenv FLAGS_foo xxx; setenv FLAGS_bar yyy # tcsh

这种方式等价于在命令行指定 --foo=xxx --bar=yyy 参数。

其中 --fromenv 时如果环境变量不存在则会报错,而 --tryfromenv 当环境变量中不存在时会使用默认值。

程序中指定

最常见的是允许用户动态进行配置,也就是说动态加载,可以调用 SetCommandLineOption() 函数来实现,函数的声明如下。

std::string SetCommandLineOption(const char* name, const char* value);

例如。

gflags::SetCommandLineOption("port", "9999");

成功则会返回 port set to 9999 字符串,否则会返回空字符串。

另外也可以通过 bool GetCommandLineOption(const char* name, std::string* OUTPUT) 函数获取 flag 的接口,如果没有指定,则会通过 OUTPUT 返回默认的值,只有当指定了一个未定义的 flag 名称时,会返回 false 。

正常读写都可以使用 if (FLAGS_foo); FLAGS_Foo = bar 的形式,但是如果需要线程安全的调用,建议使用这两个函数。

完整示例

如上设置的完整示例如下。

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

DEFINE_string(host, "localhost", "Server host address.");
DEFINE_int32(port, 8080, "Server port.");

static bool ValidatePort(const char *flag, gflags::int32 value)
{
  if (value > 0 && value < 32768)
    return true;
  std::cerr << "Invalid value for --" << flag << ": " << value << std::endl;
  return false;
}
static const bool port_dummy = gflags::RegisterFlagValidator(&FLAGS_port, &ValidatePort);

int main(int argc, char **argv)
{
  gflags::SetVersionString("1.1.0");
  gflags::SetUsageMessage("./gflags");
  gflags::ParseCommandLineFlags(&argc, &argv, true);

  std::cout << gflags::SetCommandLineOption("port", "999") << std::endl;

  std::cout << "Got '" << FLAGS_host << ":" << FLAGS_port << "'" << std::endl;
  return 0;
}

可以通过 g++ main.cc -std=c++11 -o gflags -lgflags -lpthread 命令编译。

其它

常用参数

gflags 中包含了几类默认的参数。

--help        显示所有文件的所有flag,按文件、名称排序,显示flag名、默认值和帮助
--helpfull    和 --help 相同,显示全部flag
--helpshort    只显示执行文件中包含的flag,通常是 main() 所在文件
--helpxml    类似 --help,但输出为xml
--helpon=FILE  只显示定义在 FILE.* 中得flag
--helpmatch=S  只显示定义在 *S*.* 中的flag
--helppackage  显示和 main() 在相同目录的文件中的flag
--version    打印执行文件的版本信息

--undefok=flagname,flagname,...  后面列出的flag名,可以在无定义的情况下忽略而不报错

--fromenv --tryfromenv   从环境变量中引入
--flagfile               从文件中引入

定制版本和帮助信息

通过 --version 和 --help 默认会输出其对应的版本和帮助信息,也可以通过 SetVersionString() 设置版本信息,通过 SetUsageMessage() 设置帮助的开始软件信息 (帮助信息无法覆盖)。

gflags::SetVersionString("1.1.0");
gflags::SetUsageMessage("./gflags");

注意,参数的设置需要在调用 ParseCommandLineFlags() 之前。

CMake 使用

最新版本的 gflags 已经可以支持 CMake 了,如上安装时所示,在安装时会在 /usr/lib/cmake/gflags/ 目录下安装相关的文件,那么在项目中可以通过如下方式使用。

FIND_PACKAGE(gflags REQUIRED)
INCLUDE_DIRECTORIES(${gflags_INCLUDE_DIR})

ADD_EXECUTABLE(foo main.cc)
TARGET_LINK_LIBRARIES(foo gflags)

如果是将 CMake 安装到了其它路径下,那么可以将上述的文件复制到 CMake 的模块路径下再使用。

整型选择

在 gflags/gflags_declare.h 中定义了 int32 int64 等类型,可以直接使用,不过编译时需要添加 -std=c++11 参数,否则在使用 cstdint 头文件时会报错。

其它

可以通过 void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT) 接口遍历所有的参数,更多接口可以查看 gflags/gflags.h 头文件。

参考

详细可以查看官方文档 How To Use gflags中的介绍。

到此这篇关于C++命令行解析包gflags使用教程的文章就介绍到这了,更多相关C++命令行解析包gflags使用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python中使用gflags实例及原理解析

    这篇文章主要介绍了Python中使用gflags实例及原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 安装命令pip install python-gflags 使用示例: import gflags FLAGS = gflags.FLAGS gflags.DEFINE_string('name', 'ming', 'this is a value') gflags.DEFINE_integer('qps', 0, 'test qps'

  • C++命令行解析包gflags的使用教程

    前言 gflags 是 Google 提供的一个命令行参数处理的开源库,目前已经独立开源,比传统的 getopt() 功能更加强大,可以将不同的参数定义分布到各个源码文件中,不需要集中管理. 提供了 C++ 和 Python 两个版本,这里仅详细介绍 C++ 版本的使用方式. 简介 配置参数分开还是集中管理没有严格的约束,关键要看项目里的统一规范,只是,gflags 可以支持这两种方式,允许用户更加灵活的使用. 当将参数分布到各个源码文件中时,如果定义了相同的参数,那么在编译的时候会直接报错.

  • 详解Python命令行解析工具Argparse

    最近在研究pathon的命令行解析工具,argparse,它是Python标准库中推荐使用的编写命令行程序的工具. 以前老是做UI程序,今天试了下命令行程序,感觉相当好,不用再花大把时间去研究界面问题,尤其是vc++中尤其繁琐. 现在用python来实现命令行,核心计算模块可以用c自己写扩展库,效果挺好. 学习了argparse,在官方文档中找到一篇toturial,简单翻译了下. http://docs.python.org/2/howto/argparse.html#id1 Argparse

  • Python命令行解析模块详解

    本文研究的主要是Python命令行解析模块的相关内容,具体如下. Python命令行常见的解析器有两种,一是getopt模块,二是argparse模块.下面就解读下这两种解析器. getopt模块 这个模块可以帮助脚本解析命令行参数,一般是sys.argv[1:].它遵循着Unix的getopt()函数相同的约定(用-/--指定命令参数).这个模块提供两个函数(getopt.getopt()/getopt.gnu_getopt())和一个参数异常(getopt.GetoptError). 这里重

  • python命令行解析之parse_known_args()函数和parse_args()使用区别介绍

    在python中,命令行解析的很好用, 首先导入命令行解析模块 import argparse import sys 然后创建对象 parse=argparse.ArgumentParser() 然后增加命令行 parse.add_argument("--learning_rate",type=float,default=0.01,help="initial learining rate") parse.add_argument("--max_steps&

  • C++基于Boost库实现命令行解析

    第一次尝试 #include <iostream> #include <boost/program_options.hpp> // 定义命名空间 namespace opt = boost::program_options; int main(int argc, char const *argv[]) { opt::options_description desc("Usage: 32位端口快速扫描器\n\n options: \n"); desc.add_op

  • Python命令行解析器argparse详解

    目录 第1章 argparse简介 1.1 解析 1.2 argparse定义三步骤 1.3  代码示例 第2章 参数详解 2.1 创建一个命令行解析器对象:ArgumentParser() 2.2 为命令行添加参数: add_argument() 方法 2.3 解析命令行的参数:parse_args() 2.4 命令行参数的输入 2.5 命令行参数的使用 总结 第1章 argparse简介 1.1 解析 argparse 模块是 Python 内置的一个用于命令项选项与参数解析的模块,argp

  • Python 命令行解析工具 argparse基本用法

    在工作中,我们经常需要从命令行当中解析出指定的参数,而 Python 也提供了相应的标准库来做这件事情,比如 sys, optparse, getopt, argparse.这里面功能最强大的莫过于 argparse,下面就来看看它用法. import argparse # 使用 argparse 分为以下几步 # 1. 创建命令行解析器对象 parse = argparse.ArgumentParser( description="这是命令行解析器" ) # 2.给解析器添加命令行参

  • .NET命令行解析器示例程序(命令行选项功能)

    示例需求 拷贝文件,如:CopyFiles -s "E:\Framework\Tenoner - 副本 (2)" -p "*.csproj" -t "E:\Framework\Tenoner - 副本 (2)\Bak",可以支持:深度拷贝.拷贝符合指定模式的文件.是否覆盖等选项. 使用 CommandLineParserCommandLineParser 是一个轻量级的工具,使用非常简答,官方也有教程. 选项类 复制代码 代码如下: using

  • Python安装使用命令行交互模块pexpect的基础教程

    一.安装 1.安装easy_install工具 wget http://peak.telecommunity.com/dist/ez_setup.py python ez_setup.py 安装easy_install工具(这个脚本会自动去官网搜索下载并安装) python ez_setup.py -U setuptools 升级easy_install工具 2.安装pexpect easy_install Pexpect 测试一下: [root@OMS python]# python Pyth

  • python argparse 模块命令行参数用法及说明

    目录 先上代码 下面针对一些特殊点再作详述 程序用法帮助 argparse 是 python 的一个命令行解析包,可根据需要编写高可读性的程序. 网上的许多教程较为冗长和散漫,没有达到精练好掌握的目的,本文针对项目中对 argparse 的用法,用实例对各个参数进行讲解,力求达到让读者秒懂的目的. 先上代码 import argparse if __name__ == '__main__': # 创建命令行解析器句柄,并自定义描述信息 parser = argparse.ArgumentPars

随机推荐