C++可复用代码:命令行控制模块

大二第一学期的数据结构课程设计中,我写的是一个族谱管理系统,用C语言写的win console application,黑咕隆咚的,但是程序控制方式我采用的是类似linux shell那样的命令行模式。后来觉得实现命令行控制的那部分代码可以复用,所以在大二下学期用C++对这个模块进行了改写,写出了我自认为可复用的代码。

这学期,临近考试月我们有操作系统课程设计,要求在linux下模拟实现一个命令解释器,则上面提到的那个命令行控制模块的代码正好派上了用场。下面是我写的命令解释器的main.cpp代码,这个shell只有9条命令。

 CmdNode<ShellFunSetshellCmds[9] = {//该数组的每一个元素对应该shell的一条命令

    {"pwd",&ShellFunSet::pwd},//{"命令字符串",执行该命令的函数}

    {"dir",&ShellFunSet::dir},

    {"cd",&ShellFunSet::cd},

    {"newdir",&ShellFunSet::newdir},

    {"deldir",&ShellFunSet::deldir},

    {"exit",&ShellFunSet::exit},

    {"rename",&ShellFunSet::rename},

    {"find",&ShellFunSet::find},

    {"date",&ShellFunSet::date}

  };

  ShellFunSet osFunHolder;

  CmdControl<ShellFunSet> cmdModul("YeShizhe@",9,osFunHolder,shellCmds);

  cmdModul.run();//进入命令行控制模式

以上代码中,可复用代码由两部分组成:

结构体 CmdNode<T>

 CmdControl<T>

复用该代码的方法就是根据需要编写自己的T类,然后将它作为以上俩物的持有类。

我知道直到现在为止我什么都没讲清楚,但请耐心看下去,下面是ShellFunSet的代码,

该类不属于可复用代码的范畴。

class ShellFunSet{

 public:

  bool pwd(string order[]);

  bool dir(string order[]);

  bool cd(string order[]);

  bool newdir(string order[]);

  bool deldir(string order[]);

  bool exit(string order[]);

  bool date(string order[]);

  bool rename(string order[]);

  bool find(string order[]);

};

函数内的具体代码就不展开了,对代码讲解无帮助。可以看到,ShellFunSet其实是一个函数的集合,里面的函数都有一个共同特点:

1.返回值类型为bool

2.参数类型为string数组(根据C/C++语言特性,其实更应该说是string型指针)。

只有持有按这种规范编写的函数的类才能作为CmdNode<T>以及CmdControl<T>的T类。至于函数为什么必须返回bool值,等下会说的,别急~

接下来说下CmdNode,从第二行代码后的注释可以看出,这个结构体只有两个数据成员,一个是string,代表这个shell内置的命令。另一个则是函数指针型数据,指向返回值为bool,参数为string数组的函数,我将其typedef为pFun。因此在上面代码中,初始化CmdNode数组shellCmds[9]时用到了类似这样的代码  {"pwd",&ShellFunSet::pwd},那个取址算符&取的其实就是ShellFunSet中某个函数的地址,将此地址作为函数指针赋值给CmdNode中的pFun型数据成员。通过这种方式编写代码能够实现如下功能:当用户敲入的命令匹配了某一CmdNode实例的string型数据成员时,程序能通过某种机制调用与该string成员相关联的T类中的函数,执行该命令对应的具体功能。例如{"pwd",&ShellFunSet::pwd}中,当用户敲入"pwd"时,shell程序就会调用ShellFunSet中的bool pwd(string order[])函数,在这个简易shell程序中pwd命令的功能是显示当前工作路径。

附上CmdNode的代码:

template <class T>

struct CmdNode{

  typedef bool (T::*pFun)(string *);

  string command;

  pFun fun;

};

关于这个某种机制(我特意在上面把它高亮了出来,呵呵~)其实就是CmdControl<T>,如果说在这个简易Shell程序中,CmdNode<T>数组shellCmds是它的数据结构,那么这个CmdControl<T>就是这个程序的算法。恩,我知道这种说法很扯,此程序压根不涉及啥稍微有内涵的数据结构与算法,但这只是个比喻。

CmdControl<T>的功能是:获取用户输入的命令及参数并进行匹配,然后执行相应功能,或者在用户输入错误命令时进行报错(注意,只是对错误的命令进行报错,参数错了的话CmdControl<T>是不会去管的),控制命令行模块的启停。

CmdControl<T>的初始化方式如下:

CmdControl<ShellFunSet> cmdModul("YeShizhe@",9,osFunHolder,shellCmds);

参数"YeShizhe@"是自定义的命令行提示符。整型常量9代表这个简易shell内置的命令条数,shellCmdsCmdNode<ShellFunSet>的数组,它包含9个CmdNode<ShellFunSet>型元素。osFunHolderShellFunSet类实例。这里请允许我再次强调下:CmdControlCmdNode属于可复用的代码,而ShellFunSet则是我根据这次操作系统课程设计要求而写的实现简易shell的具体功能代码。

然后,用这样的代码启动这个命令行控制模块cmdModul

cmdModul.run();

至于怎么停止,这个就要靠ShellFunSet中函数的返回值:bool

当用户输入的命令对应了一个函数,这个函数的返回值是false,一旦cmdModul调用到了该函数就会停止对用户输入的接收,退出命令行控制模式。例如在课程设计中我为"exit"命令写了这么个函数bool exit(string order[]){ return false;},简单得坑爹,但就是它实现了用户输入"exit"时退出shell的这么一个功能。

CmdControl<T>的代码如下:

template <class T>

class CmdControl {

 protected:

  T holder;//客户程序员根据需要编写的功能类

  string prompt;//命令提示符

  string cmdNotExist;//自定义的错误命令提示信息

  int cmds;//内置命令数

  bool running;//用于判断模块处于运行状态

  CmdNode<T> *cmdList;//内置命令列表

  string* getOrder();//返回字符串数组,存储用户输入的命令及参数

  int match(string order);//返回匹配用户输入的命令在cmdList中的下标

 public:

  void run();

  //接收参数:命令条数、命令持有类、命令与函数的关联数组

  CmdControl(string prmpt,int num,T Holder,CmdNode<T> *CMD);

};

重点说下getOrder()这个函数,该函数实现的功能是:

1.接收一行用户输入。

2.根据空格将用户输入分割成字符串数组。数组中下标为0的元素的第一个字符存储的是一个整型变量,代表有效字符串数,即一条命令及其所带参数所对应的字符串数。

代码:

string* CmdControl<T>::getOrder(){

  string order;//该字符串是用户输入的原始字符串,包括空格符

  getline(cin,order);

  int num = 1;

  deque<string> orders;//以空格为分隔符,将截取到的子串存入该容器中

  int from = 0;//子串头部下标

  int len = 1;//子串长度

  for(int i = 0; i < order.size();i++){

    if(order[i] == ' '){//读到空格符时将之前标记的字符串存入容器

      orders.push_back(order.substr(from,len-1));

      from = i+1;

      len = 1;

      num++;

    }

    else len++;

  }orders.push_back(order.substr(from));//存入读得的最后一个子串

  string *result = new string[num+1];//result是要返回的字符串数组,预留出0号单元

  result[0].append(1,(char)num);//0号字符串存储的是数量信息

  deque<string>::iterator itr = orders.begin();

  for(int i = 1;i <= num; i++){//将容器里的字符串存入result数组

    result[i] = *itr;

    itr++;

  }

  return result;

}

最后是run函数,该函数调用getOrder与match函数进行命令的匹配及对应函数调用:

  void run() {

    running = true;

    while(running){

      cout<<prompt<<":";//输出命令行提示符

      string *orderInput = getOrder();

      if(orderInput[1].length() == 0) continue;//用户仅只敲了个回车

      //将加工后的参数数据传递给相应函数

      int funNum = match(orderInput[1]);//若未匹配得内置命令则返回-1

      if(funNum == -1) cout<<cmdNotExist<<endl;//并输出自定义的错误提示信息

      //否则funNum为命令在cmdList中的下标,并调用相应函数执行命令功能

      else running = (holder.*(cmdList[funNum].fun))(orderInput);

    }

  }

可能在多道程序环境中这个应该写成一个线程,但现在还没去实现它。

恩,基本就是这样,希望能得到高手的指教,平手的讨论,新手的提问,哈哈~

源代码下载页:http://download.csdn.net/detail/ye_shizhe/4915429

posted on 2012-12-21 22:27  denallo  阅读(533)  评论(0编辑  收藏  举报

导航