Предположим, у меня есть массив без знака, который представляет собой группу объектов POD (например, прочитанных из сокета или через mmap). Какие типы они представляют и в какой позиции определяются во время выполнения, но мы предполагаем, что каждый из них уже правильно выровнен.Массив байтов в POD
Каков наилучший способ «бросить» эти байты в соответствующий тип POD?
Решение должно либо соответствовать стандарту C++ (скажем,> = C++ 11), либо, по крайней мере, гарантированно работать с g ++> = 4.9, clang ++> = 3.5 и MSVC> = 2015U3. РЕДАКТИРОВАТЬ: В linux, windows, работает под управлением x86/x64 или 32/64-бит.
В идеале я хотел бы сделать что-то вроде этого:
uint8_t buffer[100]; //filled e.g. from network
switch(buffer[0]) {
case 0: process(*reinterpret_cast<Pod1*>(&buffer[4]); break;
case 1: process(*reinterpret_cast<Pod2*>(&buffer[8+buffer[1]*4]); break;
//...
}
или
switch(buffer[0]) {
case 0: {
auto* ptr = new(&buffer[4]) Pod1;
process(*ptr);
}break;
case 1: {
auto* ptr = new(&buffer[8+buffer[1]*4]) Pod2;
process(*ptr);
}break;
//...
}
И, кажется, работают, но оба AFAIK неопределенное поведение в C++ 1). И только для полноты: Я в курсе «обычного» решения, чтобы просто скопировать материал в соответствующую локальную переменную:
Pod1 tmp;
std::copy_n(&buffer[4],sizeof(tmp), reinterpret_cast<uint8_t*>(&tmp));
process(tmp);
В некоторых ситуациях не может быть никаких накладных расходов в других это и в некоторых ситуациях это может быть даже быстрее, но производительность в стороне, я больше не могу, например, измените данные на месте и, честно говоря, меня просто раздражает, когда я знаю, что у меня есть правильные биты в соответствующем месте в памяти, но я просто не могу их использовать.
Несколько ума решение, которое я придумал это:
template<class T>
T* inplace_cast(uint8_t* data) {
//checks omitted for brevity
T tmp;
std::memmove((uint8_t*)&tmp, data, sizeof(tmp));
auto ptr = new(data) T;
std::memmove(ptr, (uint8_t*)&tmp, sizeof(tmp));
return ptr;
}
г ++ и лязг ++, кажется, чтобы быть в состоянии оптимизировать на эти копии, но я думаю, что это ставит много нагрузки на оптимизатор и может привести к сбою других оптимизаций, не работает с const uint8_t*
(хотя я не хочу его изменять), и выглядит просто ужасно (не думайте, что вы получите этот обзор кода).
1) Первый УБ, потому что она нарушает строгие ступенчатости, вторая, вероятно, УБ (discussed here), так как стандарт просто говорит о том, что результирующий объект не инициализирован и имеет неопределенное значение (вместо гарантируя, что базовая память не затронута). Я считаю, что первый эквивалентный c-код хорошо определен, поэтому компиляторы могут разрешить это для совместимости с c-заголовками, но я не уверен в этом.
Вы не ответили на свой вопрос? 'Каков наилучший способ« бросить »эти байты в соответствующий тип POD?» «Я знаю о« обычном »решении просто скопировать материал в соответствующую локальную переменную. – deviantfan
Если реальный вопрос заключается в том, как решить 'это просто раздражает меня, чтобы знать, что у меня есть правильные биты в соответствующем месте в памяти, но я просто не могу их использовать.", тогда, возможно, C++ не подходит для вас, или, по крайней мере, объекты не являются правильная вещь. Если вам нужны биты, зачем вообще использовать структуры/классы для данных? Просто возьмите массив байтов и измените его так, как хотите. – deviantfan
@deviantfan: Это решение не является «отличным», и я также перечислял некоторые объективные причины, почему я не доволен им (надстройка и модификация на месте). Причина, по которой я использую C++, заключается в том, что он позволяет мне (по большей части) использовать мощные абстракции, с одной стороны, но сходить туда, где это необходимо (я занимаюсь программированием микроконтроллеров). Это одна конкретная ситуация - единственная, с которой я столкнулся, где C++ не дает мне достаточного контроля. – MikeMB