程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++語言中的元類編程(七)

C++語言中的元類編程(七)

編輯:C++入門知識

做過網絡或者多媒體應用開發的朋友應該會經常的接觸一些協議(或文件格式,以下統稱協議),特別是那些二進制協議,這些協議不但晦澀難懂,而且編寫處理協議的代碼也很麻煩,我們經常需要對著文檔,一個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 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(mSubFieldDesIdx) = 0;

return mSubFieldDes[0];

}

virtual const IFieldDescriptor * GetNextFieldDescriptor() const {

++ const_cast(mSubFieldDesIdx);

if (mSubFieldDesIdx < 2)

return mSubFieldDes[mSubFieldDesIdx];

return NULL;

}

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的描述能力,同時這種表達方式也非常的便於我們(根據原始的結構)檢查每一個描述是否是正確的,同時我們也應當看到,這種描述能力是非常靈活的,可以任意的根據我們的需要進行擴展(比如我們最初提出這個設計是為了描述TS協議,但是居然也可以描述位圖!而且我們可以肯定,只要是能用樹形表示的絕大多數邏輯結構,它都能描述!),之後需要考慮的就是如何利用IFieldDescriptor和IProtocolDescriptor描述出的數據結構,去分析bit流,並且輸出一個field的序列。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved