boost I 操作系统相关的库(三)- program_options系统

目录

一、基本流程

二、组件介绍

1.选项描述器

(1)value_semantic选项值

(2)option_description

(3)options_description

2.分析器

(1)parse_command_line

(2)parse_config_file

(3)parse_environment

3.存储器

三、扩展

1.位置选项值

2.未知选项

3.命令行风格


        program_options 库提供了从外界获取程序选项的能力。程序的选项可以源自很多地方,最常见的就是命令行参数,此外program_options 库还能够从配置文件、响应文件和环境变量中获得选项,适应将来维护期的变化。

一、基本流程

        处理程序选项功能的核心类是options_description,它的成员函数add_options ( ) 重载了括号操作符,用来添加选项解析,其用法很像 assign 库

        在定义了选项参数后,函数 store ( ) 和parse_command_line ( ) 将解析命令行参数。解析的结果保存在 variables_map,它是 std::map的派生类 , 可以像使用关联数组一样使用它。

        variables_map 的 value_type 是 boost::any , 用来存储不确定类型的参数值,必须用模板成员函数 as < type > ( ) 转型才能获取具体值。在解析完成后,程序就可以随意使用variables_map 获取选项值 , 执行不同的分支以满足用户的请求。

#include <boost/program_options.hpp>
int main(int argc, char* argv[])
{
    boost::program_options::options_description opts("demo options");
    opts.add_options()
        ("about,a", "this is a about.")
        ("version,v", "program version");//两个程序选项
    boost::program_options::variables_map vm;//选项存储map容器
    boost::program_options::store(boost::program_options::parse_command_line(argc, argv, opts), vm);//解析存储
    if (vm.empty())
    {
        std::cout << "no options" << std::endl;
        return;
    }
    if (vm.count("about"))
    {
        std::cout << std::endl << opts << std::endl;
    }
    if (vm.count("version"))
    {
        std::cout <<"version is V3.3.0" << std::endl;
    }
}

        program_options 库使用的程序选项默认是 UNIX风格,使用“ -- ”和“ - ”指定选项名。规则约定是:-后面跟一个字母缩写,--后面跟多个字母的长命令。

二、组件介绍

        program_options 库的解析程序选项功能由 3 个基本组件构成 : 选项描述器、分析器和存储器。选项描述器定义选项及选项的值;分析器依据选项描述器的定义解析命令行或数据文件;存储器则把分析器的结果保存起来供程序员使用。

1.选项描述器

        选项描述器是选项分析的核心部分,包括3个基本类:value_semanti、coption_description和options_description。

(1)value_semantic选项值

        value_semantic 是选项描述器的核心类,它定义了选项值的语义信息,但我们通常不直接使用它,而是使用它的一个子类 typed_value。

        typed_value模板参数T支持int、double、string、vector<T>等类型。

        成员函数default_value( ) 与 implicit_value( )的行为有些类似。 default_value ()函数指定选项的默认值,当选项没有在命令行出现时将使用该值。implicit_value ( ) 则指定选项的隐含值,当选项出现但没有赋值时将使用该值。如果显式地给出值,长名必须用“ --x = y ”的形式,短名则要用“ -xy”的形式,即不使用空格或等号,将选项名与选项值连在一起。

        成员函数multitoken( )和 zero_tokens( )是两个设置属性的成员函数,它们表示 typed_value 可以接收多个或零个记号(token)。

        成员函数required ()要求必须提供选项值。

        成员函数composing ( ) 表示选项可以多次出现并且可以在最后被合并处理,这时类型 T 必须能够容纳这些值。

        typed_value的成员函数都返回 typed_value 的 this 指针,因此可以用箭头操作符连续调用它们,在一个表达式中完成对 typed_value 的设置。

        模板函数 value < T > ( )是一个便捷工厂函数,它可以生产出 typed_value < T > 对象,就像make_shared ( ) 。

opts.add_options()
    ("about,a", "this is a about.")
    ("version,v", "program version")
    ("dir,d", boost::program_options::value<std::vector<std::string>>()->multitoken(), "search dir.")
    ("filename", boost::program_options::value<std::string>()->default_value("test0.txt")->implicit_value("test1.txt"), "to find a file.");

(2)option_description

        option_description 用于描述单个选项,它提供了选项的名称、语法和详细的描述信息,并使用value_semantic 类来分析处理选项值。

class BOOST_PROGRAM_OPTIONS_DECL option_description {
public:
    option_description();
    option_description(const char* name,const value_semantic* s);
    option_description(const char* name,const value_semantic* s,const char* description);
}

        option_description的构造函数可接收选项名、选项值和描述信息。 

        在构造函数指定选项名时,我们可以同时指定长名和短名,并将两者用逗号隔开。长名在命令行中以双连字符开始,选项值用空格或等号跟在后面。短名以单连字符开始,使用空格连接后面的选项值,或者选项值紧挨着选项名。

        描述信息也可以被格式化,很长的描述信息可以用“ \n ”分段,每段的开头可以使用空格或“\t”来增减缩进以使格式更加美观。很遗憾,program_options 库只允许每段最多出现一个“\t”,因此如果文本很长,我们必须小心地排版格式。

boost::program_options::option_description opt("help,h", 0, "this is a help");

(3)options_description

        options_description 是 option_description 的一个聚合,它使用 vector<shared_ptr<option_description > > 来存储多个选项,提供了一个方便定义多个选项值的接口。可以用成员函数 add( )直接增加一个option_description 或一组 option_description 对象(另一个options_description ) ,但更方便的是使用 add_options ( ) 函数产生一个辅助对象,然后利用 operator( )向容器内连续插入选项信息对象。这种写法有利于编写格式规范的代码,易于阅读和维护代码。

void PrintOpts(boost::program_options::options_description& opts, boost::program_options::variables_map& vm)
{
    try {
        //实现选项处理逻辑
        if (vm.empty())
        {
            std::cout << "no options" << std::endl;
            return;
        }
        if (vm.count("about"))
        {
            std::cout << std::endl << opts << std::endl;
        }
        if (vm.count("version"))
        {
            std::cout << "version is V3.3.0" << std::endl;
        }
        if (vm.count("dir"))
        {
            std::cout << "dir 个数:" << vm["dir"].as<std::vector<std::string>>().size() << std::endl;
            for (auto str : vm["dir"].as<std::vector<std::string>>())
            {
                std::cout << str << ",";
            }
            std::cout << std::endl;
        }
        std::cout << "find " << vm["filename"].as<std::string>() << std::endl;
    }
    catch (...)//这里会拦截住所有try里没有被前面捕获的错误,但是你不知道是什么错误
    {

    }
}

int main(int argc, char* argv[])
{
    std::string filename;//外部变量
    boost::program_options::options_description opts("demo options"); 
    opts.add_options()
        ("about,a", "this is a about.")
        ("version,v", "program version")
        ("dir,d", boost::program_options::value<std::vector<std::string>>()->multitoken(), "search dir.")
        ("filename", boost::program_options::value<std::string>(&filename)->default_value("test0.txt")->implicit_value("test1.txt"), "to find a file.");

    boost::program_options::variables_map vm;//选项存储map容器
    boost::program_options::store(boost::program_options::parse_command_line(argc, argv, opts), vm);//解析存储

    boost::program_options::notify(vm);//通知variables_map 更新所有关联的外部变量 

    PrintOpts(opts, vm);
    return 0;
}

2.分析器

        program_options 库的分析器负责解析命令行参数,其行为好像字符串处理的分词动作,将命令行拆分成选项名与选项值,存储在basic_parsed_options 类中。

(1)parse_command_line

        最简单的分析器,它可以解析标准的 main( )函数入口的命令行参数。parse_command_line () 函数在内部调用command_line_parser 类,该类可以提供一些扩展功能。

boost::program_options::basic_parsed_options op = boost::program_options::parse_command_line(argc, argv, opts);//解析命令行

(2)parse_config_file

        parse_config_file( )函数可以解析配置文件,输入可以是文件名或输入输出流(istream ) ,这意味着我们可以让parse_config_file( )直接解析文件,或者我们可以预先从某处读入配置到流中,再进行解析。

basic_parsed_options<charT> parse_config_file(const char* filename, const options_description&, bool allow_unregistered = false);

        最后一个参数 allow_unregistered 如果为true,则允许配置文件中出现未定义的选项,使分析时不会抛出异常。

const char* str = "config.ini";
boost::program_options::parse_config_file<char>(str, opts, true);

std::ifstream ifs("config.ini");
boost::program_options::parse_config_file(ifs, opts, true);

        配置文件的基本格式是 name = value ,与常用的INI格式很类似,选项名必须使用长名,等号两边允许有空格。

(3)parse_environment

        parse_environment( )函数可以从环境变量中提取信息,这个功能对于很多程序都是非常有用的。我们要确定使用操作系统中的哪些环境变量,确定它们的名字,然后在我们的程序中为它们定义选项描述。

        举个栗子:获取Path、USERNAME、PROCESSOR_IDENTIFIER三个环境变量值,为了把环境变量名转换到定义的选项名,我们需要自定义一个名字转换函数name_mapper ( ),它把环境变量名映射成选项名。

std::string name_mapper(const std::string& any_name)
{
    //三个环境变量名字转换
    static std::map<std::string, std::string> nm = { {"Path","path"},{"USERNAME","username"},{"PROCESSOR_IDENTIFIER","processor-identifier"} };
    return nm[any_name];
}
int main(int argc, char* argv[])
{
    std::string filename;
    boost::program_options::options_description opts("demo options"); //
    opts.add_options()
        //三个环境变量都需要加上,否则报异常 boost unrecognised option '%canonical_option%'
        ("path,p", "environment path")
        ("username,u", boost::program_options::value<std::string>()->default_value("Tom"), "environment username")
        ("processor-identifier,i", "environment processor identifier")

        ("about,a", "this is a about.")
        ("version,v", "program version")
        ("dir,d", boost::program_options::value<std::vector<std::string>>()->multitoken(), "search dir.")
        ("filename", boost::program_options::value<std::string>(&filename)->default_value("test0.txt")->implicit_value("test1.txt"), "to find a file.");//两个程序选项

    boost::program_options::variables_map vm;//选项存储map容器
    boost::program_options::store(boost::program_options::parse_command_line(argc, argv, opts), vm);//解析存储
    //boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(opts).allow_unregistered().run(), vm);//解析存储

    try {
        boost::program_options::store(boost::program_options::parse_environment(opts, name_mapper), vm); //分析环境变量
    }
    catch (boost::program_options::error e)
    {
        std::cout << e.what() << std::endl;
    }

    boost::program_options::notify(vm);//通知variables_map 更新所有关联的外部变量 

    if (vm.count("path")) {
        std::cout << "path:" << vm["path"].as<std::string>() << std::endl;
    }
    if (vm.count("username")) {
        std::cout << "username:" << vm["username"].as<std::string>() << std::endl;
    }
    if (vm.count("processor-identifier")) {
        std::cout << "processor-identifier:" << vm["processor-identifier"].as<std::string>() << std::endl;
    }
    return 0;
}

3.存储器

        存储器是 program_options 库里的最后一个组件,它接收分析器的结果,用 store( )函数存储 variables_map 对象,再调用 notify( )把值更新到用户指定的变量。

        variables_map 是 std::map 的子类,它具有std::map 的所有功能。另外,它还提供了一个专门的成员函数notify( ),用于把选项值更新到 value_semantic 关联的外部变量中。之前我们看到的自由函数notify( )是成员函数notify( )的一个等价操作。

        variables_map 的键类型是 string ,它存放了选项描述器 option_description 定义的选项名。在使用 variables_map 时我们只能使用选项的长名,使用短名索引运行时会发生异常。

        variables_map 的值类型是 any 对象的一个包装类variable_value ,它的成员函数 as<T> ( )包装了 any_cast < T > (),取出 any 内存放的选项值。因此 as < T >( )函数的模板类型必须与存储的类型相符,否则会抛出 bad_any_cast 异常。

        store ( ) 函数接收分析器的输出basic_parsed_options,逐个遍历分析结果,将选项值存储到 variables_map 中。 store ()函数允许多次调用,可以实现同时从不同的数据源获得选项信息,比如同时使用命令行和配置文件,已经有值的选项如果再进行 store( )会自动被忽略,除非它可以接收多个记号。

三、扩展

1.位置选项值

        用之前的代码查找文件名时必须使用--flename来指定,这显得很麻烦,最好能够不用选项名,直接在命令行中写出文件名。

        program_options 库将这种没有选项名的选项称为位置选项,它的描述要使用positional_options_description类来辅助。

        positional_options_description类的主要功能集中在 add ( ) 函数 , 它指定选项的名字 name 和出现的个数 count 。add ( ) 函数返回对象自身的引用 , 因此可以用连续调用的方式添加选项。当count == -1 时表明位置选项的设置结束 , 不能再添加位置选项。

        位置选项的解析较为复杂 , 我们不能再使用parse_command_line( ) 函数 , 而要使用command_line_parser 类它的用法与parse_command_line( ) 函数类似 , 接收命令行参数,然后调用 options 传入options_description 对象 , 最后用 run ( ) 进行解析。 

boost::program_options::positional_options_description posDes;
posDes.add("filename", 1).add("dir",2);
boost::program_options::store( boost::program_options::command_line_parser(argc, argv).options(opts).positional(posDes).run(),vm);

        位置选项不支持默认的位置,一旦指定了位置的个数,我们就必须在命令行上顺序提供所有的参数。因此 , 在使用位置选项时请谨慎设置 count参数,除非必要,其他情况都应该是 1。

2.未知选项

        program_options 库的默认行为不允许出现未定义选项,如果命令行中出现了未在options_description 中定义的选项名则会抛出异常。但这通常不是一个好的选择,它迫使我们必
须用 try–catch 块来保护选项分析代码。

        command_line_parser 的成员函数allow_unregistered ( ) 允许我们改变这个行为,使用它类似在 parse_config_file( )函数中置 allow_unregistered 为 true 。当使用了allow_unregistered ( ) 函数后,command_line_parser 遇到未定义但又“像”是选项的命令行参数时,会把它保存在分析结果parsed_options 对象中,而不抛出异常。可以用 collect_unrecognized( )获得这些无法识别的参数。

boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(opts).allow_unregistered().run(), vm);

3.命令行风格

        program _ options 库使用的程序选项默认是 UNI Ⅸ风格,使用“ -- ”和“ - ”指定选项名,但如果用户喜欢,它也可以改为使用 Windows 风格的“ / ”指定选项名。

        command_line_parser的 style( )函数可以改变分析器的行为,它接收一个command_line_style的枚举值,默认是unix_style 。如果增加 allow_slash_for_short值,则可以在短名选项前使用斜杠。

boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(opts).style(boost::program_options::command_line_style::allow_slash_for_short| boost::program_options::command_line_style::default_style).allow_unregistered().run(), vm);//解析存储

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烫青菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值