跳轉到內容

C 程式設計/序列化

來自華夏公益教科書,自由的教科書
前一篇:副作用和順序點 C 程式設計 後一篇:協程

序列化

[編輯 | 編輯原始碼]

通常需要將複雜的資料結構傳送或接收給另一個程式,該程式可能在不同的架構上執行,或者可能針對不同的資料結構版本而設計。一個典型的例子是一個在退出時將狀態儲存到檔案,然後在啟動時讀回該狀態的程式。

'傳送' 函式通常首先將一個魔術識別符號和版本寫入檔案或網路套接字,然後逐個寫入所有資料成員(即序列寫入)。如果遇到可變長度陣列(例如字串),它將要麼寫入一個長度後跟資料,要麼寫入資料後跟一個特殊終止符。格式通常為 XML 或二進位制;在後一種情況下,htonl() 宏集可能會有用。

'接收' 函式幾乎相同:它將逐個讀取所有專案。可變長度陣列要麼透過讀取計數後跟資料來處理,要麼透過讀取資料直到遇到特殊終止符來處理。

由於這兩個函式經常遵循與資料(結構)宣告相同的模式,因此如果它們都可以從一個通用定義中生成,那將非常不錯。

X 宏使用預處理器強制編譯器多次編譯相同的文字片段。有時會多次包含一個特殊檔案(副檔名為 .def)。例如,variables.def 可能如下所示 

INT(value)
INT(shift)

在這個例子中,C 程式設計程式碼將如下所示 

...
#define INT(var) int var;
#include "variables.def"
#undef INT
...
printf ("version=1\n");
#define INT(var) printf (#var "=%d\n", var);
#include "variables.def"
#undef INT
...

如果多次包含一個單獨的檔案不可取,可以使用另一個宏。例如 

#define VARIABLES INT(value) \
                  INT(shift)

然後#include可以替換為對宏的呼叫。

使用這種方法,還可以傳入(一個)其他宏的名稱,這些宏可以對值列表進行操作。例如

#define VAR_LIST(_) _(value) \
                    _(shift)
...
#define VAR_INT_DECL(var) int var;
VAR_LIST(VAR_INT_DECL)
...
printf ("version=1\n");
#define VAR_INT_PRINTF(var) printf (#var "=%d\n", var);
VAR_LIST(VAR_INT_PRINTF)
...

這不需要重新定義宏,並且可以使程式碼更容易理解和維護。

X 宏對於保持字串和列舉型別之間的對映同步也特別有用。

帶版本控制的序列化

[編輯 | 編輯原始碼]

假設我們想要在上面的例子中新增額外的變數,但我們仍然希望程式能夠讀取舊的版本 1 檔案。然後,我們將為列表處理宏新增一個版本引數和一個預設值引數

#define VAR_LIST(_) _(value,1,0) \
                    _(shift,1,0) \
                    _(mask,2,0xffff)
...
int inputVer;
#define VAR_INT_DECL(var,varVer,default) int var;
VAR_LIST(VAR_INT_DECL)
...
scanf ("version=%d", &inputVer);
#define VAR_INT_SCN(var,varVer,default) if (varVer <= inputVer) scanf (#var "=%d", &var); else var = default;
VAR_LIST(VAR_INT_SCN)
...
printf ("version=2\n"); /* Always output at highest known version */
#define VAR_INT_PRT(var,varVer,default) printf (#var "=%d\n", var);
VAR_LIST(VAR_INT_PRT)
...
前一篇:副作用和順序點 C 程式設計 後一篇:協程
華夏公益教科書