命令行解释框架

#include "stdafx.h"
#include "Cmder.h"
#include <iostream>
#include <string>
#include <iomanip>
using namespace std ;

class Test: public Cmder
{
public:
BEGIN_OPT // 类似于 MFC 的消息映射表, 不过这里所映射的是选项名和选项所需要的参数个数.
MAP("-b", 2) // 如果选项的参数多余指定数量, 则将多余的参数移动至命令行参数.
MAP("-c", 4) // 如果选项的参数少于指定数量, 则将问题记录到 m_err 中.m_err 是用于记录 Fatal Error 的 vector 容器.
END_OPT

} ;

int main()
{
Test cmd ;
string str = "cmd argu1 -b \"a\" \"b\" \"c d\" \"e\" \"f\" -c \"I'm \\\"walfud\\\".\" -z A\\-Z -c dulplication" ;

cout <<"原始输入: " <<endl <<str <<endl ;

cmd.set(str) ;
cmd.handle() ;

cout <<"解析后: " <<endl ;
for (int i = 0 ;i < (int)cmd.m_opt.size() ;i++)
{
cout <<setw(5) <<cmd.m_opt[i].name <<": " ;
for (int j = 0 ;j < (int)cmd.m_opt[i].param.size() ;j++)
{
cout <<"'" <<cmd.m_opt[i].param[j] <<"' " ;
}
cout <<endl ;
}

// 以下为调试信息.
for (int i = 0 ;i < (int)cmd.m_warn.size() ;i++)
{
cout <<cmd.m_warn[i] <<endl ;
}
for (int i = 0 ;i < (int)cmd.m_err.size() ;i++)
{
cout <<cmd.m_err[i] <<endl ;
}

return 0 ;
}

 
// Cmder.h
//

#ifndef CMDER_H
#define CMDER_H

#include <string>
#include <vector>

class Opt
{
public:
Opt(){}
~Opt(){}
Opt(const Opt &other)
{
*this = other ;
}
Opt &operator=(const Opt &other)
{
if (&other != this)
{
this->name = other.name ;
this->param = other.param ;
}

return *this ;
}
public:
// Interface
void clear()
{
name.clear() ;
param.clear() ;
}

std::string &operator[](int i)
{
return param[i] ;
}

public:
std::string name ;
std::vector<std::string> param ;
} ;

class Opt_Map
{
public:
Opt_Map(const std::string opt, int param)
{
name = opt ;
num = param ;
}
~Opt_Map()
{}

void clear()
{
name.clear() ;
num = 0 ;
}

std::string name ; // Option name.
int num ; // Number of option arguments.
} ;

/*************************************************************/
/*
* This architecture is learned from MFC's Message Mapping.
*/
#define BEGIN_OPT int add_opt_map() {
#define MAP(name, num) m_opt_map.push_back(Opt_Map(name, num)) ;
#define END_OPT return 0 ; }
/*************************************************************/

class Cmder
{
public:
Cmder() ;
virtual ~Cmder() ;
Cmder(const Cmder &other) ;
Cmder &operator=(const Cmder &other) ;
public:
// Interface
void clear() ;
bool empty() ;

int isok() ; // Return 0 if no any warning and error.
// 1 only warning.
// 2 only error.
// 3 both warning and error.

const std::string &at(int opt, int param = 0) ;

void set(const std::string &Cmder) ; // Set original command line.
const std::string &get() ; // Get original command line.

int handle() ; // Call some procedures, switch orignal command line into structure.

protected:
// logic
int prepare() ; // Split command line into word.
int analyse() ; // Put word into corresponding field.
int recombine() ; // Recombine arguments into corresponding option.

virtual int add_opt_map() = 0 ; // This function is defined by client programmer using definition like Message Mapping in MFC.
int map(Opt &src, Opt &dest, const Opt_Map &map) ; // Both 'src' and 'dest' are changeable.
bool isopt(const std::string &str) ; // Option is a string started with a '-'.
bool isparam(const std::string &str) ; // Parameter is a string which is neither command nor option.
public:
// data
std::string m_cmd ; // Original command line.

std::vector<Opt> m_opt ; // Structure to store option and their arguments.
std::vector<std::string> m_words ; // Original command line will be split into several words. String between two quoter is one word.
std::vector<Opt_Map> m_opt_map ; // This is defined by client programmer.

std::vector<std::string> m_warn ; // Warning. Command is still valid.
std::vector<std::string> m_err ; // Fatal error. Command is invalid.
} ;

/*
* Faq:
* "server start -f xxx"
* command argument: 'start' is a argument of command.
* option argument: 'xxx' is argument of option '-f'.
*/

#endif // CMDER_H
// Cmder.cc
//

#include "stdafx.h"
#include "Cmder.h"
#include "Convert.h"
#include <sstream>
#include <string>
#include <algorithm>
#include <iterator>
using namespace std ;

Cmder::Cmder()
{}
Cmder::~Cmder()
{}
Cmder::Cmder(const Cmder &other)
{
*this = other ;
}
Cmder &Cmder::operator=(const Cmder &other)
{
if (&other != this)
{
this->m_cmd = other.m_cmd ;
this->m_opt = other.m_opt ;
}

return *this ;
}

// Interface
void Cmder::clear()
{
m_cmd.clear() ;
m_opt.clear() ;
m_words.clear() ;
m_warn.empty() ;
m_err.empty() ;
}
bool Cmder::empty()
{
return m_cmd.empty() ;
}

int Cmder::isok()
{
return (m_warn.empty() ? 0 : 1) | (m_err.empty() ? 0 : 2) ;
}

const std::string &Cmder::at(int opt, int param)
{
return m_opt[opt][param] ;
}

void Cmder::set(const std::string &cmd)
{
m_cmd = cmd ;
}
const std::string &Cmder::get()
{
return m_cmd ;
}

int Cmder::handle()
{
if (m_opt_map.empty())
{
add_opt_map() ;
}
prepare() ;
analyse() ;
recombine() ;

return 0 ;
}

// logic
int Cmder::prepare()
{
// Put word into vector.
int len = m_cmd.length() ;
int i = 0, j = 0 ;
bool wait_bracket = false ;
for (;i < len ;i++)
{
if (!wait_bracket)
{
if (isspace(m_cmd[i]))
{
string word ;
word.assign(m_cmd.c_str()+j, i-j) ;
if (!word.empty())
{
m_words.push_back(word) ;
}

j = i + 1 ;
}
else if ('"' == m_cmd[i])
{
j++ ;
wait_bracket =true ;
}
}
else
{
if ('\\' == m_cmd[i])
{
i++ ;
}
else if ('"' == m_cmd[i])
{
string word ;
word.assign(m_cmd.c_str()+j, i-j) ;
if (!word.empty())
{
m_words.push_back(word) ;
}

j = i + 1 ;
wait_bracket = false ;
}
}//if !wait_bracket
}//for
if (j < len)
{
string word ;
word.assign(m_cmd.c_str()+j, m_cmd.length()-j) ;
m_words.push_back(word) ;
}

// Convert Escape character.
bool escape = false ;
for (int i = 0 ;i < (int)m_words.size() ;i++)
{
string &word = m_words[i] ;
for (int j = 0 ;j < (int)word.length() ;j++)
{
if ('\\' == word[j] && escape)
{
string::iterator to_del = int_to_iter(word, j) ;
word.erase(to_del) ;

escape = false ;
}
else if ('\\' == word[j] && !escape)
{
escape = true ;
}
else if ('\\' != word[j] && escape)
{
// No convertion, just erase single escape.
string::iterator to_del = int_to_iter(word, j-1) ;
word.erase(to_del) ;

escape = false ;
}
else if ('\\' != word[j] && !escape)
{
escape = false ;
}
}//for j
}//for i

return 0 ;
}
int Cmder::analyse()
{
// Create first option, for command arguments
Opt opt_cmd ;
m_opt.push_back(opt_cmd) ;

// Other option arguments
int index = 0 ;
int len = m_words.size() ;
for (int i = 1 ;i < len ;i++)
{
if (isopt(m_words[i]))
{
// Find the corresponding option.
for (index = 1 ;index < (int)m_opt.size() ;index++)
{
if (m_opt[index].name == m_words[i])
{
break ;
}
}

// Insert argument into corresponding place.
if ((int)m_opt.size() <= index)
{
// Add new option.
Opt tmp ;
tmp.name = m_words[i] ;
m_opt.push_back(tmp) ;
}
else
{
// Warning: option duplication
string warn = "Warning: " ;
warn += "Option Duplication: " ;
warn += "'" + m_words[i] + "'" ;
m_warn.push_back(warn) ;
}//if (int)m_opt.size()
}
else if (isparam(m_words[i]))
{
m_opt[index].param.push_back(m_words[i]) ;
}//if isopt
}//for

return 0 ;
}

int Cmder::recombine()
{
// Every option will do mapping through 'm_opt_map'
for (int i = 1 ;i < (int)m_opt.size() ;i++)
{
for (int j = 0 ;j < (int)m_opt_map.size() ;j++)
{
// If this option is defined by client programmer,
// the mapping will transfer redundant option arguments to 'm_opt[0]'(as command arguments).
if (m_opt[i].name == m_opt_map[j].name)
{
map(m_opt[i], m_opt[0], m_opt_map[j]) ;
}
}//for j
}//for i

return 0 ;
}

int Cmder::map(Opt &src, Opt &dest, const Opt_Map &map)
{
// Option arguments is less than needed!
if ((int)src.param.size() < map.num)
{
string err = "Error: " ;
err += "Less of arguments. Need " + int_to_str(map.num) + ": ";
err = err + "'" + src.name + "'" ;
m_err.push_back(err) ;

return -1 ;
}

for (int i = map.num ;i < (int)src.param.size() ;i++)
{
dest.param.push_back(src.param[i]) ;
}

src.param.erase(int_to_iter(src.param, map.num), src.param.end()) ;

return 0 ;
}

bool Cmder::isopt(const std::string &str)
{
return '-' == str[0] ;
}
bool Cmder::isparam(const std::string &str)
{
return !isopt(str) ;
}

因为用到了我的函数库 Convert, 所以也贴在下边

// convert.h
// This file contains many function about convert one form to another. In principle, the two are same.

#ifndef CONVERT_H
#define CONVERT_H

#include <string>

/*
* Convert container index to iterator
* Param:
* t: A container.
* i: The index you want to get as a iterator.
* Rtn:
* A iterator index on 'i'.
*/
template <typename T>
typename T::iterator int_to_iter(T &t, int i)
{
typename T::iterator it = t.begin() ;
while (it != t.end() && 0 < i--)
{
it++ ;
}

return it ;
}

/*
* Convert integer number into std::string.
* Param:
* num: Integer number want to convert.
* radix: Radix of number
* Rtn:
* String express number.
* Note:
* Not portable!
* Default radix is 10.
*/
std::string int_to_str(int num, int radix = 10) ;

#endif // CONVERT_H
// Convert.cpp
//

#include <stdafx.h>
#include "Convert.h"
using namespace std ;

#pragma warning(disable: 4996) // '_itoa': This function or variable may be unsafe.


string int_to_str(int num, int radix)
{
// type 'int' is 32-bit, 10 characters and a '\0' is enough.
char buf[11] = {'\0'} ;
return _itoa(num, buf, radix) ;
}



posted @ 2011-11-07 22:21  walfud  阅读(1307)  评论(1编辑  收藏  举报