实现自己的“命令映射表”(下)

在上一篇中,我们讨论了如何设计一个“命令映射表”,但是上篇对应于表中“命令”的所有函数都是同一类型的,均为返回值为 void,参数为 void 的函数。

如果我们要根据不同的“命令”来执行不同类型的函数要怎么办呢? (这里的不同指的是执行函数的返回值可能不同,参数列表可能不同。)。好,我们下面就开始讨论这个问题。

可能这篇比上篇会难一点,拿出你程序员的毅力来。想想搞定了以后,我们就可以在程序中使用这种手法,可以明白MFC的消息映射表机制(其实这里非常类似于MFC的消息映射机制),还可以拓展自己的设计思路,提高自己的设计水平。最重要的是,使用它能够为你的程序带来好处。OK, Let's GO!


XXX.H 文件

1、首先,要把 "命令" 和 "函数" 对应起来,map是必不可少的,函数指针也是不能少的,而命令是 CString 类型的,所以我们在类里写下这两个东西。

如下:

class CParser
{
 ...

public:
 typedef void (CParser::*FuncPointer)(void);
 map<CString, FuncPointer> m_cmdMap;
 
 ...
}

2、我们现在是要执行不同类型的函数,上面的函数指针只是一种类型(返回值为void, 参数列表为void)。

所以,这里要定义各种类型的函数指针。

而且还需要一个enum结构来标示我们需要的函数类型。(因为函数指针都是4个字节的,可以说是无类型的,我们要另外定义类型来标示它,以便能判断选择),好,思路有了,赶快写下来:

// 要前置声明一下,因为下面这些代码不写在类里了,写在类的外头/前面
class CParser;

typedef void (CParser::*FuncPointer)(void);

// 定义一个 union 结构来装各种的函数指针

// 实际这里是使用 union 来做转换
// 因为函数指针在 union CmdFunctions 中定义,都是 4 bytes,
// 所以利用 union 的特性可顺利做转换
union CmdFunctions
{
 FuncPointer pfn;
 
 void (CParser::*pfn_vs)(CString&);  // 返回值为 void, 参数为 CString,注意pfn_vs的命名,表示返回值为void,参数为CString
 void (CParser::*pfn_vsi)(CString&, int); // 返回值为 void, 参数为 CString, int 注意命名,同上。
};

// 定义一个 enum 结构来标示我们的函数类型
enum FUNC_TYPE
{
 FUCN_TYPE_VOID_STRING,   // 函数类型:返回值为void,参数为:string
 FUCN_TYPE_VOID_STRING_INT  // 函数类型:返回值为void,参数为:string, int
};

3、好了,写下了上面两个关键的东西,我们要考虑一下,如何使用map把命令和函数指针对应起来了。

由于STL map只有两个参数,所以,我就要自定义一个结构放进map的第二个参数中去,这个结构包括了函数指针,函数类型,那我们下面就可以根据命令找到这个结构体,然后根据结构体中的函数类型调用相应的函数指针了。嗯,写下来:


// 在类的外头再定义一个结构体
typedef struct FunctionInfo
{
 enum FUNC_TYPE funcType; // 函数类型
 FuncPointer pfn;  // 函数指针

}FuncInfo;


// 在类里
class CParser
{
 ...

public:
 map<CString, FuncInfo> m_cmdMap; // map 改成这个样子了
 
 ...
}

好了,该定义的变量、结构等等的都搞定了,我们可以到  CPP 文件建立表和尝试利用命令来调用函数了。

XXX.CPP 文件

1、首先建表,在构造函数中

CParser::CParser()
{
 FuncInfo fi;
 
 // 下面要注意函数类型和函数指针类型

 fi.funcType = FUCN_TYPE_VOID_STRING;
 fi.pfn = (FuncPointer) ( void(CParser::*)(CString&) ) &CParser::MethodA;
 m_cmdMap["#a"] = fi;

 fi.funcType = FUCN_TYPE_VOID_STRING_INT;
 fi.pfn = (FuncPointer) ( void(CParser::*)(CString&, int) ) &CParser::MethodB;
 m_cmdMap["#b"] = fi;
}

由于map的限制,这里很难写成宏,可以写成函数,稍微简化一下。在MFC中是对每个不同的 ON_XXX 宏作不同的函数指针类型对应,这里不展开说明。
我们写一个函数来简化和统一建表操作。

void CParser::SetCmdMap(LPCTSTR strCmd, enum FUNC_TYPE ft, FuncPointer pfn)
{
 FuncInfo fi;
 fi.funcType = ft;
 fi.pfn = pfn;
 m_cmdMap[strCmd] = fi;
}

那我们的建表就可以简化成:

CParser::CParser()
{
 SetCmdMap("#a", FUCN_TYPE_VOID_STRING,  (FuncPointer) ( ( void(CTestCmdMapDlg::*)(CString&) ) &CParser::MethodA ) );
 SetCmdMap("#b", FUCN_TYPE_VOID_STRING_INT, (FuncPointer) ( ( void(CTestCmdMapDlg::*)(CString&, int) ) &CParser::MethodB ));
}

2、根据命令找到相应的函数并执行

我们下面就可以根据命令来寻找表中对应的函数了。代码如下,和上篇的代码差不多,就是多了个函数指针类型的判断选择 switch-case。

void CParser::ExecCmd(CString &strCmd, CString &strParam)
{
 if(m_cmdMap.find(strCmd) != m_cmdMap.end())
 {
  enum FUNC_TYPE ft;

  // 由于函数指针在 union CmdFunctions 中定义,都是 4 bytes,
  // 所以利用 union 的特性可顺利做转换
  union CmdFunctions cfs; 

  ft = m_cmdMap[strCmd].funcType;
  cfs.pfn = m_cmdMap[strCmd].pfn;
 
  switch(ft)
  {
  case FUCN_TYPE_VOID_STRING:
   (this->*cfs.pfn_vs)(strParam);  // 利用 union 的特性转换
   break;
  case FUCN_TYPE_VOID_STRING_INT:
   (this->*cfs.pfn_vsi)(strParam, 1); // 利用 union 的特性转换
   break;
  default:
   AfxMessageBox("没有定义函数类型。");
   break;
  }
 }
 else
 {
  CString strFmt;
  strFmt.Format("找不到/"%s/"对应的函数。", strCmd);
  AfxMessageBox(strFmt);
 }
}

3、写上对应的函数

void CParser::MethodA(CString &strParam)
{
 AfxMessageBox(strParam);
}

void CParser::MethodB(CString &strParam, int nParam)
{
 CString strFmt;
 strFmt.Format("%s,  %d", strParam, nParam);
 AfxMessageBox(strFmt);
}

4、写个函数测试一下这个表

void CParser::OnButton1()
{
 ExecCmd(CString("#a"), CString("123"));
 ExecCmd(CString("#b"), CString("456"));
}

经过第4步的测试,显然结果是正确的。到这里,我们实现了更高级的“命令映射表”,可以执行不同类型的函数了。

看到这里,希望你能明白这种设计方法。我也累了。。。。。。

上面的代码可能不完整,我写了个简单完整的 MFC对话框 程序来演示这个设计,需要的朋友可以在以下地址下载:

http://www.cppblog.com/Files/dylgsy/TestCmdMap.rar

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/dylgsy/archive/2006/09/21/1260534.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值