C では実行時に型情報を取得できないため、C は直接のシリアル化メカニズムをサポートしていません。実行時に型情報を自分で注入し、その型情報によって必要なオブジェクトを構築する必要があります。したがって、可能なすべての構造体を定義します。
typedef struct {
int myInt;
float myFloat;
unsigned char myData[MY_DATA_SIZE];
} MyStruct_1;
typedef struct {
unsigned char myUnsignedChar;
double myDouble;
} MyStruct_2;
次に、合計でどの構造体があるかに関する情報を収集する列挙型を定義します。
typedef enum {
ST_MYSTRUCT_1,
ST_MYSTRUCT_2
} MyStructType;
構造体のサイズを決定できるヘルパー関数を定義します。
int GetStructSize(MyStructType structType) {
switch (structType) {
case ST_MYSTRUCT_1:
return sizeof(MyStruct_1);
case ST_MYSTRUCT_2:
return sizeof(MyStruct_2);
default:
// OOPS no such struct in our pocket
return 0;
}
}
次に、シリアル化関数を定義します。
void BinarySerialize(
MyStructType structType,
void * structPointer,
unsigned char * serializedData) {
int structSize = GetStructSize(structType);
if (structSize != 0) {
// copy struct metadata to serialized bytes
memcpy(serializedData, &structType, sizeof(structType));
// copy struct itself
memcpy(serializedData+sizeof(structType), structPointer, structSize);
}
}
そして逆シリアル化機能:
void BinaryDeserialize(
MyStructType structTypeDestination,
void ** structPointer,
unsigned char * serializedData)
{
// get source struct type
MyStructType structTypeSource;
memcpy(&structTypeSource, serializedData, sizeof(structTypeSource));
// get source struct size
int structSize = GetStructSize(structTypeSource);
if (structTypeSource == structTypeDestination && structSize != 0) {
*structPointer = malloc(structSize);
memcpy(*structPointer, serializedData+sizeof(structTypeSource), structSize);
}
}
シリアル化の使用例:
MyStruct_2 structInput = {0x69, 0.1};
MyStruct_1 * structOutput_1 = NULL;
MyStruct_2 * structOutput_2 = NULL;
unsigned char testSerializedData[SERIALIZED_DATA_MAX_SIZE] = {0};
// serialize structInput
BinarySerialize(ST_MYSTRUCT_2, &structInput, testSerializedData);
// try to de-serialize to something
BinaryDeserialize(ST_MYSTRUCT_1, &structOutput_1, testSerializedData);
BinaryDeserialize(ST_MYSTRUCT_2, &structOutput_2, testSerializedData);
// determine which object was de-serialized
// (plus you will get code-completion support about object members from IDE)
if (structOutput_1 != NULL) {
// do something with structOutput_1
free(structOutput_1);
}
else if (structOutput_2 != NULL) {
// do something with structOutput_2
free(structOutput_2);
}
これはCで最も単純なシリアル化アプローチだと思います.しかし、いくつかの問題があります:
- ポインタをシリアル化するときにどれだけのメモリを割り当てる必要があるか、またデータをどこからどのようにポインタにシリアル化するかがわからないため、構造体にはポインタを含めてはなりません。
- この例では、システムのエンディアンに問題があります。データがメモリに格納される方法に注意する必要があります。ビッグ エンディアンまたはリトル エンディアンの方法で、必要に応じてバイトを反転します [
char *
などの整数型にキャストする場合enum
] (...またはリファクタリングコードの移植性が向上します)。