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

解析命令行参数,这个看起来很简单,但是其中有不少小问题,在这里我先记录下这几天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: \n
15 * 1. Add option term with long and short term. \n
16 * 2. Add option description and option handler. \n
17 * 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中建立<key,function>这样的有序对,因为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是以此调用,所以假如你的选项之间有依赖关系的话,在添加时就得格外小心了。

暂时就这些,肯定还有很多理解不到的地方,请大家多多指教阿。

posted on 2011-10-22 00:24  闲人草堂 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/jtf-china/archive/2011/10/22/2220769.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值