此外,通常处理一种协议的代码也是专用的,当我们需要增加一种新协议的支持时,我们不得不重写一套新的代码,然后反复的测试,调试,解bug。这样的开发效率是很低的,而且成本也很高。
如果用传统的编程方法,我们很难避免这些问题,然而如果采用元类编程思想,这些问题就会变得相对简单得多。让我们以ts流(视频传输中最常见的一种格式)为例,来看看我们如何使用元类编程思想来处理它,并能带来哪些优势。
这里我不打算(也无法)详细的介绍ts流协议的细节,有兴趣的朋友可以自己上网查阅相关资料,对于某些没有相关背景知识的朋友,你只需要知道ts流的结构是一种按field组织成的流式结构,每一种field都有特定的大小或结构(由许多子field组成),其中有些fields的内容用于指示之后的fields的类型(即某些fields是否存在或其数量取决于它之前的某些fields)。
类似于我们解决第一个例子问题时的情况,首先我们需要设计一个元类来描述每一种field。为避免我们纠结于ts流协议的细节,下面我直接给出一个定义:
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 IField