此外,通常处理一种协议的代码也是专用的,当我们需要增加一种新协议的支持时,我们不得不重写一套新的代码,然后反复的测试,调试,解bug。这样的开发效率是很低的,而且成本也很高。
如果用传统的编程方法,我们很难避免这些问题,然而如果采用元类编程思想,这些问题就会变得相对简单得多。在正式开始介绍处理方法之前,先让我们来假设一个简单的协议,因为实际运用的协议大多都很复杂,而且很多朋友可能没有相关经验,缺乏背景知识,这可能会让他们在理解后面的设计时感到困难。
我们每个人小的时候应该都玩过跳棋,就是那种由6种颜色的玻璃小弹珠和六角星形棋盘组成的游戏棋,我们不妨就借用这6种不同颜色的小弹珠来讨论我们的协议。首先,我们可以规定,这六种小弹珠的特定组合具有特定的含义,比如“红黄蓝”代表“你”,而“红绿蓝”代表“我”,那么我们就可以利用这些小弹珠排列成一个序列,来表达出一种特定的含义。很显然,我们需要规定什么样的组合代表什么样的含义,这就是一种协议。(如果你是一个显赫的人物或是一个社会名流,当你想给情人写信的时候,你就可以这样写:红黄白绿黄蓝黑黑绿黄蓝白绿……,而你的情人就可以利用这个协议把你要说的意思翻译出来,万一这封信遗失了也不用担心,只要其他人不知道你的协议,他/她就不会知道你的秘密,除非他是个密码学家)如果我们要让计算机来处理,就需要对这6种颜色的小弹珠进行编码,这样我们就能将一个小弹珠的序列转换成bit流了(自然,反向也成立)。而我们不妨将这种协议中的特定组合称为field,而这种协议的基本组成单元——6种颜色的小弹珠,自然也是6种最基本的field。好,现在来看看我们如何运用元类编程思想来处理这种协议,并能带来哪些优势。
让我们先来看看从bit流得到field序列的情况,第一步,我们需要设计一个元类来描述每一种field。为避免我们纠结于具体协议的细节,下面我直接给出一个定义:
class CBitStream; // 这是一个已实现的处理bit流的类,我们不用关心这个类的具体实现,只需要知道这个类提供了访问一段buffer中的某个(或某些)bit的方法
typedef CBitStream::CIterator stream_iterator; // CBitStream采用了iterator的设计,同样,我们不需要知道这个iterator的实现细节,只需要知道它是访问某个(或某些)bit的接口
struct IProtocolDescriptor; // 如之前所说,有些field是有结构的,这种field我们可以把它看作是一种子协议
struct IFieldDescriptor {
virtual unsigned ID() const = 0; // 为提高调用函数的处理效率,我们需要一个ID来唯一标识这种field
virtual const char * Name() const = 0; // 这个容易理解
virtual bool IsCondition() const = 0; // 指示这种field是否是另一种field的条件,参见ConditionFieldDescriptor
virtual const IFieldDescriptor * ConditionFieldDescriptor() const = 0; // 用来标识这种field是否由另一种field来控制,参见IsCondition
virtual const IProtocolDescriptor * CastToProtocolDescriptor() const = 0; // 参见IProtocolDescriptor的注释,如果这个转换能成功,说明这是一种子协议field virtual size_t FieldSize(stream_iterator fieldData) const = 0; // 这种field的大小(单位是bit),如果是变长编码的field,我们可以根据它的内容计算出来
virtual size_t FieldCount(stream_iterator conditionFieldData, stream_iterator fieldData) const = 0; // 这种field在bit流中的个数,如果不存在,应当返回0,否则我们可以根据它的条件field的内容和其本身内容计算出来
};
注意上面的定义中IsCondition和ConditionFieldDescriptor用于描述两种field的依赖关系,而FieldCount则是(运行时)取得field对象个数的元函数。另外,它提到了IProtocolDescriptor接口(以及与这个接口的关系),其定义如下:
struct IProtocolDescriptor {
virtual size_t GetFieldDescriptorCount() const = 0;
virtual const IFieldDescriptor * GetFirstFieldDescriptor() const = 0;
virtual const IFieldDescriptor * GetNextFieldDescriptor() const = 0;
}; // 注意这个接口描述的是一个协议field由多少个子field组成,以及如何访问这些子field的描述,它与field的对象在bit流中的分布不是一回事。
结合这两个定义,不难发现,我们可以用它们描述出一个树状逻辑结构,比如:
struct image {
struct header_t {
short width;
short height;
} image_header; unsigned char image_data[];
};
上面是一个简化版的位图格式的数据结构,用我们的IFieldDescriptor和IProtocolDescriptor描述出来会像这样:
enum image_des_id {
image_des = 1,
image_des_width,
image_des_height,
image_des_header,
image_des_data
};
class CImageHeaderWidthDes: public IFieldDescriptor {
virtual unsigned ID() const { return image_des_width; }
virtual const char * Name() const { return "image_des_width"; }
virtual bool IsCondition() const { return false; }
virtual const IFieldDescriptor * ConditionFieldDescriptor() const { return NULL; }
virtual const IProtocolDescriptor * CastToProtocolDescriptor() const { return NULL; }
virtual size_t FieldSize(stream_iterator fieldData) const { return 16; }
virtual size_t FieldCount(stream_iterator conditionFieldData, stream_iterator fieldData) const { return 1; }
};
class CImageHeaderHeightDes: public IFieldDescriptor {
...
}; // 请仿造CImageHeaderWidthDes将这个定义补充完整
class CImageHeaderDes : public IFieldDescriptor, public IProtocolDescriptor {
virtual unsigned ID() const { return image_des_header; }
virtual const char * Name() const { return "image_des_header"; }
virtual bool IsCondition() const { return true; }
virtual const IFieldDescriptor * ConditionFieldDescriptor() const { return NULL; }
virtual const IProtocolDescriptor * CastToProtocolDescriptor() const { return this; }
virtual size_t FieldSize(stream_iterator fieldData) const { return 0; } // 我们可以不关心子协议类型的field的大小,至于具体原因,我们将来会再解释
virtual size_t FieldCount(stream_iterator conditionFieldData, stream_iterator fieldData) const { return 1; }
virtual size_t GetFieldDescriptorCount() const { return 2; }
virtual const IFieldDescriptor * GetFirstFieldDescriptor() const {
const_cast<int &>(mSubFieldDesIdx) = 0;
return mSubFieldDes[0];
}
virtual const IFieldDescriptor * GetNextFieldDescriptor() const {
++ const_cast<int &>(mSubFieldDesIdx);
if (mSubFieldDesIdx < 2) return mSubFieldDes[mSubFieldDesIdx];
}
CImageHeaderWidthDes mWidthDes;
CImageHeaderHeightDes mHeigthDes;
const IFieldDescriptor * mSubFieldDes[2];
int mSubFieldDesIdx;
public:
CImageHeaderDes() {
mSubFieldDes[0] = &mWidthDes;
mSubFieldDes[1] = &mHeigthDes;
mSubFieldDesIdx = 0;
}
};
class CImageDataDes: public IFieldDescriptor {
virtual unsigned ID() const { return image_des_data; }
virtual const char * Name() const { return "image_des_data"; }
virtual bool IsCondition() const { return false; }
virtual const IFieldDescriptor * ConditionFieldDescriptor() const { return mImageHeaderDes; }
virtual const IProtocolDescriptor * CastToProtocolDescriptor() const { return NULL; }
virtual size_t FieldSize(stream_iterator fieldData) const { return 8; }
virtual size_t FieldCount(stream_iterator conditionFieldData, stream_iterator fieldData) const {
short size[2] = {0};
conditionFieldData.CopyBits( 32, &size, sizeof(size) ); // 这个操作从conditionFieldData中拷贝32bits到size中。
return (int) size[0] * (int) size[1] * 4;
}
const IFieldDescriptor * mImageHeaderDes;
public:
CImageDataDes(const IFieldDescriptor * imageHeaderDes) {
mImageHeaderDes = imageHeaderDes;
}
};
class CImageDes: public IFieldDescriptor, public IProtocolDescriptor {
... // 省略接口IFieldDescriptor和IProtocolDescriptor的实现,请仿造CImageHeaderDes自行补充完整
CImageHeaderDes mImageHeaderDes;
CImageDataDes mImageDataDes;
const IFieldDescriptor * mSubFieldDes[2];
int mSubFieldDesIdx;
public:
CImageDes(): mImageDataDes(&mImageHeaderDes) {
mSubFieldDes[0] = &mImageHeaderDes;
mSubFieldDes[1] = &mImageDataDes;
mSubFieldDesIdx = 0;
}
};
从上面的例子中,我们可以清晰的看到接口IFieldDescriptor和IProtocolDescriptor的描述能力,同时这种表达方式也非常的便于我们(根据原始的结构)检查每一个描述是否是正确的,同时我们也应当看到,这种描述能力是非常灵活的,可以任意的根据我们的需要进行扩展(比如我们最初提出这个设计是为了描述那个“弹珠”协议,但是居然也可以描述位图!而且只要是能用树形表示的绝大多数逻辑结构,它都能描述!),这样我们会惊奇的发现,此时一个协议具体是怎么规定的已经不重要了,重要的是如何利用IFieldDescriptor和IProtocolDescriptor描述出的数据结构,去分析bit流,并且输出一个field的序列。