C 程式設計/序列化
外觀
< C 程式設計
通常需要將複雜的資料結構傳送或接收給另一個程式,該程式可能在不同的架構上執行,或者可能針對不同的資料結構版本而設計。一個典型的例子是一個在退出時將狀態儲存到檔案,然後在啟動時讀回該狀態的程式。
'傳送' 函式通常首先將一個魔術識別符號和版本寫入檔案或網路套接字,然後逐個寫入所有資料成員(即序列寫入)。如果遇到可變長度陣列(例如字串),它將要麼寫入一個長度後跟資料,要麼寫入資料後跟一個特殊終止符。格式通常為 XML 或二進位制;在後一種情況下,htonl() 宏集可能會有用。
'接收' 函式幾乎相同:它將逐個讀取所有專案。可變長度陣列要麼透過讀取計數後跟資料來處理,要麼透過讀取資料直到遇到特殊終止符來處理。
由於這兩個函式經常遵循與資料(結構)宣告相同的模式,因此如果它們都可以從一個通用定義中生成,那將非常不錯。
| 一位華夏公益教科書的貢獻者建議將 C 程式設計/預處理器#X 宏 合併到本章。 在討論頁面上討論是否應該進行此合併。 |
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)
...