include: co/flag.h.
#基本概念
co.flag 是一个命令行参数及配置文件解析库,其原理很简单,代码中定义全局变量,然后在程序启动时解析命令行参数或配置文件,修改这些全局变量的值。
#flag 变量
co.flag 中的宏定义的配置项,实际上是全局变量,称为 flag 变量。如下面的代码定义了一个 flag 变量,变量名是 FLG_x
。
DEF_int32(x, 0, "xxx"); // int32 FLG_x = 0;
co.flag 支持 7 种类型的 flag 变量:
bool, int32, int64, uint32, uint64, double, string
每个 flag 变量都有一个默认值,用户可以通过命令行参数或配置文件修改 flag 变量的值。如前面定义的 FLG_x
,在命令行中可以用 -x=23
,在配置文件中可以用 x = 23
,设置一个新的值。
#command line flag
命令行参数中,以 -x=y
的形式出现,其中 x
被称为一个 command line flag(以下简称为 flag)。命令行中的 flag x
对应代码中的全局变量 FLG_x
,命令行中的 -x=y
就相当于将 FLG_x
的值设置为 y
。
为了方便,本文档中可能将 command line 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
#APIs
#flag::parse
co::vector<fastring> parse(int argc, char** argv);
void parse(const fastring& path);
-
v3.0.1 新增。
-
第 1 个 parse 函数,解析命令行参数及配置文件,并更新 flag 变量的值。此函数一般需要在 main 函数开头调用一次。大致流程如下:
- 对命令行参数进行预处理,此过程中可能会更新
FLG_config
的值。 - 如果
FLG_config
非空,解析由它指定的配置文件,更新 flag 变量的值。 - 解析其他命令行参数,更新 flag 变量的值。
- 若
FLG_mkconf
为 true,则生成配置文件,并退出程序。 - 若
FLG_daemon
为 true,则将程序放入后台运行 (仅适用于 linux 平台)。 - 遇到任何错误时,输出错误信息,并立即退出程序。
- 若未发生任何错误,返回 non-flag 列表。如执行
./exe x y
时,此函数将返回["x", "y"]
。
- 对命令行参数进行预处理,此过程中可能会更新
-
第 2 个 parse 函数,解析配置文件,并更新 flag 变量的值。参数
path
是配置文件的路径。遇到错误时,会输出错误信息,并退出程序。
flag::init
v3.0.1 中,flag::init()
被标记为 deprecated,请使用flag::parse()
。
- 示例
#include "co/flag.h"
int main(int argc, char** argv) {
flag::parse(argc, argv);
}
#flag::set_value
fastring set_value(const fastring& name, const fastring& value)
-
v3.0 新增,设置 flag 变量的值,
name
是 flag 名。 -
此函数非线程安全,一般需要在 main 函数开头调用。
-
示例
DEF_bool(b, false, "");
DEF_int32(i, 0, "");
DEF_string(s, "", "");
int main(int argc, char** argv) {
flag::set_value("b", "true"); // FLG_b -> true
flag::set_value("i", "23"); // FLG_i -> 23
flag::set_value("s", "xx"); // FLG_s -> "xx"
flag::parse(argc, argv);
}
#flag::alias
bool alias(const char* name, const char* new_name);
-
v3.0 新增,给 flag 取别名,在命令行参数或配置文件中可以用别名取代原名。
-
此函数非线程安全,需要在
flag::parse()
之前调用。 -
示例
DEF_bool(all, false, "");
int main(int argc, char** argv) {
flag::alias("all", "a");
flag::parse(argc, argv);
}
#代码中使用 flag 变量
#定义 flag 变量
DEF_bool(name, value, help, ...)
DEF_int32(name, value, help, ...)
DEF_int64(name, value, help, ...)
DEF_uint32(name, value, help, ...)
DEF_uint64(name, value, help, ...)
DEF_double(name, value, help, ...)
DEF_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 = ...;
v3.0.1 中,DEF_string
实际上定义了一个fastring
类型的引用。
#flag 添加别名
-
v3.0 新增,定义 flag 变量时,可以为 flag 添加任意数量的别名。
-
在命令行或配置文件中,可以用别名取代原名。
-
示例
DEF_bool(debug, false, ""); // no alias
DEF_bool(debug, false, "", d); // d is an alias of debug
DEF_bool(debug, false, "", d, dbg); // 2 aliases
#声明 flag 变量
DEC_bool(name)
DEC_int32(name)
DEC_int64(name)
DEC_uint32(name)
DEC_uint64(name)
DEC_double(name)
DEC_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::parse(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 "hello world"
./xx -i=8 u=88 -s=xxx
./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
命令查看程序的帮助信息,该命令会显示 usage 信息及用户定义的 flag 列表。
$ ./xx --help
usage: $exe [-flag] [value]
$exe -x -i 8k -s ok # x=true, i=8192, s="ok"
$exe -- # print all flags
$exe -mkconf # generate config file
$exe -conf xx.conf # run with config file
flags:
-n int32
type: int32 default: 0
from: test/flag.cc
-s string
type: string default: "hello world"
from: test/flag.cc
#查看 flag 列表(–)
co.flag 可以用 --
命令查看程序中定义的全部 flag 列表(包括co内部定义的flags):
$ ./xx --
flags:
-boo bool flag
type: bool default: false
from: test/flag.cc
-co_sched_num number of coroutine schedulers, default: os::cpunum()
type: uint32 default: os::cpunum()
from: src/co/sched.cc
#查看程序版本信息
-
version
是 coost 内部定义的 flag,命令行中可以使用-version
命令查看版本信息。 -
version
默认值为空,用户需要在调用flag::parse()
前,修改其值。 -
示例
#include "co/flag.h"
int main(int argc, char** argv) {
FLG_version = "v3.0.0";
flag::parse(argc, argv);
return 0;
}
$ ./xx -version
v3.0.0
#配置文件
#配置文件格式
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 类型
#自动生成配置文件
mkconf
是 coost 内部定义的 flag,它是自动生成配置文件的开关。- 命令行中可以用
-mkconf
自动生成配置文件。
./xx -mkconf # 在 xx 所在目录生成 xx.conf
./xx -mkconf -x u=88 # 自定义配置项的值
#调整配置项的顺序
自动生成的配置文件中,配置项按 flag 级别、所在文件名、所在代码行数进行排序。如果用户想让某些配置项的排序靠前些,可以将 flag 的级别设成较小的值,反之可以将 flag 级别设成较大的值。
定义 flag 时可以在注释开头用 #n
指定级别,n 必须是 0 到 9 之间的整数,若注释非空,n 后面必须有一个空格。不指定时,默认 flag 级别为 5。
DEF_bool(x, false, "comments"); // 默认级别为 5
DEF_bool(y, false, "#3"); // 级别为 3, 注释为空
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", conf);
config
是 coost 内部定义的 flag,表示配置文件的路径,它有一个别名conf
。- 命令行中可以用
-config
或-conf
指定配置文件。 - 代码中可以在调用
flag::parse()
之前,修改FLG_config
的值,以指定配置文件。
./xx -config xx.conf
./xx -conf xx.conf
# 若配置文件名以 .conf 或 config 结尾, 且是命令行的
# 第一个 non-flag 参数, 则可省略 -config
./xx xx.conf
./xx xx.conf -x
#自定义帮助信息
-
help
是 coost 内部定义的 flag,命令行中可以使用--help
命令查看帮助信息。 -
FLG_help
默认为空,使用 coost 内部提供的默认帮助信息。 -
用户想自定义帮助信息时,可以在调用
flag::parse()
前,修改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::parse(argc, argv);
return 0;
}
#让程序在后台运行
-
daemon
是 coost 内部定义的 flag,若为 true,程序将在后台运行,仅支持 linux 平台。 -
命令行中可以用
-daemon
指定程序以 daemon 形式在后台运行。 -
示例
./xx -daemon