Critique My Serialization API Hi all. I wrote a serialization API in C++ for myself and wanted to ask for a critique from anyone interested. I like the JSON file format and wanted to use it originally. JSON is a good way to support versioning for your serialized stuff, since you can build in mechanisms to handle missing fields, and extra fields can be ignored. I audited a few JSON options and this one by sheredom was the best, but I still found it a little lacking. Specifically I couldn't figure out the API. It's all... Weird. And the examples are terrible. Since I couldn't find exactly what I wanted I wrote my own. Here were my specific list of requirements. No external dependencies (other than c runtime). JSON-like text output. Can implement the writer/reader with the same code (instead of two different functions). No annoying linked-lists in the API. Supports arrays. Base64 encoding built-in. Shouldn't do anything special for utf8 (they can just be base64 encoded, or sent as-is). Can inspect a field's type before attempting to read it. Arrays have length prepended. Example output. { x = 5, y = 10.300000, str = "Hello.", sub_thing = { num0 = 7, num1 = 3, }, blob_data = "U29tZSBibG9iIGlucHV0LgA=", array_of_ints = [8] { 0, 1, 2, 3, 4, 5, 6, 7, }, array_of_array_of_ints = [2] { [3] { 0, 1, 2 }, [3] { 0, 1, 2 }, }, array_of_objects = [2] { { some_integer = 13, some_string = "Good bye.", }, { some_integer = 4, some_string = "Oi!", }, }, }, The header itself, kv.h. struct kv_t; #define CUTE_KV_MODE_WRITE 1 #define CUTE_KV_MODE_READ 0 kv_t* kv_make(void* user_allocator_context = NULL); void kv_destroy(kv_t* kv); error_t kv_reset(kv_t* kv, const void* data, int size, int mode); int kv_size_written(kv_t* kv); enum kv_type_t { KV_TYPE_NULL = 0, KV_TYPE_INT64 = 1, KV_TYPE_DOUBLE = 2, KV_TYPE_STRING = 3, KV_TYPE_ARRAY = 4, KV_TYPE_BLOB = 5, KV_TYPE_OBJECT = 6, }; error_t kv_key(kv_t* kv, const char* key, kv_type_t* type = NULL); error_t kv_val(kv_t* kv, uint8_t* val); error_t kv_val(kv_t* kv, uint16_t* val); error_t kv_val(kv_t* kv, uint32_t* val); error_t kv_val(kv_t* kv, uint64_t* val); error_t kv_val(kv_t* kv, int8_t* val); error_t kv_val(kv_t* kv, int16_t* val); error_t kv_val(kv_t* kv, int32_t* val); error_t kv_val(kv_t* kv, int64_t* val); error_t kv_val(kv_t* kv, float* val); error_t kv_val(kv_t* kv, double* val); error_t kv_val_string(kv_t* kv, char** str, int* size); error_t kv_val_blob(kv_t* kv, void* data, int* size, int capacity); error_t kv_object_begin(kv_t* kv); error_t kv_object_end(kv_t* kv); error_t kv_array_begin(kv_t* kv, int* count); error_t kv_array_end(kv_t* kv); void kv_print(kv_t* kv); The implementation is 948 lines of code - pretty small! Here's what it generally looks like to use. char buffer[1024]; kv_reset(kv, buffer, sizeof(buffer), CUTE_KV_MODE_WRITE); thing_t thing; thing.a = 5; thing.b = 10.3f; thing.str = "Hello."; thing.str_len = 7; kv_begin_object(kv); kv_key(kv, "a"); kv_val(kv, &thing.a); kv_key(kv, "b"); kv_val(kv, &thing.b); kv_key(kv, "str"); kv_val(kv, &thing.str, &thing.str_len); kv_object_end(kv); printf("%s", buffer); Which would output: { a = 5, b = 10.300000, str = "Hello." } Depending on if mode is set to read/write the kv_* functions will either write to the buffer, or read (parse) from the buffer. This means the serialization routine only needs to be written once (most of the time) by using some polymorphism. If anyone was brave enough to read through all this info, allow me to say thanks! I really appreciate it https://ift.tt/eA8V8J
Hi all. I wrote a serialization API in C++ for myself and wanted to ask for a critique from anyone interested. I like the JSON file format and wanted to use it originally. JSON is a good way to support versioning for your serialized stuff, since you can build in mechanisms to handle missing fields, and extra fields can be ignored. I audited a few JSON options and this one by sheredom was the best, but I still found it a little lacking. Specifically I couldn't figure out the API. It's all... Weird. And the examples are terrible. Since I couldn't find exactly what I wanted I wrote my own. Here were my specific list of requirements. No external dependencies (other than c runtime). JSON-like text output. Can implement the writer/reader with the same code (instead of two different functions). No annoying linked-lists in the API. Supports arrays. Base64 encoding built-in. Shouldn't do anything special for utf8 (they can just be base64 encoded, or sent as-is). Can inspect a field's type before attempting to read it. Arrays have length prepended. Example output. { x = 5, y = 10.300000, str = "Hello.", sub_thing = { num0 = 7, num1 = 3, }, blob_data = "U29tZSBibG9iIGlucHV0LgA=", array_of_ints = [8] { 0, 1, 2, 3, 4, 5, 6, 7, }, array_of_array_of_ints = [2] { [3] { 0, 1, 2 }, [3] { 0, 1, 2 }, }, array_of_objects = [2] { { some_integer = 13, some_string = "Good bye.", }, { some_integer = 4, some_string = "Oi!", }, }, }, The header itself, kv.h. struct kv_t; #define CUTE_KV_MODE_WRITE 1 #define CUTE_KV_MODE_READ 0 kv_t* kv_make(void* user_allocator_context = NULL); void kv_destroy(kv_t* kv); error_t kv_reset(kv_t* kv, const void* data, int size, int mode); int kv_size_written(kv_t* kv); enum kv_type_t { KV_TYPE_NULL = 0, KV_TYPE_INT64 = 1, KV_TYPE_DOUBLE = 2, KV_TYPE_STRING = 3, KV_TYPE_ARRAY = 4, KV_TYPE_BLOB = 5, KV_TYPE_OBJECT = 6, }; error_t kv_key(kv_t* kv, const char* key, kv_type_t* type = NULL); error_t kv_val(kv_t* kv, uint8_t* val); error_t kv_val(kv_t* kv, uint16_t* val); error_t kv_val(kv_t* kv, uint32_t* val); error_t kv_val(kv_t* kv, uint64_t* val); error_t kv_val(kv_t* kv, int8_t* val); error_t kv_val(kv_t* kv, int16_t* val); error_t kv_val(kv_t* kv, int32_t* val); error_t kv_val(kv_t* kv, int64_t* val); error_t kv_val(kv_t* kv, float* val); error_t kv_val(kv_t* kv, double* val); error_t kv_val_string(kv_t* kv, char** str, int* size); error_t kv_val_blob(kv_t* kv, void* data, int* size, int capacity); error_t kv_object_begin(kv_t* kv); error_t kv_object_end(kv_t* kv); error_t kv_array_begin(kv_t* kv, int* count); error_t kv_array_end(kv_t* kv); void kv_print(kv_t* kv); The implementation is 948 lines of code - pretty small! Here's what it generally looks like to use. char buffer[1024]; kv_reset(kv, buffer, sizeof(buffer), CUTE_KV_MODE_WRITE); thing_t thing; thing.a = 5; thing.b = 10.3f; thing.str = "Hello."; thing.str_len = 7; kv_begin_object(kv); kv_key(kv, "a"); kv_val(kv, &thing.a); kv_key(kv, "b"); kv_val(kv, &thing.b); kv_key(kv, "str"); kv_val(kv, &thing.str, &thing.str_len); kv_object_end(kv); printf("%s", buffer); Which would output: { a = 5, b = 10.300000, str = "Hello." } Depending on if mode is set to read/write the kv_* functions will either write to the buffer, or read (parse) from the buffer. This means the serialization routine only needs to be written once (most of the time) by using some polymorphism. If anyone was brave enough to read through all this info, allow me to say thanks! I really appreciate it
from GameDev.net http://bit.ly/30h5LxG
from GameDev.net http://bit.ly/30h5LxG
ليست هناك تعليقات