C++语言中的元类编程(七)(一)

2014-11-24 11:12:40 · 作者: · 浏览: 0
做过网络或者多媒体应用开发的朋友应该会经常的接触一些协议(或文件格式,以下统称协议),特别是那些二进制协议,这些协议不但晦涩难懂,而且编写处理协议的代码也很麻烦,我们经常需要对着文档,一个bit一个bit的去分析我们的处理逻辑,而如果我们对协议的理解有偏差,产生了一些bug,那调试和修改这些bug也是极为费力的(特别是调试别人的或旧的代码,这种问题更加突出)。

此外,通常处理一种协议的代码也是专用的,当我们需要增加一种新协议的支持时,我们不得不重写一套新的代码,然后反复的测试,调试,解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