include: co/flag.h.
#基本概念
co/flag
是一个类似 google gflags 的命令行参数及配置文件解析库,其原理很简单,代码中定义全局变量,然后在程序启动时解析命令行参数或配置文件,修改这些全局变量的值。
#flag 变量
co/flag 中的宏定义的全局变量,称为 flag 变量。如下面的代码定义了一个 flag 变量,变量名是 FLG_x
。
DEF_bool(x, false, "xxx"); // bool FLG_x = false;
co/flag 支持 7 种类型的 flag 变量:
bool, int32, int64, uint32, uint64, double, string
每个 flag 变量都有一个默认值,用户可以通过命令行参数或配置文件修改 flag 变量的值。如前面定义的 FLG_x
,在命令行中可以用 -x=true
,在配置文件中可以用 x = true
,设置一个新的值。
#command line flag
命令行参数中,以 -x=y
的形式出现,其中 x
被称为一个 command line flag(以下简称为 flag)。命令行中的 flag x
对应代码中的全局变量 FLG_x
,命令行中的 -x=y
就相当于将 FLG_x
的值设置为 y
。为了方便,下面可能将 flag 与 flag 变量统一称为 flag。
co/flag 为了简便易用,设计得非常灵活:
-
-x=y
可以省略前面的-
,简写为x=y
. -
-x=y
也可以写成-x y
. -
x=y
前面可以添加任意数量的-
. -
bool 类型的 flag,
-b=true
可以简写为-b
. -
示例
# b, i, s 都是 flag, xx 不是 flag
./exe -b -i=32 -s=hello xx
#初始化(flag::init)
std::vector<fastring> init(int argc, const char** argv);
std::vector<fastring> init(int argc, char** argv);
void init(const fastring& path);
-
前两个 init 函数,解析命令行参数及配置文件,并更新 flag 变量的值。此函数一般需要在 main 函数开头调用一次。大致流程如下:
- 对命令行参数进行预处理,此过程中可能会更新
FLG_config
的值。 - 如果
FLG_config
非空,解析由它指定的配置文件,更新 flag 变量的值。 - 解析其他命令行参数,更新 flag 变量的值。
- 若
FLG_mkconf
为 true,则生成配置文件,并退出程序。 - 若
FLG_daemon
为 true,则将程序放入后台运行 (仅适用于 linux 平台)。 - 遇到任何错误时,输出错误信息,并立即退出程序。
- 若未发生任何错误,返回 non-flag 列表。如执行
./exe x y
时,此函数将返回["x", "y"]
。
- 对命令行参数进行预处理,此过程中可能会更新
-
最后一个 init 函数,解析配置文件,并更新 flag 变量的值。参数
path
是配置文件的路径。遇到错误时,会输出错误信息,并退出程序。 -
示例
#include "co/flag.h"
int main(int argc, char** argv) {
flag::init(argc, argv);
}
#代码中使用 flag 变量
#定义 flag 变量
#define DEF_bool(name, value, help) _DEFINE_FLAG(bool, name, value, help)
#define DEF_int32(name, value, help) _DEFINE_FLAG(int32, name, value, help)
#define DEF_int64(name, value, help) _DEFINE_FLAG(int64, name, value, help)
#define DEF_uint32(name, value, help) _DEFINE_FLAG(uint32, name, value, help)
#define DEF_uint64(name, value, help) _DEFINE_FLAG(uint64, name, value, help)
#define DEF_double(name, value, help) _DEFINE_FLAG(double, name, value, help)
#define DEF_string(name, value, help) _DEFINE_FLAG(string, name, value, help)
-
上面的 7 个宏,分别用于定义 7 种不同类型的 flag 变量。
-
参数
name
是 flag 名,对应的全局变量名是FLG_name
,参数value
是默认值,参数help
是注释信息。 -
flag 变量是全局变量,一般不要在头文件中定义。
-
flag 变量的名字是唯一的,不能定义两个名字相同的 flag 变量。
-
flag 变量一般在命名空间之外定义,否则可能无法使用 FLG_name 访问 flag 变量。
-
示例
DEF_bool(b, false, "comments"); // bool FLG_b = false;
DEF_int32(i32, 32, "comments"); // int32 FLG_i32 = 32;
DEF_int64(i64, 64, "comments"); // int64 FLG_i64 = 64;
DEF_uint32(u32, 0, "comments"); // uint32 FLG_u32 = 0;
DEF_uint64(u64, 0, "comments"); // uint64 FLG_u64 = 0;
DEF_double(d, 2.0, "comments"); // double FLG_d = 2.0;
DEF_string(s, "x", "comments"); // fastring FLG_s = "x";
#声明 flag 变量
#define DEC_bool(name) _DECLARE_FLAG(bool, name)
#define DEC_int32(name) _DECLARE_FLAG(int32, name)
#define DEC_int64(name) _DECLARE_FLAG(int64, name)
#define DEC_uint32(name) _DECLARE_FLAG(uint32, name)
#define DEC_uint64(name) _DECLARE_FLAG(uint64, name)
#define DEC_double(name) _DECLARE_FLAG(double, name)
#define DEC_string(name) _DECLARE_FLAG(string, name)
-
上面的 7 个宏,分别用于声明 7 种不同类型的 flag 变量。
-
参数
name
是 flag 名,对应的全局变量名是FLG_name
。 -
一个 flag 变量只能定义一次,但可以声明多次,可以在任何需要的地方声明它们。
-
flag 变量一般在命名空间之外声明,否则可能无法使用 FLG_name 访问 flag 变量。
-
示例
DEC_bool(b); // extern bool FLG_b;
DEC_int32(i32); // extern int32 FLG_i32;
DEC_int64(i64); // extern int64 FLG_i64;
DEC_uint32(u32); // extern uint32 FLG_u32;
DEC_uint64(u64); // extern uint64 FLG_u64;
DEC_double(d); // extern double FLG_d;
DEC_string(s); // extern fastring FLG_s;
#使用 flag 变量
定义或声明 flag 变量后,就可以像普通变量一样使用它们:
#include "co/flag.h"
DEC_bool(b);
DEF_string(s, "hello", "xxx");
int main(int argc, char** argv) {
flag::init(argc, argv);
if (!FLG_b) std::cout << "b is false" << std::endl;
FLG_s += " world";
std::cout << FLG_s << std::endl;
return 0;
}
#命令行中使用 flag
#命令行中修改 flag 变量的值
假设程序中定义了如下的 flag:
DEF_bool(x, false, "bool x");
DEF_bool(y, false, "bool y");
DEF_int32(i, -32, "int32");
DEF_uint64(u, 64, "uint64");
DEF_string(s, "nice", "string");
程序启动时,可以通过命令行参数修改 flag 变量的值:
# -x=y, x=y, -x y, 三者是等价的
./xx -i=8 u=88 -s=xxx
./xx -i 8 -u 88 -s "hello world"
./xx -i8 # 仅适用于单字母命名的整数类型 flag
# bool 类型设置为 true 时, 可以略去值
./xx -x # -x=true
# 多个单字母命名的 bool flag, 可以合并设置为 true
./xx -xy # -x=true -y=true
# 整数类型的 flag 可以带单位 k, m, g, t, p, 不区分大小写
./xx -i -4k # i=-4096
# 整数类型的 flag 可以传 8 进制 或 16 进制数
./xx i=032 # i=26 8 进制
./xx u=0xff # u=255 16 进制
#查看帮助信息(–help)
co/flag 支持用 --help
命令查看程序的帮助信息:
$ ./xx --help
usage:
./xx -- print flags info
./xx --help print this help info
./xx --mkconf generate config file
./xx --daemon run as a daemon (Linux)
./xx xx.conf run with config file
./xx config=xx.conf run with config file
./xx -x -i=8k -s=ok run with commandline flags
./xx -x -i 8k -s ok run with commandline flags
./xx x=true i=8192 s=ok run with commandline flags
#查看 flag 列表(–)
co/flag 可以用 --
命令,用于查看程序中定义的 flag 列表:
$ ./xx --
--config: .path of config file
type: string default: ""
from: ../../base/flag.cc
--mkconf: .generate config file
type: bool default: false
from: ../../base/flag.cc
#配置文件
#配置文件格式
co/flag 的配置文件格式比较灵活:
-
一行一个配置项,每个配置项对应一个 flag,形式统一为
x = y
,看起来一目了然。 -
#
或//
表示注释,支持行尾注释。 -
引号中的
#
或//
不是注释。 -
忽略行前、行尾的空白字符,书写更自由,不容易出错。
-
=
号前后可以任意添加空白字符,书写更自由。 -
可以用
\
续行,以免一行太长,影响美观。 -
字符串不支持转义,以免产生歧义。
-
字符串可以用双引号、单引号或 3个反引号括起来。
-
配置文件示例
# config file: xx.conf
boo = true # bool 类型
s = # 空字符串
s = hello \
world # s = "helloworld"
s = "http://github.com" # 引号中的 # 与 // 不是注释
s = "I'm ok" # 字符串中含有单引号,两端可以用双引号括起来
s = 'how are "U"' # 字符串中含有双引号,两端可以用单引号括起来
s = ```I'm "ok"``` # 字符串两端也可以用 3 个反引号括起来
i32 = 4k # 4096, 整型可以带单位 k,m,g,t,p, 不区分大小写
i32 = 032 # 8 进制, i32 = 26
i32 = 0xff # 16 进制, i32 = 255
pi = 3.14159 # double 类型
#自动生成配置文件
DEF_bool(mkconf, false, ".generate config file");
mkconf
是 co/flag 内部定义的 flag,它是自动生成配置文件的开关。- 命令行中可以用
-mkconf
自动生成配置文件。
./xx -mkconf # 在 xx 所在目录生成 xx.conf
./xx -mkconf -x u=88 # 自定义配置项的值
#调整配置文件中配置项的顺序
自动生成的配置文件中,配置项按 flag 级别、所在文件名、所在代码行数进行排序。如果用户想让某些配置项的排序靠前些,可以将 flag 的级别设成较小的值,反之可以将 flag 级别设成较大的值。
定义 flag 时可以在注释开头用 #n
指定级别,n 必须是 0 到 99 之间的整数,若注释非空,n 后面必须有一个空格。不指定时,默认 flag 级别为 10。
DEF_bool(x, false, "comments"); // 默认级别为 10
DEF_bool(y, false, "#23"); // 级别为 23, 注释为空
DEF_bool(z, false, "#3 comments"); // 级别为 3, 注释非空, 3 后面必须有一个空格
#禁止某些配置项生成到配置文件中
注释以 .
开头的 flag,带有隐藏属性,不会生成到配置文件中,但用 --
命令可以查看。注释为空的 flag,则是完全不可见的,既不会生成到配置文件中,也不能用 --
命令查看。
DEF_bool(x, false, ".say something here");
DEF_string(s, "good", "");
#程序启动时指定配置文件
DEF_string(config, "", ".path of config file");
config
是 co/flag 内部定义的 flag,表示配置文件的路径。- 命令行中可以用
-config
指定配置文件。 - 代码中可以在调用
flag::init()
之前,修改FLG_config
的值,以指定配置文件。
./xx -config xx.conf
# 若配置文件名以 .conf 或 config 结尾, 且是命令行的
# 第一个 non-flag 参数, 则可省略 -config
./xx xx.conf
./xx xx.conf -x
#自定义帮助信息
DEF_string(help, "", ".help info");
-
help
是 co/flag 内部定义的 flag,表示程序的帮助信息,命令行中使用--help
命令可以查看此信息。 -
FLG_help
默认为空,使用 co/flag 内部提供的默认帮助信息。 -
用户想自定义帮助信息时,可以在调用
flag::init()
前,修改FLG_help
的值。 -
示例
#include "co/flag.h"
int main(int argc, char** argv) {
FLG_help << "usage:\n"
<< "\t./xx -ip 127.0.0.1 -port 7777\n";
flag::init(argc, argv);
return 0;
}
#让程序在后台运行
DEF_bool(daemon, false, "#0 run program as a daemon");
-
daemon
是 co/flag 内部定义的 flag,若为 true,程序将在后台运行,仅支持 linux 平台。 -
命令行中可以用
-daemon
指定程序以 daemon 形式在后台运行。 -
示例
./xx -daemon