上一节看到了表驱动法的优点,结构清晰明了,易于维护修改,现在在现实的程序中找几个例子看看吧!
1.Vim之可设置参数
1.1 构建表
Vim是一个开源的软件,随着功能的不断增强,其源码量也在不断攀升,就来研究下官方能找到的最早源码版本1.14吧。
Vim是可以自我定制,即你可以自己编写命令来控制,比如你可以设置一些系统提供的一些变量,如autoindent(ai),ignorecase(ic),number(nu)等等,像每一个参数都有一个缩写形式,所以我们猜测这可能是一个结构体,我们可以在param.h中找到这个结构体:
-
-
/**************
-
* param:
-
* 参数结构体:
-
* 全名
-
* 缩写
-
* 值: long或char* 型别
-
* 标志位,含类型信息
-
* */
-
struct param{
-
char *fullname; /* 全名 */
-
char *shortname; /* 缩写 */
-
union{
-
long intval; /* intenger 参数值 */
-
char *strval; /* string 参数值 */
-
} val;
-
int flags;
-
};
-
紧跟着的是标志位信息宏定义,有以下几种:P_BOOL, P_NUM, P_CHANGED,P_STRING,是按照状态标志位的形式来给出。
-
-
#define P_BOOL 0x01 /* 布尔型 */
-
#define P_NUM 0x02 /* 数值型 */
-
#define P_CHANGED 0x04 /* 改参数被更改 */
-
#define P_STRING 0x08 /* 字符串型 */
-
在这个文件中,有这样一个数组声明:
-
extern struct param params[];
打开param.c,我们就可以找到表驱动法的踪影了:
-
-
struct param params[] =
-
{
-
//参数全名 缩写 初始值 类型
-
{"autoindent", "ai", FALSE, P_BOOL},
-
{"autowrite", "aw", FALSE, P_BOOL},
-
{"backspace", "bs", FALSE, P_BOOL},
-
{"backup", "bk", TRUE, P_BOOL},
-
{"writeany", "wa", FALSE, P_BOOL},
-
// .......略
-
{"yankendofline", "ye", FALSE, P_BOOL},
-
{NULL, NULL, 0, 0} /* 末尾标记 */
-
};
全局就是维护着这样一个表,来控制这Vim的状态信息。注意在表的末尾有一个空行,即{NULL,NULL, 0,
0},这是作为表尾标记出现,我们可以通过它来检测数组中元素个数。这一做法也经常出现在一些其他程序中。
而在main.c的usage()函数中,我们有看到另一种解决方案,即
[数组元素个数] = sizeof( [数组] ) / sizeof( [数组中每个元素] )
这两种方案都是解决表大小判断的。第一种方案,结构清晰,规整;第二种则巧妙。并且两种方案都无需在表中增删元素的情况下更改代码的其他部分。
1.2访问
与上述表紧密定义的是一组下标索引:
-
-
#define P_AI 0 /* auto-indent */
-
#define P_AW 1 /* auto-write */
-
#define P_BS 2 /* backspace over newlines in insert mode */
-
#define P_BK 3 /* make backups when writing out files */
-
// ....
-
索引与表不可分割,改动一个另一个也得相应的改动,这一点必须注意。
接下来访问表的元素就可以定义成下面宏形式:
-
-
#define P(n) ((params[n].val.intval))
-
#define PS(n) (params[n].val.strval)
-
事实上,就是通过这中访问方式访问的:
2.MFC之消息映射
表只是一种形式,内部存放的东西可以是任何东西,当然也包括函数。我们通过MFC的消息机制,看下控件是如何与处理函数关联起来的。
通常在MFC类的声明中会出现这样一句:
相应地,在实现文件中有如下宏:
-
BEGIN_MESSAGE_MAP(CtestDlg, CDialog)
-
ON_WM_SYSCOMMAND()
-
ON_WM_PAINT()
-
ON_WM_QUERYDRAGICON()
-
//}}AFX_MSG_MAP
-
ON_BN_CLICKED(IDOK, &CtestDlg::OnBnClickedOk)
-
ON_WM_COPYDATA()
-
END_MESSAGE_MAP()
这几样东西,是关联消息映射的。内部是如何工作,看看宏的实现。
2.1 声明
看起来是声明消息映射,转到定义一看究竟吧。
-
-
#define DECLARE_MESSAGE_MAP() \
-
protected: \
-
static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
-
virtual const AFX_MSGMAP* GetMessageMap() const; \
-
看起来,这个宏定义就是声明了两个函数,一个静态const型,一个虚的const型,都是返回一个AFX_MSGMAP型的指针,这是一个什么结构体,继续搜索:
-
-
struct AFX_MSGMAP
-
{
-
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
-
//指向基类的结构体指针
-
const AFX_MSGMAP_ENTRY* lpEntries;
-
};
AFX_MSGMAP_ENTRY是下列结构:
-
-
struct AFX_MSGMAP_ENTRY
-
{
-
UINT nMessage; // windows message
-
UINT nCode; // control code or WM_NOTIFY code
-
UINT nID; // control ID (or 0 for windows messages)
-
UINT nLastID; // used for entries specifying a range of control id's
-
UINT_PTR nSig; // signature type (action) or pointer to message#
-
AFX_PMSG pfn; // routine to call (or special value) 指向回调函数
-
};
这个结构体才是真正的桥接,把windows消息和消息处理函数关联了起来,只要通过访问结构体,就可以获得相关联的信息。
2.2实现
看实现文件中还有这几个宏:
-
-
BEGIN_MESSAGE_MAP(CtestDlg, CDialog)
-
ON_WM_SYSCOMMAND()
-
ON_WM_PAINT()
-
ON_WM_QUERYDRAGICON()
-
//}}AFX_MSG_MAP
-
ON_BN_CLICKED(IDOK, &CtestDlg::OnBnClickedOk)
-
ON_WM_COPYDATA()
-
END_MESSAGE_MAP()
看第一个:
-
-
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
-
PTM_WARNING_DISABLE \
-
const AFX_MSGMAP* theClass::GetMessageMap() const \
-
{ return GetThisMessageMap(); } \
-
const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
-
{ \
-
typedef theClass ThisClass; \
-
typedef baseClass TheBaseClass; \
-
static const AFX_MSGMAP_ENTRY _messageEntries[] = \
-
{
有两个函数,这两个函数都是声明文件中的DECLARE_MESSAGE_MAP()宏声明的两个函数。第一个是接口函数,第二个是实现函数,
我们看到实现函数中维护着一个静态const表,是AFX_MSGMAP_ENTRY数组,接下来的ON类宏都是一个数组元素,如:
-
-
#define ON_WM_PAINT() \
-
{ WM_PAINT, 0, 0, 0, AfxSig_vv, \
-
(AFX_PMSG)(AFX_PMSGW) \
-
(static_cast< void (AFX_MSG_CALL CWnd::*)(void) > ( &ThisClass :: OnPaint)) },
而与控件相关联的宏一般都是一个中间接口宏,如
-
-
#define ON_BN_CLICKED(id, memberFxn) \
-
ON_CONTROL(BN_CLICKED, id, memberFxn)
-
而
-
-
#define ON_CONTROL(wNotifyCode, id, memberFxn) \
-
{ WM_COMMAND, (WORD)wNotifyCode, (WORD)id, (WORD)id, AfxSigCmd_v, \
-
(static_cast< AFX_PMSG > (memberFxn)) },
-
而最末一个宏:
-
-
#define END_MESSAGE_MAP() \
-
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
-
}; \
-
static const AFX_MSGMAP messageMap = \
-
{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
-
return &messageMap; \
-
} \
-
PTM_WARNING_RESTORE
在数组表的末尾又看到了末尾标记:
-
-
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \ // AfxSig_end是0
-
};
这样一个表的内容就构建完了。由此出发又进一步构造了AFX_MSGMAP型的消息映射messageMap,第一个参数指向基类的此结构体,
第二个参数就是本类刚刚定义的消息映射表入口。实现函数GetThisMessageMap()返回的就是这个AFX_MSGMAP型的消息映射messageMap
结构体,由此你可以关联基类或本类的消息函数了。
2008年10月28日 16:47
不错哦,哈哈