很有趣,我现在正在做这个。我在这里只描述一般的想法。完整的代码目前超过 500 行,并且使用更多的实用程序进行格式化等。
例如,我正在制作程序help
(这很愚蠢)。一个普通的结构包含程序需要的所有选项,以及默认值:
struct help_opt
{
string owner;
string path;
bool rec;
int depth;
help_opt() : owner(""), path("/"), rec(false), depth(0) { }
};
另一个结构对命令行参数进行建模:
struct help_args : public help_opt, public arguments <help_args>
{
template <typename S>
void define(S& s)
{
set(s, owner, "owner", 'w', "force <name> as owner", "name");
set(s, path, "output", 'o', "save all output to <path>", "path");
set(s, rec, "recurse", 'r', "recurse into sub-topics");
set(s, depth, "depth", 'd', "set <number> as maximum depth", "number");
}
string title() const { return "my help"; }
string usage() const { return "help [<options>] [<topic>]"; }
string info() const
{
return
"Display help on <topic>, "
"or list available topics if no <topic> is given.";
}
help_args(int argc, char* argv[]) : arguments(argc, argv) { }
};
其中类arguments
是我需要的所有基础设施。所有参数以及元数据(名称、缩写等)都在 中定义define()
,每个参数都通过调用方法来定义set()
。
参数s
用于支持多种操作,例如收集用户输入、构建帮助文本或显示用户给出的值。然后我们将引用传递给每个成员,可以根据用户输入给它一个适当的值,或者保留默认值。然后是参数名称、缩写、完整的帮助文本和可选的参数名称。布尔参数被单独视为标志,没有参数。
附加方法title()
等info()
指定程序自定义的信息消息。
实际处理由 的构造函数完成arguments
。这调用define()
了help_args
,而后者又调用set()
了arguments
。对于每个选项,都会扫描命令行输入并根据需要更新变量。同时,像在 gnu 程序中一样--help
,元数据被收集并自动为选项生成输出,如 , 。--usage
--version
help_args
作为模板参数传递给的事实arguments
是允许 define()
从基类调用,即使它是一个模板,因此不是虚拟的。如果只需要一个操作,那么我们可以切换回普通的虚拟方法。
我希望这很干净。我知道有很多可用的工具和库,但我真的更喜欢这种方式。我可以在准备好后分享实现,它几乎完成了。