Сравнение union'ов
От: mp_op  
Дата: 11.08.11 07:28
Оценка:
Подскажите пожалуйста, как корректно (по стандарту) сравнить два union`а гарантированно одинакового размера, но неизвестного содержания (см. пример для ясности). Какие тут могут быть подводные камни?
union u1
{
     int *   a;
     void * b;
} v1;

union u2
{
    int *   a;
    char * b;
    void * c;
} v2;

v1 == v2; //??? какой из вариантов на данном этапе активирован - неизвестно
Re: Сравнение union'ов
От: Pavel Dvorkin Россия  
Дата: 11.08.11 07:41
Оценка: 2 (1)
Здравствуйте, mp_op, Вы писали:

_>Подскажите пожалуйста, как корректно (по стандарту) сравнить два union`а гарантированно одинакового размера, но неизвестного содержания (см. пример для ясности). Какие тут могут быть подводные камни?

_>
_>union u1
_>{
_>     int *   a;
_>     void * b;
_>} v1;

_>union u2
_>{
_>    int *   a;
_>    char * b;
_>    void * c;
_>} v2;

_>v1 == v2; //??? какой из вариантов на данном этапе активирован - неизвестно
_>


Побитно сравнить можно, например, с помощью memcmp, но смысла в этом немного.
With best regards
Pavel Dvorkin
Re[2]: Сравнение union'ов
От: mp_op  
Дата: 11.08.11 07:55
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Побитно сравнить можно, например, с помощью memcmp, но смысла в этом немного.


А нет никакой лазейки, основанной на том, что и там и там void* присутствует, а все остальные типы неявно к нему приводятся?
Re[3]: Сравнение union'ов
От: Pavel Dvorkin Россия  
Дата: 11.08.11 08:02
Оценка:
Здравствуйте, mp_op, Вы писали:

_>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Побитно сравнить можно, например, с помощью memcmp, но смысла в этом немного.


_>А нет никакой лазейки, основанной на том, что и там и там void* присутствует, а все остальные типы неявно к нему приводятся?


Да сравнивать можно какие угодно (по типу) указатели, только вот чего ты хочешь добиться ? Убедиться, что в обоих юнионах сейчас один и тот же указатель ? Это — бога ради.

А если в юнионах еще что-то иное есть, тогда какой смысл ?
With best regards
Pavel Dvorkin
Re[4]: Сравнение union'ов
От: mp_op  
Дата: 11.08.11 08:13
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, mp_op, Вы писали:


_>>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>>Побитно сравнить можно, например, с помощью memcmp, но смысла в этом немного.


_>>А нет никакой лазейки, основанной на том, что и там и там void* присутствует, а все остальные типы неявно к нему приводятся?


PD>Убедиться, что в обоих юнионах сейчас один и тот же указатель ?

Да. Просто вот в чем дело, вроде как по стандарту считается некорректным класть в юнион один тип, а получать другой (union cast). Так вот вопрос мой по сути о том, не является ли исключением из правил такая ситуация:
    union u1
    {
        int *  a;
        void * b;
    };

    int * aptr = 0;
    u1 v1 = { aptr };
    void * bptr = v1.b;

И не равнозначна ли она такой:
    int * aptr = 0;
    void * bptr = aptr;


PD>А если в юнионах еще что-то иное есть, тогда какой смысл ?

Нет. Только указатели.
Re[5]: Сравнение union'ов
От: Pavel Dvorkin Россия  
Дата: 11.08.11 09:49
Оценка:
Здравствуйте, mp_op, Вы писали:

_>Да. Просто вот в чем дело, вроде как по стандарту считается некорректным класть в юнион один тип, а получать другой (union cast). Так вот вопрос мой по сути о том, не является ли исключением из правил такая ситуация:

_>
_>    union u1
_>    {
_>        int *  a;
_>        void * b;
_>    };

_>    int * aptr = 0;
_>    u1 v1 = { aptr };
_>    void * bptr = v1.b;
_>

_>И не равнозначна ли она такой:
_>
_>    int * aptr = 0;
_>    void * bptr = aptr;
_>


Я не большой знаток и любитель стандарта, но ИМХО вообще некорректно класть в юнион одно, а брать другое. Хотя в реальной ситуации вполне может сработать.

А зачем все эти проблемы, если там только указатели ?
With best regards
Pavel Dvorkin
Re: Сравнение union'ов
От: Centaur Россия  
Дата: 11.08.11 12:59
Оценка: +3
Здравствуйте, mp_op, Вы писали:

_>Подскажите пожалуйста, как корректно (по стандарту) сравнить два union`а гарантированно одинакового размера, но неизвестного содержания


Для начала следует определиться, что значит «объект a типа union A равен объекту b типа union B». В общем случае это неосмысленное отношение.

В частном случае, когда имена и типы полей совпадают, скорее всего, понятие равенства будет выглядеть как «в обоих объектах активен один и тот же вариант И значения этих вариантов равны», для какого-нибудь набора предикатов равенства над парами каждого варианта.
Re: Сравнение union'ов
От: uzhas Ниоткуда  
Дата: 11.08.11 13:28
Оценка: 7 (2)
Здравствуйте, mp_op, Вы писали:

рекомендую к прочтению: http://stackoverflow.com/questions/740577/sizeof-a-union-in-c-c
чтобы сравнивать на равенство структуры с разным строением, нужно осуществить проекцию обоих типов на некий третий тип, на котором определить отношение эквивалентности (чтобы выполнялись стандартные правила типа симметричности, транзитивности и рефлексивности)
например, оба юниона можно превратить в int*, где уже определено отношение равенства:
v1.a == v2.a;
Re[2]: Сравнение union'ов
От: mp_op  
Дата: 11.08.11 15:10
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Здравствуйте, mp_op, Вы писали:


_>>Подскажите пожалуйста, как корректно (по стандарту) сравнить два union`а гарантированно одинакового размера, но неизвестного содержания


C>Для начала следует определиться, что значит «объект a типа union A равен объекту b типа union B».

sizeof(u1) == sizeof(u2)

C>В общем случае это неосмысленное отношение.

Общий случай мне и не нужен. Вот еще раз пример, который я приводил, давайте отталкиваться от него.
union u1
{
     int *  a;
     void * v;
} v1;

union u2
{
    int *  a;
    char * b;
    void * v;
} v2;

uzhas написал, про сведение к единому типу, если почитать ветку с Pavel Dvorkin, то я там как раз это и предлагаю. Только вместо int* — void*.
В юнионах всегда только указатели и всегда присутствует void*.
Мой вопрос в следующем, корректно ли по стандарту сравнивать

v1.v == v2.v

при условии, что изначально записаны в v1 был int*, а в v2 — char*?
Равносильно ли это простому приведению к типу void*? Даже чуть больше уточню, интересует сравнение на неравенство (и только).

Я знаю, что union_cast`ы запрещены. Но знаю, что любой указатель на объект в С++ можно сконвертировать в void* неявно. Нет ли тут лазейки?

C>В частном случае, когда имена и типы полей совпадают, скорее всего, понятие равенства будет выглядеть как «в обоих объектах активен один и тот же вариант И значения этих вариантов равны», для какого-нибудь набора предикатов равенства над парами каждого варианта.

В том и дело, что какой из вариантов активен — неизвестно. Нужно просто сравнить v1 и v2. Хочу узнать корректный способ. Типы полей совпадают всегда только в одном — есть void*, все остальные могут быть произвольные указатели на объекты (только указатели). Исходя из этого все мои юнионы всегда sizeof(U) == sizeof(void*), так как получается, что void* всегда самый большой член юниона (по причине конвертируемости в него всех остальных указателей).

Немного сумбурно, но вроде сейчас больше нет недомолвок. Спасибо.
Re[6]: Сравнение union'ов
От: mp_op  
Дата: 11.08.11 15:30
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>А зачем все эти проблемы, если там только указатели ?


Надо сравнить указатели в юнионах. Так как я не знаю что там за варианты конкретно активированы, ищу способ это побороть не прибегая к хакам.
Re[7]: Сравнение union'ов
От: Pavel Dvorkin Россия  
Дата: 11.08.11 16:00
Оценка: 2 (1) +2
Здравствуйте, mp_op, Вы писали:

_>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>А зачем все эти проблемы, если там только указатели ?


_>Надо сравнить указатели в юнионах. Так как я не знаю что там за варианты конкретно активированы, ищу способ это побороть не прибегая к хакам.


Если реально в юнионе один указатель, то не создавай проблем себе.


union A{
    int* pi;
    char* pc;
};
union B {
    void* pv;
    double* pd;
    int* pi;
};
int _tmain(int argc, _TCHAR* argv[])
{
    A a;
    a.pi = NULL;
    B b;
    b.pd = NULL;
    void* p1 = a.pi;
    void* p2 = b.pv;
    if(p1 == p2)
    {
        printf("Equal\n");
    }

}


За точное соответствие стандарту не поручусь, но

все указатели (не на члены классов!) имеют один и тот же размер
все указатели можно приводить к void*

А уж если хочется точное соответствие стандарту, то выкини union вообще и храни void*. Все равно ты из этого union не знаешь в принципе, какой из вариантов реализован сейчас — нет там этой информации. Значит, ты получаешь эту информацию из другого места, если тебе надо разыменовать указатель из union. Ну и храни вместо union void*, а когда надо разыменовать — касти его к int* или char* и т.д.
With best regards
Pavel Dvorkin
Re[8]: Сравнение union'ов
От: Masterkent  
Дата: 12.08.11 05:50
Оценка: +1
Pavel Dvorkin:

PD>все указатели (не на члены классов!) имеют один и тот же размер


Это стандартом не гарантируется, как и одинаковость представления.

PD>все указатели можно приводить к void*


Можно, но лишь посредством стандартного преобразования или преобразования, эквивалентного оному. Скажем, reinterpret_cast<void *&>(lvalue_pointer_to_int) — это уже хак, работоспособность которого стандартом не гарантируется. То же самое тут с union-ами.

PD>А уж если хочется точное соответствие стандарту, то выкини union вообще и храни void*. Все равно ты из этого union не знаешь в принципе, какой из вариантов реализован сейчас — нет там этой информации. Значит, ты получаешь эту информацию из другого места, если тебе надо разыменовать указатель из union. Ну и храни вместо union void*, а когда надо разыменовать — касти его к int* или char* и т.д.


+1.
Re[9]: Сравнение union'ов
От: Pavel Dvorkin Россия  
Дата: 12.08.11 05:55
Оценка: +1
Здравствуйте, Masterkent, Вы писали:

PD>>все указатели (не на члены классов!) имеют один и тот же размер


M>Это стандартом не гарантируется, как и одинаковость представления.


Не гарантируется, верно. Более того, бывают случаи, когда это неверно


union A // MS-DOS
 {
   int* far pIF;
   int* near pIN;
};


Но если речь идет о современной плоской модели — это так.
With best regards
Pavel Dvorkin
Re[3]: Сравнение union'ов
От: Centaur Россия  
Дата: 12.08.11 19:00
Оценка: 2 (1)
Здравствуйте, mp_op, Вы писали:

_>Мой вопрос в следующем, корректно ли по стандарту сравнивать

_>v1.v == v2.v

_>при условии, что изначально записаны в v1 был int*, а в v2 — char*?

Нет, некорректно.

_>Я знаю, что union_cast`ы запрещены. Но знаю, что любой указатель на объект в С++ можно сконвертировать в void* неявно. Нет ли тут лазейки?


Нет. Чтобы сконвертировать указатель, лежащий в юнионе, в void*, нужно сначала его оттуда достать. А чтобы достать из юниона, нужно знать, какой вариант активен.

_>В том и дело, что какой из вариантов активен — неизвестно. Нужно просто сравнить v1 и v2. Хочу узнать корректный способ.


Прежде чем «просто сравнивать», нужно определиться с понятием равенства. Я из всего этого объяснения так и не понял, что значит, что union { int* pi; void* pv } u1, в котором активен вариант pi, равен union { char* pch; void* pv } u2, в котором активен вариант pch. Обращаться к u1.pv или u2.pv нельзя, потому что это не активные варианты. И вообще ни к какому члену обоих юнионов нельзя обращаться, потому что мы не знаем, какой вариант активен.
Re[4]: Сравнение union'ов
От: mp_op  
Дата: 12.08.11 20:25
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Прежде чем «просто сравнивать», нужно определиться с понятием равенства.

Мне скорее нужно неравество. Реальная ситуация такая, есть два объекта одного юниона, то есть тип юниона одинаковый, но в юнионе много указателей. Какой из них активен — неизвестно. Надо просто выяснить один и тот же указатель записан в обоих объектах или нет.

Pavel Dvorkin предложил отличное решение, не использовать union вообще. Пожалуй я так и поступлю (хотя код не мой), но хотелось бы до конца разобраться.

Я знал, что записывать одно, а извлекать другое из юниона нельзя. Вы все это подтвердили. Так же опровергли моей предположение о том, что есть исключение с void*. Теперь для полного просветления мне не хватает ответа на один вопрос: Что если в юнионе лежат все указатели одного типа? Например все типа void*. В этом случае сравнивать разные элементы таких юнионов тоже нельзя?
Re: Сравнение union'ов
От: wander  
Дата: 17.08.11 18:30
Оценка: :)
Здравствуйте, mp_op, Вы писали:

_>...

Я тут стандарт почитал...

Не то, что бы я претендую на абсолютную достоверность, но похоже твою задачу можно решить. Правда практическая ценность такого решения сомнительна, и лучше в таком случае использовать void*, как тут уже посоветовали. Но я все-таки приведу его.

Сечас я приведу цитаты из стандарта, на основании которых, как мне кажется, это решение будет работать:

9.2/16
If a POD-union contains two or more POD-structs that share a common initial sequence, and if the PODunion
object currently contains one of these POD-structs, it is permitted to inspect the common initial part
of any of them.

Иными словами, если юнион содержит, например, две POD-структуры и эти структуры имеют общую начальную последовательность (common initial sequence) членов, то разрешено получать доступ к этой общей последовательности через любой мембер юниона.
Про эту возможность так же упоминается в пункте 9.5/1.

9.5/1
[Note: one special guarantee is made in order to simplify the use of unions: If a POD-union contains several
POD-structs that share a common initial sequence (9.2), and if an object of this POD-union type contains one
of the POD-structs, it is permitted to inspect the common initial sequence of any of POD-struct members; ]

Что такое common initial sequence?

Two POD-structs share a common initial sequence if corresponding members have layout-compatible
types (and, for bit-fields, the same widths) for a sequence of one or more initial members.

То есть это мемберы (последовательность) структуры, типы которых layout-compatible.
Теперь осталось выяснить как определить layoutcompatible.

If two types T1 and T2 are the same type, then T1 and T2 are layout-compatible types.

И определение layout-compatible структуры.

Two POD-struct (clause 9) types are layout-compatible if they have the same number of nonstatic data
members, and corresponding nonstatic data members (in order) have layout-compatible types (3.9).

Исходя из первого определения void* a и void* b — layout-compatible.
На основе этого достаточно просто удовлетворить второе условие, получить POD-struct c common initial sequence и layout-compatible мемберами.
template <typename T>
struct layout_compatible_ptr
{
    typedef T * data_type;
    void * data;

    operator data_type() const
    {
        return static_cast<data_type>(data);
    }
    void operator=(data_type p)
    {
        data = p;
    }
    template <typename A>
    bool operator!=(layout_compatible_ptr<A> const & x) const
    {
        return data != x.data; 
    }
};

Итак мы имеем POD-struct, которая хранит void*, но помнит настоящий тип указателя.
union my_type1
{
   layout_compatible_ptr<int>    m1;
   layout_compatible_ptr<double> m2;
   layout_compatible_ptr<char>   m3;
};

union my_type2
{
   layout_compatible_ptr<double> m1;
   layout_compatible_ptr<int>    m2;
};

my_type1 u1;
my_type2 u2;

u1.m3 = 0;
u2.m2 = 0;

(u1.m1 != u2.m1);


Теперь, согласно первой и второй цитатам, этот код должен быть полностью валиден по стандарту.
пример

PS. Думаю, если я ошибаюсь, то меня поправят.
PS2. Надеюсь тебе это поможет понять насколько это все скользко. Разве что академический интерес (как у меня)
Re[3]: Сравнение union'ов
От: Erop Россия  
Дата: 18.08.11 07:51
Оценка:
Здравствуйте, mp_op, Вы писали:

_>Мой вопрос в следующем, корректно ли по стандарту сравнивать

_>

_>v1.v == v2.v

_>при условии, что изначально записаны в v1 был int*, а в v2 — char*?
_>Равносильно ли это простому приведению к типу void*? Даже чуть больше уточню, интересует сравнение на неравенство (и только).

Некорректно. При сегментной можели памяти и софтверной аддресации к байтам можно даже словить авост.

Во-первых, нет гарантий, что бинарные представления разных типов указателей совпадают.
Например, если на 16-битной архитектуре аддресуется 65538 СЛОВ, особенно, если слова там double, например, то есть имеют размер 8 байт. То char* запросто может иметь другую структуру, чем int* или double*...

_>Я знаю, что union_cast`ы запрещены. Но знаю, что любой указатель на объект в С++ можно сконвертировать в void* неявно. Нет ли тут лазейки?

Нет. Даже, если ты конвертируешь указатель в void* неявно, компилятор всё равно желает это ЯВНО. А тут ты какие-то биты куда-то засунул, а потом решил их интерпретировать иначе. Так делать нельзя. Моэно вообще некорректный указатель получить и словить аппаратное прерывание от проца.

_>В том и дело, что какой из вариантов активен — неизвестно. Нужно просто сравнить v1 и v2. Хочу узнать корректный способ. Типы полей совпадают всегда только в одном — есть void*, все остальные могут быть произвольные указатели на объекты (только указатели).

Корректного способа нет.

Во-первых это всё UB и зависит от плаформы и реализации.
Во-вторых, если у тебя эти варианты бинарно совпадут, это не будет ничего обозначать.

Ну и главное, я не совсем понимаю, на кой вообще тут юнион?
Чего бы не хранить ВСЕГДА void* и не кастить её туда, куда надо? Например методами этой структуры...

_>Исходя из этого все мои юнионы всегда sizeof(U) == sizeof(void*), так как получается, что void* всегда самый большой член юниона (по причине конвертируемости в него всех остальных указателей).

Такой гарантии тоже нет. void* может быть как-то упакован, например.
Ну и даже если бы она была. Вот представь себе, что void* больше int*

сценарий 1:
кладём в один инион void*1 а в другой void*2
Потом присваиваем в оба один и тот же int*.
Тем не менее бинарно юнионы не совпадают.

сценарий 2:
Кладём в оба юниона один и тот же void*.
Случайно у нас находится int*, который по бираному представлению совпадает с теми битами нашего void*, которые в юнионе попадают на int*.
Кладём в один из этих двух юнионов такой int*.
Логически юнионы различны, а бинарно совпадают.

В общем бинарное сравнени юнионов не даёт никаких гарантий вообще. Ни гарантий равенства ни гарантий неравенства, ни гарантий работоспособности, кстати, тоже...

_>Немного сумбурно, но вроде сейчас больше нет недомолвок. Спасибо.

Ты бы лучше рассказал на кой тебе это вообще понадобилось? Может тебе кто что дельное туит посоветует...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: Сравнение union'ов
От: Erop Россия  
Дата: 18.08.11 07:52
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>все указатели (не на члены классов!) имеют один и тот же размер

Это неверное утверждение. Такой гарантии нет.


PD>А уж если хочется точное соответствие стандарту, то выкини union вообще и храни void*. Все равно ты из этого union не знаешь в принципе, какой из вариантов реализован сейчас — нет там этой информации. Значит, ты получаешь эту информацию из другого места, если тебе надо разыменовать указатель из union. Ну и храни вместо union void*, а когда надо разыменовать — касти его к int* или char* и т.д.


А вот эьл правильно. Если нет какого-то ещё более прямого решения, конечно
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[10]: Сравнение union'ов
От: Erop Россия  
Дата: 18.08.11 07:55
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Но если речь идет о современной плоской модели — это так.


Тоже не факт. Если ограничить "современную плоскую модель" винтелом, AMD и армом, то факт, а иначе --

Кроме того, на свете есть и интерпретаторы С/С++
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Сравнение union'ов
От: Erop Россия  
Дата: 18.08.11 08:01
Оценка:
Здравствуйте, wander, Вы писали:

W>Теперь, согласно первой и второй цитатам, этот код должен быть полностью валиден по стандарту.

W>пример

Ясен пень, что должен и будет. Но возникает вопрос, не проще ли хранить явно void*, и обойтись без юниона вообще? Ты же делаешь тоже самое, только терморектальным методом...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.