解析命令行参数,这个看起来很简单,但是其中有不少小问题,在这里我先记录下这几天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 ,
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 fPtr;
30 vector > data_;
31 static const char * style_[];
32 options_description opts;
33 };
parser是对外的接口,负责解析命令行参数,init_option_data和special_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 >::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 specia