сериализовать любой тип данных как векториспользовать reinterpret_cast? - PullRequest
6 голосов
/ 02 июля 2010

Я не нашел ничего, что напрямую связано с поиском, поэтому, пожалуйста, прости, если это дубликат.

Я хочу сериализовать данные через сетевое соединение.Мой подход состоит в том, чтобы преобразовать все, что мне нужно, в std::vector< uint8_t > и на принимающей стороне распаковать данные в соответствующие переменные.Мой подход выглядит следующим образом:

template <typename T>
inline void pack (std::vector< uint8_t >& dst, T& data) {
    uint8_t * src = static_cast < uint8_t* >(static_cast < void * >(&data));
    dst.insert (dst.end (), src, src + sizeof (T));
}   

template <typename T>
inline void unpack (vector <uint8_t >& src, int index, T& data) {
    copy (&src[index], &src[index + sizeof (T)], &data);
}

который я использую как

vector< uint8_t > buffer;
uint32_t foo = 103, bar = 443;
pack (buff, foo);
pack (buff, bar);

// And on the receive side
uint32_t a = 0, b = 0;
size_t offset = 0;
unpack (buffer, offset, a);
offset += sizeof (a);
unpack (buffer, offset, b);

Меня беспокоит линия

uint8_t * src = static_cast < uint8_t* >(static_cast < void * >(&data));

(что я понимаю, чтобы сделать то же самое, что и reinterpret_cast).Есть ли лучший способ сделать это без двойного приведения?

Мой наивный подход состоял в том, чтобы просто использовать static_cast< uint8_t* >(&data), который потерпел неудачу.Мне говорили в прошлом , что reinterpret_cast - это плохо.Поэтому я хотел бы избежать этого (или конструкции, которую я имею в настоящее время), если это возможно.

Конечно, всегда есть uint8_t * src = (uint8_t *)(&data).

Предложения?

Ответы [ 4 ]

16 голосов
/ 02 июля 2010

Я предлагаю игнорировать всех людей, говорящих вам, что reinterpret_cast - это плохо.Они говорят, что это плохо, потому что обычно не рекомендуется брать карту памяти одного типа и делать вид, что это другой тип.Но в этом случае это именно то, что вы хотите сделать, так как ваша цель - передать карту памяти в виде последовательности байтов.

Это гораздо лучше, чем использовать двойной - static_cast, так какэто полностью детализирует факт, что вы берете один тип и целенаправленно притворяетесь, что это что-то другое.Это именно то, для чего нужна reinterpret_cast, и уклонение от ее использования с посредником пустого указателя просто затеняет ваш смысл без пользы.

Кроме того, я уверен, что вы знаете об этом, ночасы для указателей в T.

8 голосов
/ 05 ноября 2014

Ваша ситуация - именно то, для чего reinterpret_cast, она проще, чем двойная static_cast и четко документирует, что вы делаете.

Чтобы быть в безопасности, вы должны использовать unsigned char вместоuint8_t:

  • выполнение reinterpret_cast до unsigned char * и последующая разыменование результирующего указателя является безопасным и переносимым и явно разрешено [basic.lval] §3.10 / 10
  • выполнение reinterpret_cast до std::uint8_t * и последующая разыменование результирующего указателя является нарушением строгого правила псевдонимов и неопределенным поведением, если std::uint8_t реализован как расширенный тип целого без знака.

    Если он существует, uint8_t всегда должен иметь ту же ширину, что и unsigned char.Однако это не обязательно должен быть тот же тип;это может быть отдельный расширенный целочисленный тип.Это также не обязательно должно иметь то же представление, что и unsigned char (см. Когда uint8_t ≠ unsigned char? ).

    (Это не совсем гипотетически: сделать [u]int8_t специальным расширеннымцелочисленный тип допускает некоторые агрессивные оптимизации)

Если вы действительно хотите uint8_t, вы можете добавить:

static_assert(std::is_same<std::uint8_t, unsigned char>::value,
              "We require std::uint8_t to be implemented as unsigned char");

, чтобы код не компилировался наплатформы, на которых это приведет к неопределенному поведению.

2 голосов
/ 02 июля 2010

Вы можете избавиться от одного приведения, используя тот факт, что любой указатель может быть неявно приведен к void*.Кроме того, вы можете добавить несколько const:

//Beware, brain-compiled code ahead!
template <typename T>
inline void encode (std::vector< uint8_t >& dst, const T& data)
{
    const void* pdata = &data;
    uint8_t* src = static_cast<uint8_t*>(pdata);
    dst.insert(dst.end(), src, src + sizeof(T));
}

Возможно, вы захотите добавить проверку во время компиляции, чтобы T был POD, без struct, и без указателя.

Однако интерпретация памяти некоторого объекта на уровне байтов никогда не будет сохраняться, точка.Если вам нужно сделать это, то сделайте это в красивой обертке (как вы сделали) и покончите с этим.Когда вы портируете на другую платформу / компилятор, обратите внимание на эти вещи.

1 голос
/ 02 июля 2010

Вы не делаете здесь никакой реальной кодировки , вы просто копируете необработанное представление данных из памяти в байтовый массив и затем отправляете его по сети.Это не сработает.Вот краткий пример того, почему:

struct A {
  int a;
};

struct B {
  A* p_a;
}

Что происходит, когда вы используете свой метод для отправки B по сети?Получатель получает p_a, адрес какого-либо объекта A на вашем компьютере, но этот объект не находится на его компьютере.И даже если вы отправите им объект A, он не будет находиться по тому же адресу.Там нет никакого способа, который может работать, если вы просто отправите необработанную B структуру.И это даже не учитывая более тонкие проблемы, такие как порядковый номер и представление с плавающей точкой, которые могут повлиять на передачу таких простых типов, как int и double.

То, что вы делаете прямо сейчас, принципиально не отличается отприведение к uint8_t* относительно того, сработает он или нет (не сработает, за исключением самых тривиальных случаев).

Вам нужно разработать метод сериализации .Сериализация означает любой способ решения такого рода проблем: как вывести объекты в памяти в сеть в такой форме, чтобы их можно было существенно реконструировать с другой стороны.Это сложная проблема, но это хорошо известная и неоднократно решаемая проблема.Вот хорошая отправная точка для чтения: http://www.parashift.com/c++-faq-lite/serialization.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...