跳转到内容

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 编程 后一篇:协程
华夏公益教科书