撤销和重做(Undo和Redo)的C++完美实现(8-B)

#if 0

本来这一章是要讨论对前一章里面封装的控制面板类进行STL算法兼容性讨论的,但是
在讨论这个问题之前发现前一章里面对客户端的代码编写有一个特别的要求:简单类型和
复合类型必须用前一章里面的分隔符进行分隔,这就要对客户端编码规范进行限制。这是
一个对库的使用者的限制,但是本撤销和重做库的目标之一就是尽可能少的给出使用该库
的规则,降低库的使用者的使用门槛。因此也就自然而然的想到了,这个额外的规范到底
是不是必须的,实际上在编写前一章的时候已经考虑过这个问题,因为当时还没有解决方
案,所以就给出了上一章里面的简单方案,但是经过一晚上的考虑之后(参看了<<Modern
C++ Design>>),终于发现上面的这个规范是不必要的,因此在这一章里面将会重新考虑
这个没有上面的编码规范的方案。有了这一章讨论的这个方案,客户端的编码将会更加自
由。

上一章里面的控制类之所以有上面讨论的编码规范,实际上就是为了自动区分简单类
型和复合类型。前一章里面采用的区分简单类型和复合类型的方法是通过在传入给控制类
的类型串中添加一个分隔符来实现的,在分隔符之前的是简单类型,而在分隔符之后的则
是复合类型。所以在本章里面讨论的关键问题就是如何不使用分隔符来区分出一个类型是
简单类型还是复合类型。当然这里的复合类型不仅仅是compound模板类,还包括许许多多
的派生自compound模板类的派生类。

本章将会讨论的问题:

(1)实现简单类型和复合类型的自动区分

(2)利用(1)中的方案重新实现前一章里面的控制类

先来看看第(1)个问题吧,也是实现第(2)个问题的关键。


#endif
#ifdef TRAITS_H
#include "compound.h"
//实现简单类型和复合类型的自动区分的模板代码,这里面主要参考了<<Modern
//C++ Design>>一书里面的讨论编译器诊断继承关系一节。不清楚的地方可以参
//看这本书。
template <class Type>class traits
{
typedef char simple_type;
class compound_type{char unnamed[2];};
//默认的类型是简单类型
static simple_type test(...);
template <class T>
static compound_type test(pandaxcl::compound<T>);
static Type _type();//避免Type类型的构造函数是私有的
public:
enum{
is_simple = (sizeof(test(_type()))==sizeof(simple_type)),
is_compound= (sizeof(test(_type()))==sizeof(compound_type))
};
};
#endif//TRAITS_H
#ifdef CODE1//g++ -DTRAITS_H -DCODE1 thisfile.cpp
#include <iostream>
#include "compound.h"
namespace xcl=pandaxcl;
//测试上面的类型诊断模板的代码
class Simple{};//简单类型
typedef xcl::cons<Simple,
xcl::cons<Simple,
xcl::null_type > > CONS;//复合类型属性类型串
typedef xcl::compound<CONS> Compound;//直接复合类型
//派生的复合类型
class DerivedCompound : public xcl::compound<CONS>{};
int main()
{
std::cout << "Simple is kind of simple ? " /
<< traits<Simple >::is_simple /
<< std::endl;
std::cout << "Simple is kind of compound ? " /
<< traits<Simple >::is_compound /
<< std::endl;
std::cout << "Compound is kind of simple ? " /
<< traits<Compound >::is_simple /
<< std::endl;
std::cout << "Compound is kind of compound ? " /
<< traits<Compound >::is_compound /
<< std::endl;
std::cout << "DerivedCompound is kind of simple ? " /
<< traits<DerivedCompound>::is_simple /
<< std::endl;
std::cout << "DerivedCompound is kind of compound ? " /
<< traits<DerivedCompound>::is_compound /
<< std::endl;
};
#endif//CODE1

//该程序的运行结果为:
/*******************************************************************************
Simple is kind of simple ? 1
Simple is kind of compound ? 0
Compound is kind of simple ? 0
Compound is kind of compound ? 1
DerivedCompound is kind of simple ? 0
DerivedCompound is kind of compound ? 1
*******************************************************************************/

#if 0

从上面的CODE1中的代码的运行结果可以看出,已经成功的分辨出了简单类型和复合类
型,因此可以开始着手重写前面的控制类,也就是本文需要讨论的第(2)个问题。控制类见
CONTROL_H所示:

#endif
#ifdef CONTROL_H
#include "center.h"
#include "identifier.h"
#include "meta.h"
#include "compound.h"
namespace xcl=pandaxcl;
//对容器类基元进行自动化包装
template <class T> struct control_unit
:public xcl::container<xcl::identifier<T>,T>
{
};
//下面是控制面板的自动化实现
template <class Cons>struct control
:public xcl::center
,public xcl::scatter<Cons,control_unit>
{
typedef Cons cons_type;
//给一种统一的接口来实现简单对象和复合对象的三种基本操作
template <class T>
xcl::identifier<T> create(const T&OC)
{
//准备好创建参数
typename CREATE<T>::ENVIRONMENT e(*this,OC);
//根据当前类型是简单类型还是复合类型选择不同的创建操作
return xcl::IF<traits<T>::is_simple,
typename CREATE<T>::Simple,
typename CREATE<T>::Compound
>::result::execute(e);
}
//当IDT为简单类型标识号时,T表示简单类
//当IDT为复合类型标识号时,T表示复合类的属性类型
//复合类型自身保存在标识号类里面
//template <int i=0,class IDT,class T>
//template <class IDT,class T,int i=0>
template <int i,class IDT,class T>
void modify(const IDT&ID,const T&OM)
{
typedef typename IDT::object_type object_type;
//准备好创建参数
typename MODIFY<object_type,i>/
::template ENVIRONMENT<T> e(*this,ID,OM);
//根据当前类型是简单类型还是复合类型选择不同的修改操作
pandaxcl::IF<traits<object_type>::is_simple,
typename MODIFY<object_type,i>::Simple,
typename MODIFY<object_type,i>::Compound
>::result::execute(e);
}
template <class IDT>
void remove(const IDT&ID)
{
typedef typename IDT::object_type object_type;
//准备好创建参数
typename REMOVE<IDT>::ENVIRONMENT e(*this,ID);
//根据当前类型是简单类型还是复合类型选择不同的删除操作
pandaxcl::IF<traits<object_type>::is_simple,
typename REMOVE<IDT>::Simple,
typename REMOVE<IDT>::Compound
>::result::execute(e);
}
private:
//针对简单类型和复合类型分别调用的创建过程
template <class T> struct CREATE
{
typedef T object_type;
typedef xcl::identifier<T> identifier_type;
typedef xcl::container<identifier_type,T> container_type;
//传递给创建操作的环境变量
struct ENVIRONMENT
{
ENVIRONMENT(control&C,const object_type&O)
:_C(C),_O(O){}
control&_C;
const object_type &_O;
};
struct Simple
{//用来创建简单类对象
template <class EnvironmentType>
static identifier_type execute(EnvironmentType&e)
{
typedef xcl::create<container_type> COMMAND;
identifier_type ID(true);
e._C.execute(new COMMAND(e._C,ID,e._O));
return ID;
}
};
struct Compound
{//用来创建复合类对象
template <class EnvironmentType>
static identifier_type execute(EnvironmentType&e)
{
identifier_type ID(true);
object_type::create(e._C,ID,e._O);
return ID;
}
};
};
//下面的模板参数size_t对于简单对象是没有用的,随便给一个整数都可以,
//但是这个模板参数对复合类型表示的就是复合类型的第i个属性类型了。
template <class T,size_t i> struct MODIFY
{
typedef T object_type;
typedef xcl::identifier<T> identifier_type;
typedef xcl::container<identifier_type,T> container_type;
//传递给修改操作的环境变量
template <class PropertyType>
struct ENVIRONMENT
{
ENVIRONMENT(control&C,const identifier_type&ID,const PropertyType&O)
:_C(C),_ID(ID),_O(O){}
control&_C;
const identifier_type&_ID;
const PropertyType &_O;
};
struct Simple
{//用来修改简单类对象
template <class EnvironmentType>
static void execute(EnvironmentType&e)
{
typedef xcl::modify<container_type> COMMAND;
e._C.execute(new COMMAND(e._C,e._ID,e._O));
}
};
struct Compound
{//用来修改复合类对象的一个属性
template <class EnvironmentType>
static void execute(EnvironmentType&e)
{
object_type::template modify<i>(e._C,e._ID,e._O);
}
};
};
//根据类型是简单类型还是复合类型调用删除操作
template <class IDT> struct REMOVE
{
typedef typename IDT::object_type object_type;
typedef IDT identifier_type;
typedef xcl::container<IDT,object_type> container_type;
//传递给删除操作的环境变量
struct ENVIRONMENT
{
ENVIRONMENT(control&C,const identifier_type&ID)
:_C(C),_ID(ID){}
control&_C;
const identifier_type &_ID;
};
struct Simple
{//用来删除简单类对象
template <class EnvironmentType>
static void execute(EnvironmentType&e)
{
typedef xcl::remove<container_type> COMMAND;
e._C.execute(new COMMAND(e._C,e._ID));
}
};
struct Compound
{//用来删除复合类对象
template <class EnvironmentType>
static void execute(EnvironmentType&e)
{
object_type::remove(e._C,e._ID);
}
};
};

};
#endif//CONTROL_H
#if 0

下面的CODE2是上面的自动化控制面板类的测试代码。注意:下面的代码中已经没有了
分隔符,同时也就没有了上一章里面规定的那些编码规范。这样就可以进一步减少使用该
库所必须知道的注意事项,降低了客户端使用该库的门槛,也降低了出现错误的可能性。
从而进一步体现了C++自动化编程的优势,这也是我编写这个撤销和重做库的初衷之一:尽
可能的减少该库的使用规范:)。

#endif
#ifdef CODE2//g++ -DTRAITS_H -DCONTROL_H -DCODE2 thisfile.cpp
#include <iostream>
#include "compound.h"
#include "meta.h"
namespace xcl = pandaxcl;//名字空间重命名
//矩形类
class Rectangle
{
public:
Rectangle():_x(0),_y(0),_width(20),_height(10){}
Rectangle(int x,int y,int w,int h):_x(x),_y(y),_width(w),_height(h){}
private:
int _x,_y,_width,_height;
//下面的函数仅仅是为了输出信息而准备的
friend std::ostream&operator<<(std::ostream&s,Rectangle&o)
{
s << "(" << o._x << "," << o._y << "," << o._width << "," << o._height << ")" ;
return s;
}
};
//圆形类
class Circle
{
public:
Circle():_x(0),_y(0),_radius(20){}
Circle(int x,int y,int r):_x(x),_y(y),_radius(r){}
private:
int _x,_y,_radius;
//下面的函数仅仅是为了输出信息而准备的
friend std::ostream&operator<<(std::ostream&s,Circle&o)
{
s << "(" << o._x << "," << o._y << "," << o._radius << ")" ;
return s;
}
};
//圆角矩形类
typedef xcl::cons<Rectangle,
xcl::cons<Circle,//重复类型
xcl::cons<Circle,//重复类型
xcl::cons<Circle,//重复类型
xcl::cons<Circle,//重复类型
xcl::null_type> > > > >ROUNDRECTANGLE;
//由上面的类型串自动生成圆角矩形类
//typedef xcl::compound<ROUNDRECTANGLE> RoundRectangle;
class RoundRectangle : public xcl::compound<ROUNDRECTANGLE>
{
friend std::ostream&operator<<(std::ostream&s,RoundRectangle&o)
{
s << "R:[" << std::hex << xcl::field<0>(o).ID << "] " ;
s << "C:[" << std::hex << xcl::field<1>(o).ID << "] " ;
s << "C:[" << std::hex << xcl::field<2>(o).ID << "] " ;
s << "C:[" << std::hex << xcl::field<3>(o).ID << "] " ;
s << "C:[" << std::hex << xcl::field<4>(o).ID << "] " ;
return s;
}
};
//观察容器的内容而准备的
template <class Container>
void display(const char*str,Container&c)
{
typedef typename Container::value_type value_type;
struct X
{
static void print(value_type&v)
{
std::cout.setf(std::ios::showbase);//显示16进制的0x前缀
std::cout << "(" <<std::hex<< v.first << "," ;
std::cout << std::dec << v.second << ") ";
}
};
//输出提示信息,并输出容器中的元素数量
std::cout << str << "[" << std::dec << c.size() << "] ";
std::for_each(c.begin(),c.end(),X::print);//输出容器中的所有的标识号和对象信息
std::cout << std::endl;
}
//控制面板里面不应该有重复的类型,这是必须保证的:),另外下面的代码还故意将简单
//类型和复合类型混在一起以和前一章的控制类相区别。
typedef xcl::cons<Rectangle,//简单类型
xcl::cons<RoundRectangle,//复合类型
xcl::cons<Circle,//简单类型
xcl::null_type> > > CONTROL;
//其它的代码和前一章的完全相同没有丝毫的改动,但是却成功的实现了和前一章讨论的
//控制类完全相同的功能:)
class Control : public control<CONTROL>
{
public:
//下面的函数是输出该控制面板对象的相关信息的
void display(const char*str)
{
std::cout << "----------"<<str<<"----------" <<std::endl;
std::cout << "Undo:[" << std::dec << xcl::center::undo_type::size() << "] " ;
std::cout << "Redo:[" << std::dec << xcl::center::redo_type::size() << "] " ;
std::cout << std::endl ;
//::display("矩形容器 :",xcl::field<0>(*this));
//::display("圆形容器 :",xcl::field<1>(*this));
//::display("圆角矩形容器:",xcl::field<2>(*this));
//正是因为控制面板里面没有重复的类型,所以可以直接使用下面的
//field函数
::display("矩形容器 :",xcl::field<Rectangle >(*this));
::display("圆形容器 :",xcl::field<Circle >(*this));
::display("圆角矩形容器:",xcl::field<RoundRectangle>(*this));
std::cout << "=========="<<str<<"==========" <<std::endl;
}
};

int main()
{
//首先应用程序中必须有一个全局的控制面板类对象
Control C;
//复合对象的标识号对象,这里采用false参数表示不产生新标识号
xcl::identifier<Rectangle> idr (false);
xcl::identifier<Circle> idc (false);
xcl::identifier<RoundRectangle> idcom(false);
//模拟创建对象
C.record();
idr = C.create(Rectangle() );
idc = C.create(Circle() );
idcom = C.create(RoundRectangle());
C.stop();
C.display("创建对象");
C.undo();//撤销一步
C.display("撤销一步");
C.redo();//重做一步
C.display("重做一步");
//模拟修改对象
C.record();
//C.modify(idr ,Rectangle(11,22,1000,2000));
//C.modify(idc ,Circle (11,22,1000 ));
//修改简单对象的时候任意给出一个模板参数就可以啦
C.modify<0>(idr ,Rectangle(11,22,1000,2000));
C.modify<0>(idc ,Circle (11,22,1000 ));
C.modify<0>(idcom,Rectangle(1 ,1 ,200,100));
C.modify<1>(idcom,Circle (1 ,1 ,5 ));
C.modify<2>(idcom,Circle (201,1 ,5 ));
C.modify<3>(idcom,Circle (201,101,5 ));
C.modify<4>(idcom,Circle (1 ,101,5 ));
C.stop();
C.display("修改对象");
C.undo();//撤销一步
C.display("撤销一步");
C.redo();//重做一步
C.display("重做一步");
//模拟删除复合对象
C.record();
C.remove(idr );
C.remove(idc );
C.remove(idcom);
C.stop();
C.display("删除对象");
C.undo();//撤销一步
C.display("撤销一步");
C.redo();//重做一步
C.display("重做一步");
return 0;
}
#endif//CODE2

//该程序的运行结果:
/*******************************************************************************
----------创建对象----------
Undo:[1] Redo:[0]
矩形容器 :[2] (0x1,(0,0,20,10)) (0x2,(0,0,20,10))
圆形容器 :[5] (0x1,(0,0,20)) (0x2,(0,0,20)) (0x3,(0,0,20)) (0x4,(0,0,20)) (0x5,(0,0,20))
圆角矩形容器:[1] (0x1,R:[0x2] C:[0x2] C:[0x3] C:[0x4] C:[0x5] )
==========创建对象==========
----------撤销一步----------
Undo:[0] Redo:[1]
矩形容器 :[0]
圆形容器 :[0]
圆角矩形容器:[0]
==========撤销一步==========
----------重做一步----------
Undo:[1] Redo:[0]
矩形容器 :[2] (0x1,(0,0,20,10)) (0x2,(0,0,20,10))
圆形容器 :[5] (0x1,(0,0,20)) (0x2,(0,0,20)) (0x3,(0,0,20)) (0x4,(0,0,20)) (0x5,(0,0,20))
圆角矩形容器:[1] (0x1,R:[0x2] C:[0x2] C:[0x3] C:[0x4] C:[0x5] )
==========重做一步==========
----------修改对象----------
Undo:[2] Redo:[0]
矩形容器 :[2] (0x1,(11,22,1000,2000)) (0x2,(1,1,200,100))
圆形容器 :[5] (0x1,(11,22,1000)) (0x2,(1,1,5)) (0x3,(201,1,5)) (0x4,(201,101,5)) (0x5,(1,101,5))
圆角矩形容器:[1] (0x1,R:[0x2] C:[0x2] C:[0x3] C:[0x4] C:[0x5] )
==========修改对象==========
----------撤销一步----------
Undo:[1] Redo:[1]
矩形容器 :[2] (0x1,(0,0,20,10)) (0x2,(0,0,20,10))
圆形容器 :[5] (0x1,(0,0,20)) (0x2,(0,0,20)) (0x3,(0,0,20)) (0x4,(0,0,20)) (0x5,(0,0,20))
圆角矩形容器:[1] (0x1,R:[0x2] C:[0x2] C:[0x3] C:[0x4] C:[0x5] )
==========撤销一步==========
----------重做一步----------
Undo:[2] Redo:[0]
矩形容器 :[2] (0x1,(11,22,1000,2000)) (0x2,(1,1,200,100))
圆形容器 :[5] (0x1,(11,22,1000)) (0x2,(1,1,5)) (0x3,(201,1,5)) (0x4,(201,101,5)) (0x5,(1,101,5))
圆角矩形容器:[1] (0x1,R:[0x2] C:[0x2] C:[0x3] C:[0x4] C:[0x5] )
==========重做一步==========
----------删除对象----------
Undo:[3] Redo:[0]
矩形容器 :[0]
圆形容器 :[0]
圆角矩形容器:[0]
==========删除对象==========
----------撤销一步----------
Undo:[2] Redo:[1]
矩形容器 :[2] (0x1,(11,22,1000,2000)) (0x2,(1,1,200,100))
圆形容器 :[5] (0x1,(11,22,1000)) (0x2,(1,1,5)) (0x3,(201,1,5)) (0x4,(201,101,5)) (0x5,(1,101,5))
圆角矩形容器:[1] (0x1,R:[0x2] C:[0x2] C:[0x3] C:[0x4] C:[0x5] )
==========撤销一步==========
----------重做一步----------
Undo:[3] Redo:[0]
矩形容器 :[0]
圆形容器 :[0]
圆角矩形容器:[0]
==========重做一步==========
*******************************************************************************/


#if 0

从上面的讨论可以看出,我们已经成功的将上一章里面的那个编码规范去掉了,从而
极大的增强了使用该库编码的自由度。为了以后讨论的方便,我将把这一章的TRAITS_H之
间的代码和CONTROL_H之间的代码保存到“control.h”文件中,用来取代前一章里面保存
到“control.h”文件中的代码,方便以后讨论。因为TRAITS_H之间的代码只会用在
control中,所以将它们直接合并在一个文件中更方便。

这一章本来是要讨论如何使该控制类和STL算法保持兼容的,但是在讨论这个兼容性之
前,却发现了一个更完美的控制类,所以在这里就插入了这样的一个控制类演化过程。至
于如何充分利用STL的算法将会在后续的章节里面讨论。(敬请关注!)

未完,待续...

#endif
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值