命令行参数解析的一些心得

解析命令行参数,这个看起来很简单,但是其中有不少小问题,在这里我先记录下这几天coding的一些心得体会,和大家共同探讨一下。

首先明确需求。

要解析命令行参数,最起码得有输入输出吧。

输入一般有这么几种:

1.跟在可执行文件后的一长串参数选项,表达式

2.配置文件

3.标准输入,这个可以由其他程序的输出通过管道重定向而来。

输出:

这个就比较单一了,要么屏幕,要么文件。

看起来输入比较麻烦,那就主要分析一下输入吧。上面这三种输入方式其实都可以通过参数选项进行控制,所以进而归类到如何解析参数选项和表达式呢。

我选用了boost::program_options和boost::property_tree两个库来帮忙。

这两者的设计目的截然不同,program_options主要是用来parse入口参数,它的存储介质是variable_map,这是个std::map的继承版本。本质上也是键值有序对,所以很显然,variable_map的层次结构只有一层。

而property_tree的设计目的是用来存储层次结构的数据,所以里面有路径这一说法,但是我觉得很奇怪,为啥program_options不使用property_tree作为存储介质呢?

借助property_tree解析xml,json,ini,info的能力,program_options可以很方便的存储和读取配置文件啊。

所以我决定把两者拼起来。

首先设计一个基类:

1 class parser_cmd_line 2         { 3         public: 4                 /**  5                  * Parse the main parameters. It depends on the init_option_data and special_parser function \n 6                  *  7                  * @param argc arguments number from 8                  * @param argv arguments from main 9                  */10                 void parser(int argc, char *argv[]);11                 virtual ~parser_cmd_line(){}12         protected:13                 /** 14                  * This function init the data_, which needs to be rewrote, it includes sereral steps: \n15                  * 1. Add option term with long and short term. \n16                  * 2. Add option description and option handler. \n17                  * 3. Both terms above are pair format <long term, short term>, <description, handler>18                  * 4. The handler uses boost::function, which should bind to the function you need. 19                  */20                 virtual void init_option_data(){}21                 /** 22                  * Add a user designed parser, you can parse some special style input as the "style_" offered.23                  * It also needs to be rewrote.24                  * @param argc 25                  * @param argv 26                  */27                 virtual void special_parser(int argc, char *argv[], const char * style[] = style_){}28 29                 typedef function<void(const std::string&)> fPtr;30                 vector<tuple<string,string,string,fPtr> > data_;31                 static const char * style_[];32                 options_description opts;33             };




parser是对外的接口,负责解析命令行参数,init_option_dataspecial_parser留给用户实现,主要是用来初始化一些内建的选项,和对特定形式的表达式进行解析。往往得针对每个选项,都会有些特定的操作,所以我在此使用了boost::function库,它可以很方便的绑定函数,传递变量。但是有利也有弊端,使用boost::function带来的一个问题是,我无法在map中建立这样的有序对,因为map本质上是一个同源容器,它无法存储不同类型的数据。(参见http://stackoverflow.com/questions/646737/map-of-boost-function-of-different-types)所以我只能换tuple了,这也是boost中的一个库,有点类似范化的pair,可以比pair容纳更多元素。

有了这些定义,下面实现parser就变得很简单了。program-option还有个缺陷,即假如输入为定义的选项时会丢出一个异常,其实这往往不是用户想要的,比较好的方式是给出一个提示,这就是第21-27行干的事。

1 void parser_cmd_line::parser(int argc, char * argv[]) 2   { 3     init_option_data(); 4  5     typedef vector<tuple<string,string,string,fPtr> >::const_iterator vci; 6     for(vci it = data_.begin(); it != data_.end(); ++it) 7       { 8         std::string option_name = it->get<0>(); 9         if(!it->get<1>().empty())10           {11             option_name += ",";12             option_name += it->get<1>();13           }14         const string &desc = it->get<2>();15         opts.add_options()(option_name.c_str(), desc.c_str());16       }17 18     special_parser(argc,argv,style_);19     20     variables_map vm;21     BOOST_AUTO(pr, command_line_parser(argc,argv).options(opts).allow_unregistered().run());22     vector<string> ur_opts = collect_unrecognized(pr.options, include_positional);23     BOOST_FOREACH(string str, ur_opts) 24       {25         if(str.find(style_[1]) == std::string::npos)26           std::cerr << "Unknown option: " << str << std::endl;27       }28     store(pr,vm);29     notify(vm);30     31     if(vm.size() == 0 && argc == 1)32       {33         std::cout << opts << std::endl;34         return ;35       }36 37     for(vci it = data_.begin();    it != data_.end(); ++it)38       {39         const std::string& key = it->get<0>();40         if(vm.count(key.c_str()))41            it->get<3>()(key);42       }43     44   }


用户可以继承这个基类后,添加选项,描述,想对应的处理函数等等。

等等,你是不是发现啥问题了,对,按照我这样的写法,你是无法实现 -L./xxx.so这样的功能的。我并没有提供一个参数后跟一个输入的形式,主要原因是我觉得这样的例子并不直观,我更倾向于L=xx.so这样的表达式,所以我提供了special_parser这个函数,你可以很方便的扩展成你想要的style。

当然现在的这个功能还是很单一的,比如因为我的handler是以此调用,所以假如你的选项之间有依赖关系的话,在添加时就得格外小心了。

暂时就这些,肯定还有很多理解不到的地方,请大家多多指教阿。
原文链接: https://www.cnblogs.com/jtf-china/archive/2011/10/22/2220769.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/34865

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月8日 上午11:47
下一篇 2023年2月8日 上午11:47

相关推荐