C++模板实现模块间参数传递及回调
[Version:1.0]
TCL CMD Software (Shanghai)
2012-3-14
Page: 1 / Total: 43
Version history
Version
1.0
Date
2012-3-14
Author
Pan Wei
Comments
Created
Page: 2 / Total: 43
目录
目录 .....................................................................................................................................................3
1 引言 ................................................................................................................................................ 5
1.1 编写目的 ........................................................................................... 错误!未定义书签。
1.2 背景 ..................................................................................................................................... 5
1.3 定义 ................................................................................................... 错误!未定义书签。
1.4 参考资料 ............................................................................................................................. 5
2 模块的参数传递............................................................................................................................ 6
2.1 适用范围 ............................................................................................................................. 6
2.2 使用方法 ............................................................................................................................. 6
2.3 实现 ..................................................................................................................................... 6
2.4 内存释放 ............................................................................................................................. 9
2.5 关于效率 ........................................................................................................................... 11
2.6 已知缺陷 ........................................................................................................................... 11
2.7 还可以更复杂 ................................................................................................................... 11
3 回调函数 ...................................................................................................................................... 14
3.1 适用范围 ........................................................................................................................... 14
3.2 使用方法 ........................................................................................................................... 14
3.3 实现 ................................................................................................................................... 15
4 回调管理器.................................................................................................................................. 19
4.1 代码示例 ........................................................................................................................... 19
4.2 实现 ................................................................................................................................... 19
Annex A: 完整代码 .........................................................................................................................23
CParam.h...................................................................................................................................23
CCallback.h...............................................................................................................................29
CCallbackManager.h................................................................................................................ 37
Page: 3 / Total: 43
Page: 4 / Total: 43
1 引言
1.1 内容简介
本文介绍了一种模块间传递参数及实现回调机制的泛型接口,并实现了一个泛化的回调
管理器。
本文的内容对于修补代码和解 BUG 等类型的工作而言没有任何意义,但如果你要从零
开始设计一个软件或者重构一个系统的话相信你可以从文中获得一点好处。理解本文的内容
需要一定的 C++模板相关知识。
由于能力限制,这里讲的内容难免会有错误,欢迎指正和提供更简洁的实现方案。
1.2 背景
略。
1.3 关键字
模板、参数传递、回调函数
1.4 参考资料
[1] 《C++设计新思维:范型编程与设计模式之应用》
Page: 5 / Total: 43
2 模块的参数传递
2.1 适用范围
当我们设计一个软件时,有时会需要在两个模块或者类之间传递类型不同数量不同的 N
组参数,为了实现这个目的不得不设计 N 个接口,如果这两个互相通信的类是抽象接口的
话,当你需要修改或添加参数及方法时会十分麻烦,因为还需要同时修改所有的子类。更重
要的是一个全功能接口的类违反了设计的美学。
当然你可以利用定义多个结构体,并强制转化为 void*然后附加一个消息类型的方案避
免上述问题,但是这种方法会导致产生多个结构体,并且转化总是会成功,即使是在你写错
的情况下。
这里介绍的方法也是需要一个附加的消息类型,但是没有使用 void*带来的各种麻烦。
2.2 使用方法
这里介绍一些这个方案的例子代码。
//模块 1 中代码:
CParam *p1 = CreateParam(2.0,“12345”,false,33);
CParam *p2 = CreateParam(5);
//模块 2 中代码:
double d;
const char *s;
bool b;
int i;
GetParam(p1,d,s,b,i);//如果转化失败会导致一个断言或抛出异常
GetParam(p2,di);
这里你只需要调用 CreateParam 接口传递任意数目和类型的参数并生成一个 CParam *
变量,然后通过某个特定的消息传递给其他模块。通过这个方法你可以少定义很多无聊的结
构体,并且转化失败时会得到通知。
2.3 实现
首先来定义转化函数:
template
Page: 6 / Total: 43
R param_cast(T t)
{
R r = dynamic_cast(t);
assert(r && t);
if (!r)
{
throw param_bad_cast();
}
return r;
}
为了得到会失败的转化,我们需要使用 dynamic_cast,这个转化操作符只能用于多态类
型,失败时传回 NULL。因此我们需要定义一个参数集合体的基类:
struct CParam
{
virtual ~CParam(){}
};
这里虚拟的析构是必须的,因为我们需要一个多态的删除行为,以及使用 dynamic_cast
的能力。
下面是参数集合类:
template
struct CParam2 : public CParam
{
CParam2(T1 &t1,T2 &t2):v1(t1),v2(t2){}
CParam2(){}
T1 v1;
T2 v2;
};
这个类可以保存任意两个类型的参数的,前提是你的参数是内置类型,或带有无参的
构造函数以及赋值和拷贝构造函数。为了保存一个单一的参数,我们需要利用模板的偏特化
和一个帮助类。
class NullType;
template
struct CParam2 : public CParam
{
CParam2(T1 &t1):v1(t1){}
CParam2(){}
T1 v1;
};
这里的 NullType 只有声明没有定义,因为我们根本不需要定义一个 NullType 的变量。
有了这两个类,我们可以实现任意数目和类型的参数了,这里我只扩展到 5 个:
#define PARAM1(T1) CParam2
#define PARAM2(T1,T2) CParam2
#define PARAM3(T1,T2,T3) CParam2 >
Page: 7 / Total: 43
#define PARAM4(T1,T2,T3,T4) CParam2
> >
#define PARAM5(T1,T2,T3,T4,T5)
CParam2 > > >
\
现在我们可以实现 CreateParam 和 GetParam 了。这里 TRAITS()是一个宏,定义为#define
TRAITS(T) T ,作用稍后说明。
template
CParam * CreateParam(TRAITS(T1) t1)
{
return (new CParam2(t1));
}
template
CParam * CreateParam(TRAITS(T1) t1,TRAITS(T2) t2)
{
return (new CParam2(t1,t2));
}
template
CParam * CreateParam(TRAITS(T1) t1,TRAITS(T2) t2,TRAITS(T3) t3)
{
CParam2 t(t2,t3);
return new CParam2 >(t1,t);
}
同理可得任意数量的参数的 CreateParam,一般来说 10 个足够大部分项目使用。
template
void GetParam(CParam *p,T1 &t1)
{
CParam2* p2 = param_cast*>(p);
t1 = p2->v1;
}
template
void GetParam(CParam *p,T1 &t1,T2 &t2)
{
CParam2* p2 = param_cast*>(p);
t1 = p2->v1;
t2 = p2->v2;
}
template
void GetParam(CParam *p,T1 &t1,T2 &t2,T3 &t3)
{
PARAM3(T1,T2,T3)* p2 = param_cast(p);
t1 = p2->v1;
t2 = p2->v2.v1;
Page: 8 / Total: 43