Приведение большого POD в маленькое POD - гарантированно сработает?

Предположим, у меня есть структура POD, которая имеет более 40 членов. Эти члены не являются встроенными типами, скорее большинство из них являются структурами POD, которые, в свою очередь, имеют много членов, большинство из которых снова являются структурами POD. Этот шаблон распространяется на многие уровни - POD имеет POD, имеет POD и так далее - вплоть до 10 или около того уровней.

Я не могу опубликовать фактический код, поэтому вот один очень простой пример этого шаблона:

//POD struct
struct Big  
{
    A a[2];  //POD has POD
    B b;     //POD has POD
    double dar[6];
    int m;
    bool is;
    double d;
    char c[10];
};

А также A а также B определяются как:

struct A
{
    int i;
    int j;
    int k;
};

struct B
{
    A a;   //POD has POD 
    double x;
    double y;
    double z;
    char *s;
};

Это действительно очень упрощенная версия кода, который был написан (на C) почти 20 лет назад Citrix Systems, когда они разработали протокол ICA. За прошедшие годы код сильно изменился. Теперь у нас есть исходный код, но мы не можем знать, какой код используется в текущей версии ICA, а какой был отброшен, так как отброшенная часть также присутствует в исходном коде.

Это фон проблемы. Проблема в том, что теперь у нас есть исходный код, и мы строим систему на основе протокола ICA, для которой в какой-то момент нам нужно знать значения нескольких членов большой структуры. Мало членов, не все. К счастью, эти члены появляются в начале структуры, поэтому мы можем написать структуру, которая является частью большой структуры, как:

//Part of struct B 
//Whatever members it has, they are in the same order as they appear in Big.
struct partBig
{
    A a[2];
    B b;
    double dar[6];
    //rest is ignored
};

Теперь предположим, что мы знаем указатель на Big struct (которую мы знаем путем расшифровки протокола и потоков данных), тогда мы можем написать это:

Big *pBig = GetBig(); 
partBig *part = (partBig*)pBig; //Is this safe?
/*here can we pretend that part is actually Big, so as to access 
first few members safely (using part pointer), namely those 
which are defined in partBig?*/

Я не хочу определять весь Big struct в нашем коде, так как в нем слишком много членов POD, и если я полностью определю структуру, мне нужно сначала определить сотни других структур. Я не хочу этого, так как даже если я это сделаю, я сомневаюсь, что смогу сделать это правильно, так как я не знаю все структуры правильно (относительно того, какая версия используется сегодня, а какая отвергнута).

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

Соответствующие ссылки из спецификации языка будут оценены.:-)

Вот демонстрация такого кастинга: http://ideone.com/c7SWr

4 ответа

Спецификация языка имеет некоторые функции, которые похожи на то, что вы пытаетесь сделать: в 6.5.2.3/5 говорится, что если у вас есть несколько структур, которые имеют общую начальную последовательность членов, вы можете проверять этих общих членов через любой из структуры в союзе, независимо от того, какой из них в настоящее время активен. Таким образом, из этой гарантии легко вывести, что структуры с общей начальной последовательностью элементов должны иметь одинаковую структуру памяти для этих общих элементов.

Однако язык, по-видимому, явно не позволяет делать то, что вы делаете, то есть переосмысливать один структурный объект как другой не связанный структурный объект посредством приведения указателя. (Язык позволяет получить доступ к первому члену объекта структуры через приведение указателя, но не к тому, что вы делаете в своем коде). Таким образом, с этой точки зрения то, что вы делаете, может быть нарушением строгого псевдонима (см. 6.5/7). Хотя я не уверен в этом, так как мне не сразу понятно, было ли намерение 6.5 / 7 запретить такой доступ.

Однако, в любом случае, я не думаю, что компиляторы, которые используют строгие правила псевдонимов для оптимизации, делают это на уровне агрегированных типов. Скорее всего, они делают это только на уровне фундаментальных типов, что означает, что ваш код должен работать на практике.

Да, при условии, что ваша маленькая структура имеет те же правила упаковки / заполнения, что и большая, все будет в порядке. Порядок расположения в памяти полей в структуре хорошо определяется спецификациями языка.

Единственный способ получить BigPart от Big использует reinterpret_cast (или стиль C, как в вашем вопросе), и стандарт не определяет, что происходит в этом случае. Тем не менее, он должен работать как ожидалось. Вероятно, он потерпит неудачу только для некоторых супер экзотических платформ.

Соответствующее правило псевдонимов типов - 3.10/10: "Если программа пытается получить доступ к сохраненному значению объекта через glvalue другого, чем один из следующих типов, поведение не определено: ...".

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

  1. тип, подобный (как определено в 4.4) динамическому типу объекта
  2. агрегатный или объединенный тип, который включает в себя один из вышеупомянутых типов среди своих элементов или нестатических элементов данных (включая рекурсивно элемент или элемент нестатических данных субагрегата или содержащего объединения)

Первый случай недостаточен: он просто охватывает cv-квалификации. Второй случай позволяет на union хак, что упоминает AndreyT (общая начальная последовательность), но не что-нибудь еще.

Другие вопросы по тегам