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) ...