Я пытаюсь реализовать десериализацию, где отображение поля / члена известно только во время выполнения (это сложно). В любом случае, я пытаюсь сделать что-то вроде следующего:
Class A
{
public:
int a; // index 0
float b; // index 1
char c; // index 2
}
Затем у меня есть два массива, один с индексом поля, а другой с чем-то, что указывает тип. Затем я хочу перебрать массивы и записать в поля из байтового потока.
Извините за дерьмовое описание, но я просто не знаю, как реализовать это в коде. Любые идеи будут оценены спасибо!
Да, вы можете, есть две вещи, на которые нужно обратить внимание при выполнении этого.
Прежде всего, убедитесь, что вы начинаете писать с (const char*)&A.a
потому что все компиляторы добавляют вещи, которые вас не касаются в начале объекта (например, Visualc помещает туда vtable), и вы не будете писать то, что вы думаете, если начнете с адреса объекта.
Во-вторых, вы можете сделать #pragma pack(1)
перед объявлением любого класса, который должен быть записан на диск, потому что компиляторы обычно выравнивают члены класса, чтобы сделать передачу DMA более эффективной, и у вас могут также возникнуть проблемы с этим.
Что касается динамической части, если создание одного определения класса для каждой комбинации полей, которую вы хотите иметь, является приемлемым, то это нормально делать так, иначе вам лучше включить хеш-таблицу в ваш класс и сериализацию / десериализацию его содержимое путем записи пар ключ-значение в файл
Я не могу придумать языковую конструкцию, которая сможет дать вашему адресу поля заданный индекс во время выполнения. Если бы вы могли иметь массив «type» для включения размеров полей, вы бы могли сделать что-то вроде:
istream &in = <get it somehow>;
size_t *field_size = <get it somehow>;
size_t num_of_fields = <get it somehow>;
A a;
char *ptr = reinterpret_cast<char *>(&a);
for (int i = 0; i < num_of_fields; i++)
{
in.read(ptr, field_size[i]);
ptr += field_size[i];
}
Обратите внимание, что это будет верно, если ваш класс прост и не имеет членов виртуальных функций
(или унаследовал от такого класса). Если это так, вам лучше включить фиктивного члена
для получения байтового смещения, где поля начинаются внутри класса:
class A
{
int __dummy; /* must be the first data member in the class */
...
<rest of your class definition here>
};
а теперь измените инициализацию PTR следующее:
ptr = reinterpret_cast<char *>(&a) + offsetof(A, __dummy);
Другое неявное предположение для этого кода состоит в том, что порядок машинных байтов одинаков как для машины, на которой выполняется этот код, так и для машины, с которой поступают сериализованные данные. Если нет, то вам нужно будет преобразовать порядок байтов данных, прочитанных из потока. Это преобразование, конечно, зависит от типа, но вы можете иметь другой массив функций преобразования для каждого поля.
Есть много вопросов и решений, необходимых. В самом простом, вы могли бы держать смещение в A
для каждого поля вы можете включить тип и установить указатель на поле. Например — при условии, что есть int16_t
кодирование номеров полей во входном потоке, не прикладывая усилий к использованию static_cast<>
и т.д., где это немного приятнее, и предполагается, что входной терминатор числа поля 0 …
A a;
char* pa = (char*)&a;
char* p_v = (char*)&input_buffer;
...
while ((field_num = *(int16_t)p_v) && (p_v += sizeof(int16_t)))
switch (type[field_num])
{
case Int32:
*(int32_t*)(p_a + offset[field_num]) = *(int32_t*)(p_v);
p_v += sizeof(int32_t);
break;
...
}
Вы можете рассмотреть возможность использования, например, ntohl()
и т. д. для обработки преобразований с порядком байтов.
Пусть компилятор сделает это:
Написать operator>>
функция.