Безопасный способ переинтерпретировать необработанную структуру с определенным смещением и типом?

return *reinterpret_cast<UInt32*>((reinterpret_cast<char*>(this) + 2));

Структура упакована прагмой 1 и содержит кучу полей uint, char, short...

Поскольку это UInt32, следует ли сначала переинтерпретировать_приведение к беззнаковому char* вместо этого или это вообще имеет значение?

Кроме того, скорость здесь имеет решающее значение, и я считаю, что reinterpret_cast является самым быстрым из приведений, в отличие от static_cast.

РЕДАКТИРОВАТЬ: структура фактически состоит из двух однобайтовых полей, за которыми следует объединение примерно 16 других структур, 15 из которых имеют UInt32 в качестве первого поля. Я быстро проверяю, что это не тот, у которого нет, а затем делаю переинтерпретацию_приведения к смещению в 2 байта.


person chriskirk    schedule 29.06.2011    source источник
comment
Я считаю, что reinterpret_cast медленнее, чем static_cast.   -  person Nawaz    schedule 29.06.2011
comment
@Nawaz какие-нибудь ссылки на это заявление?   -  person chriskirk    schedule 29.06.2011
comment
@Наваз: Правда? Кажется маловероятным..   -  person Lightness Races in Orbit    schedule 29.06.2011
comment
Откуда у тебя 2? Если это переменная, содержащая известное смещение члена Uint32 структуры, но во время выполнения это будет другой член в соответствии с некоторым свойством времени выполнения, то, возможно, вместо этого вы могли бы использовать указатель на член. Если вы читаете какую-то смесь различных данных в стиле ASN.1, которая сообщает вам смещения различных битов самой себя, то переносимый способ подобрать 4-байтовое целое число — это по одному байту за раз, сдвиг и ИЛИ. Если вы хотите быстро, вам нужно сделать что-то конкретное для реализации. UInt32 выглядит Windows-ой, но кто его знает.   -  person Steve Jessop    schedule 29.06.2011
comment
@Nawaz: определите медленнее для операции времени компиляции.   -  person Martin York    schedule 29.06.2011
comment
Почему вы не можете просто получить доступ к члену?   -  person Karl Knechtel    schedule 29.06.2011
comment
@Martin: мешает оптимизатору.   -  person MSalters    schedule 29.06.2011
comment
@MSalters: оптимизатору все равно. Приведения в основном говорят компилятору доверять пользователю. После этого типы указаны пользователем, компилятору/оптимизатору все равно, если вы сделаете ошибку.   -  person Martin York    schedule 30.06.2011


Ответы (4)


Разве вы не можете просто получить доступ к члену напрямую? Это неопределенное поведение, и оно вообще не будет работать в системах, которые обеспечивают выравнивание слов (что, вероятно, не является проблемой, учитывая, что вы это делаете, но необходимо упомянуть).

reinterpret_cast не будет быстрее, чем static_cast, потому что они просто сообщают компилятору, как использовать память во время компиляции. Однако dynamic_cast будет медленнее.

Нет законного способа просто рассматривать ваш struct + offset как тип, отличный от char.

person Mark B    schedule 29.06.2011
comment
Пожалуйста, смотрите мой комментарий ниже. Я бы просто получил к нему доступ напрямую, однако местоположение элемента находится внутри объединения, и я делаю предварительную проверку перед выполнением reinterpret_cast - person chriskirk; 29.06.2011

reinterpret_cast и static_cast должны иметь одинаковое время выполнения — близкое к нулю, если только не требуется числовое преобразование. Вы должны выбирать бросок для использования не на основе «скорости», а на основе правильности. Если вы говорили о dynamic_cast, у вас может быть причина для спора, но как reinterpret_cast, так и static_cast обычно приводят (в худшем случае) к копированию регистра (например, из целочисленного регистра в регистр с плавающей запятой). (Предполагая, что никакие пользовательские операторы преобразования не попадают в картину, тогда это вызов функции со всеми сопутствующими вещами)

Не существует безопасного способа делать то, что вы делаете. Это нарушает строгое правило псевдонимов. Если вы хотите сделать что-то подобное, ваш struct должен быть в какой-то форме union, где вы получаете доступ к UInt32 через объединение.

Наконец, как уже упоминалось, этот пример не будет работать на любой платформе с проблемами выравнивания. Это означает, что у вас все будет хорошо на x86, но, например, не будет хорошо на x64.

person Billy ONeal    schedule 29.06.2011
comment
Мне нравится твой ответ. Пожалуйста, проверьте мой отредактированный пост, так как он может вас заинтересовать. - person chriskirk; 29.06.2011
comment
@chriskirk: Если это так, нет причин reinterpret_cast - просто получите доступ к члену любого из этих 16 союзов. Они будут занимать одно и то же место в памяти, верно? - person Billy ONeal; 29.06.2011

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

struct AnyInfoStruct {
   char Name[65]; 
   char Address[65]; 
   short Whatever; 
   uint Years;
   union AExtraData { 
      int A; 
      char B; 
      double C; 
   } ExtraData
}; 

// recieves generic pointer, hidding struct fields:
void showMsg(void* AnyPtr)
{
  AnyInfoStruct* MyAnyInfo = &(static_cast<*AnyPtr>);
  cout << "Years: " << MyAnyInfo->Years << "\n";

  cout << "ExtraData.A: " << MyAnyInfo->ExtraData.A << "\n";
}

void main()
{
  AnyInfoStruct* MyAnyInfo; 

  // hide struct into a ptr
  void* AnyPtr = AnyInfoStruct;

  showMsg(MyAnyInfo);
}

Ваше здоровье.

ОБНОВЛЕНИЕ 1: добавлено «союз» в пример.

person umlcat    schedule 29.06.2011
comment
На самом деле член, к которому я пытаюсь получить доступ, находится в объединении внутри структуры. - person chriskirk; 29.06.2011

Поскольку вы говорите, что структура содержит целые числа и шорты, я рискну и отвечу, исходя из предположения, что это объединение является POD. Если это так, то вы получаете выгоду от 9,5/1:

одна специальная гарантия делается для того, чтобы упростить использование объединений: если POD-union содержит несколько POD-структур, которые имеют общую начальную последовательность (9.2), и если объект этого типа POD-union содержит одну из POD- структур разрешено проверять общую начальную последовательность любого из элементов POD-структуры.

Итак, предположим, что ваша структура выглядит так:

struct Foo1 { UInt32 a; other stuff; };
struct Foo2 { UInt32 b; other stuff; };
...
struct Foo15 { UInt32 o; other stuff; };
struct Bar { UInt16 p; other stuff; };

// some kind of packing pragma
struct Baz {
    char is_it_Foo;
    char something_else;
    union {
        Foo1 f1;
        Foo2 f2;
        ...
        Foo15 f15;
        Bar b;
    } u; 
};

Тогда вы можете сделать это:

Baz *baz = whatever;
if (baz->is_it_Foo) {
    UInt32 n = baz->u.f1.a;
}

Если члены объединения не являются POD, то ваш reinterpret_cast все равно не работает, так как больше нет никакой гарантии, что первый элемент данных структуры расположен по смещению 0 от начала структуры.

person Steve Jessop    schedule 29.06.2011