It is often necessary to send or receive complex data structures to or from another program that can run on a different architecture, or complex data structures can be designed for different versions of the data structure in question. A typical example is a program that saves its state to a file when it exits and then reads it back when it starts.
The "send" function usually writes the magic identifier and version to the file or network socket first, and then writes all data members one by one (i.e., serially). If it encounters a variable-length array (e.g., a string), it will either write a length followed by data, or it will write the data followed by a special terminator. The format is usually XML or binary; in the latter case, the htonl() macro set may come in handy.
'receive' functions almost identically: it will read all items one by one. Variable length arrays can be handled either by reading the count followed by the data, or by reading the data until a special terminator is reached.
Since both functions typically follow the same pattern as data (structure) declarations, it would be nice if they could both be generated from generic definitions.
X-Macros
| A Wikibookian suggests thatC Programming/Preprocessor #X-Macrosbe merged into this book or chapter. Discuss whether this merger should be done on the discussion page. |
X-Macros uses a preprocessor to force the compiler to compile the same piece of text multiple times. Sometimes special files (with a .def extension) are included multiple times. For example, variables.def might look like this:
INT (value) INT (shift)
In this example, C programming would be as follows:
...
#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
...
If you do not want to include separate files multiple times, you can use another macro. For example :
#define VARIABLES INT (value) \
INT (shift)
The # include S can then be replaced with the calling macro.
Using this method, you can also pass in (a) the names of other macros that can operate on the value list. Example:
#define VAR_LIST(_)_(value)\
_ (transfer)
...
#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)
...
This does not require redefining macros and can make code easier to understand and maintain.
X-Macros is particularly useful for keeping mappings between strings and enumerated types synchronized.
Versioning Serialization
Suppose we want to add other variables to the above example, but we still want the program to be able to read old version 1 files. We then add a version parameter and a default value parameter to the list processing macro:
#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 in highest known version * /
#define VAR_INT_PRT (var, varVer, default) printf (#var "= %d \ n", var); ...
VAR_LIST (VAR_INT_PRT)
Guess you want to read:C Programming. Advanced C" 5. Collaborative Programs



