Здравствуйте, Yotce, Вы писали:
Y>Я понимаю что sizeof( Method ) != sizeof ( void * )
Y>Разве не для этого и используется union Y>приведу пример: Y>это разве не тоже самое?
То же самое. С хранением проблем нет. Проблемы есть с чтением не того, что сохранил. В общем случае нельзя записать в один член объединения, а потом прочитать из другого. Ты можешь записать в свое объединение cFunc и потом воспользоваться cFunc. Либо записать voidFunc и потом пользоваться voidFunc. Но нельзя записать cFunc, а потом воспользоваться voidFunc — а это именно то, что ты делаешь тут:
cFunc = &TestCall::test_call_method; // тут ты записал в cFunc
v_func = voidFunc; // а тут ты читаешь из voidFunc
Здравствуйте, Yotce, Вы писали:
Х>>Текст стандарта только подтверждает вышесказанное. Y>можно увидеть текст стандарта где это написано, желательно с разъяснениями почему Вы понимаете это так?
Вот у тебя есть например:
union
{
бомба,
картофелина
}
Тебе кладут туда бомбу, а ты думаешь что это картофелина и кладешь на сковороду... а по факту там бомба.
В стандарте это описано тут:
3.10/10 If a program attempts to access the stored value of an object through a glvalue of other than one of the
following types the behavior is undefined:
— the dynamic type of the object,
— a cv-qualified version of the dynamic type of the object,
— a type similar (as defined in 4.4) to the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type
of the object,
— an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic
data members (including, recursively, an element or non-static data member of a subaggregate
or contained union),
— a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
— a char or unsigned char type.
Как можно земетить, в некоторых случаях так можно делать.
Re: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
Y>Не раз наталкиваюсь на токую запись Y>... Y>Чем грозит такое приведение типов? Y>
Y> union
Y> {
Y> int ( __stdcall IObject::*Method )( int, float );
Y> void *void_func;
Y> };
Y>
Указатель на функцию-член может иметь размеры, отличные от указателя на данные со всеми вытекающими. Т.е. после преобразования туда-обратно ты не можешь рассчитывать на валидность указателя на функцию.
--
Справедливость выше закона. А человечность выше справедливости.
Re[3]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
Y>Еще раз перечитал стандарт, "По стандарту в union может храниться только один член" это да. А вот с этим не согласен "если записали один член, а считали другой, то это — неопределенное поведение."
Здравствуйте, andyp, Вы писали:
A>Здравствуйте, Yotce, Вы писали: Y>>9.5 Y>>A union of the form Y>>union { member-specification } ; Y>>is called an anonymous union; it defines an unnamed object of unnamed type. The member-specification of Y>>an anonymous union shall only define non-static data members. [ Note: Nested types and functions cannot Y>>be declared within an anonymous union. —end note ] The names of the members of an anonymous union Y>>shall be distinct from the names of any other entity in the scope in which the anonymous union is declared. Y>>For the purpose of name lookup, after the anonymous union definition, the members of the anonymous union Y>>are considered to have been defined in the scope in which the anonymous union is declared. [ Example: Y>>void f() { Y>>union { int a; const char* p; }; Y>>a = 1; Y>>p = "Jennifer"; Y>>} Y>> Y>>Here a and p are used like ordinary (nonmember) variables, but since they are union members they have Y>>the same address. —end example ] Y>>
Y>>Тут как раз и говорится что адреса ( *a == *p )
A>Пример иллюстрирует видимость имен полей, определенных в anonymous union, адреса равны в том смысле, что a и р имеют общую память, но почему Вы делаете вывод, что допустимо записать в один член, а считать из другого, если стандарт практически явно утверждает обратное немного выше по тексту?
A>ps сравнение указателей разных типов в общем случае не имеет смысла, так как компилятор волен хранить их по-разному
Можно поподробнее где именно это утверждается?
Re[8]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, andyp, Вы писали:
A>Здравствуйте, Yotce, Вы писали:
Y>>тут говорится о том что если
Y>>
Y>>void f()
Y>>{
Y>> union
Y>> {
Y>> int a;
Y>> const char* p;
Y>> };
Y>> a = 1;
Y>> p = "Jennifer";
Y>> /// то a уже не можем быть равна 1
Y>>}
Y>>
A>Чему же в этом случае будет равно a? Что будет, если прочитать а? (hint — будет неопределенное поведение, так как a уже фактически нет). Прочитайте, чуть подальше (9.5.4)- там для того, чтобы переключить активный член union в общем случае требуют вызвать деструктор для другого, проинициализированного до этого, члена. Что же будет, если скопировать в переменную другую, для которой же вызван деструктор?
это примечание к пункту 9.5.3
Я понимаю это так:
если у нас есть union
union
{
int a;
std::string str;
MyClass m_class;
}
<- тут вызывается конструктор std::string перед str.append( "test" );
str.append( "test" );
то при записи должен быть вызван конструктор std::string или другого класса, это и понятно если это бы не делалось чем бы был тогда str даже представить не могу...
наверно тоже самое что если записать что-то на вроде этого
union
{
int a;
std::string str;
MyClass m_class;
}
<- тут вызывается конструктор std::string перед str.append( "test" );
str.append( "test" );
<- тут вызывается деструктор std::string
a = 1;
если-бы не происходил вызов деструктора была-бы банальная утечка памяти...
struct MyClass
{
char *buf;
MyClass() { buf = new char[ 1000 ]; }
~MyClass() { delete [] buf; }
void test( char *str, int len ) { memcpy( buf, str, len ); }
};
union
{
int a;
std::string str;
MyClass m_class;
}
<- тут вызывается конструктор MyClass
m_class.test( "test\0", strlen("test\0") );
<- тут вызывается деструктор MyClass
a = 1;
Здравствуйте, Yotce, Вы писали:
Y>Чем грозит такое приведение типов?
union
{
int ( __stdcall IObject::*Method )( int, float );
void *void_func;
};
Такое приведение грозит тем, что в void_func может оказаться все, что угодно: указатель на исполняемый код, указатель на какую-то внутреннюю таблицу компилятора, вообще какой-нибудь оффсет. Подробнее здесь.
В приведенном вами коде, правда, это несущественно: никто напрямую void_func не вызывает. Но зато этот код закладывается на то, что размеры указателя на функцию-член класса и простого указателя на void одинаковы. В реальности они могут различаться, и тогда при вызове метода через "обрезанный" указатель наступит трындец (см. здесь).
Re[2]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Хреннос, Вы писали:
Х>Здравствуйте, Yotce, Вы писали:
Y>>Чем грозит такое приведение типов? Х>
Х> union
Х> {
Х> int ( __stdcall IObject::*Method )( int, float );
Х> void *void_func;
Х> };
Х>
Х>Такое приведение грозит тем, что в void_func может оказаться все, что угодно: указатель на исполняемый код, указатель на какую-то внутреннюю таблицу компилятора, вообще какой-нибудь оффсет. Подробнее здесь.
Х>В приведенном вами коде, правда, это несущественно: никто напрямую void_func не вызывает. Но зато этот код закладывается на то, что размеры указателя на функцию-член класса и простого указателя на void одинаковы. В реальности они могут различаться, и тогда при вызове метода через "обрезанный" указатель наступит трындец (см. здесь).
Я понимаю что sizeof( Method ) != sizeof ( void * )
Разве не для этого и используется union
приведу пример:
char *test = "test\0";
union
{
char **buf; /// это int ( __stdcall IObject::*Method )( int, float ); void *addr; /// void *void_func;
};
buf = &test;
это разве не тоже самое?
Re: union { int (Foo::*cFunc ) (); void *void_func;}
По стандарту в union может храниться только один член. Т.е. если записали один член, а считали другой, то это — неопределенное поведение. Исключение только для структур с одинаковыми полями в начале, на сколько помню.
Re[3]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
Y>Я понимаю что sizeof( Method ) != sizeof ( void * )
Y>Разве не для этого и используется union
Дело в том, что в вашем коде два юниона, а данные между ними передаются копированием мЕньшей части.
Память в юнионе будет распределена примерно так:
[sizeof(void*)] — общая часть на оба указателя
[sizeof(ptr_to_member_function) — sizeof(void*)] — остаток, в котором лежат данные указателя на функцию-член.
Если копировать данные только через обычный указатель, то часть юниона останется неинициализированной.
...
cFunc = ( &TestCall::test_call_method ); // пишем в union::cFunc указатель на функцию-член - ок, она большая.
v_func = voidFunc; // а вот здесь мы копируем только часть этого указателя в указатель на воид.
...
for ( int i = 0; i < 10000; i++ )
{
...
void_func = v_func; // копируем неполный указатель в другой union...int ret = ( ( test_call_obj )->* ( Method ) ) ( i, 7.999f ); // ... и пытаемся использовать указатель на функцию-член.
// В этом месте часть указателя в поле Method будет содержать неинициализированные данные.
...
}
Y>приведу пример:
Y>
Y> char *test = "test\0";
Y> union
Y> {
Y> char **buf; /// это НЕ МОЖЕТ быть int ( __stdcall IObject::*Method )( int, float );
Y> void *addr; /// void *void_func;
Y> };
Y> buf = &test;
Y>
Y>это разве не тоже самое?
Нет, это юнион, в котором лежат два обычных указателя. Указатель на нестатическую функцию-член класса — это особая статья, его нельзя заменить простым указателем на данные или на функцию.
Re: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
Y>Чем грозит такое приведение типов?
Например, в зависимости от заданного уровня оптимизации, твой пример, скомпилированный clang, то падает с SIGSEGV, то делает вид что работает. Как раз демонстрация того, как неопределённого поведение может некоторое время не проявлять себя, но привести к ошибке даже при малейшем изменении окружения. Ну а в чём тут именно заключается неопределённого поведение тебе уже раньше ответили.
Re[2]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, andyp, Вы писали:
A>По стандарту в union может храниться только один член. Т.е. если записали один член, а считали другой, то это — неопределенное поведение. Исключение только для структур с одинаковыми полями в начале, на сколько помню.
Еще раз перечитал стандарт, "По стандарту в union может храниться только один член" это да. А вот с этим не согласен "если записали один член, а считали другой, то это — неопределенное поведение."
Document Number: N3242=11-0012
Date: 2011-02-28
Revises: N3225
Reply to: Pete Becker
Roundhouse Consulting, Ltd.
pete@versatilecoding.com
9.5
A union of the form
union { member-specification } ;
is called an anonymous union; it defines an unnamed object of unnamed type. The member-specification of
an anonymous union shall only define non-static data members. [ Note: Nested types and functions cannot
be declared within an anonymous union. —end note ] The names of the members of an anonymous union
shall be distinct from the names of any other entity in the scope in which the anonymous union is declared.
For the purpose of name lookup, after the anonymous union definition, the members of the anonymous union
are considered to have been defined in the scope in which the anonymous union is declared. [ Example:
void f() {
union { int a; const char* p; };
a = 1;
p = "Jennifer";
}
Here a and p are used like ordinary (nonmember) variables, but since they are union members they have
the same address. —end example ]
Тут как раз и говорится что адреса ( *a == *p )
Re[3]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали: Y>9.5 Y>A union of the form Y>union { member-specification } ; Y>is called an anonymous union; it defines an unnamed object of unnamed type. The member-specification of Y>an anonymous union shall only define non-static data members. [ Note: Nested types and functions cannot Y>be declared within an anonymous union. —end note ] The names of the members of an anonymous union Y>shall be distinct from the names of any other entity in the scope in which the anonymous union is declared. Y>For the purpose of name lookup, after the anonymous union definition, the members of the anonymous union Y>are considered to have been defined in the scope in which the anonymous union is declared. [ Example: Y>void f() { Y>union { int a; const char* p; }; Y>a = 1; Y>p = "Jennifer"; Y>} Y> Y>Here a and p are used like ordinary (nonmember) variables, but since they are union members they have Y>the same address. —end example ] Y>
Y>Тут как раз и говорится что адреса ( *a == *p )
Пример иллюстрирует видимость имен полей, определенных в anonymous union, адреса равны в том смысле, что a и р имеют общую память, но почему Вы делаете вывод, что допустимо записать в один член, а считать из другого, если стандарт практически явно утверждает обратное немного выше по тексту?
ps сравнение указателей разных типов в общем случае не имеет смысла, так как компилятор волен хранить их по-разному
Re[4]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, jazzer, Вы писали:
J>Здравствуйте, Yotce, Вы писали:
Y>>Еще раз перечитал стандарт, "По стандарту в union может храниться только один член" это да. А вот с этим не согласен "если записали один член, а считали другой, то это — неопределенное поведение."
J>
Здравствуйте, Yotce, Вы писали:
Y>Можно поподробнее где именно это утверждается?
9.5 Unions [class.union]
1 In a union, at most one of the non-static data members can be active at any time, that is, the value of at
most one of the non-static data members can be stored in a union at any time.
Re[5]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
A>>ps сравнение указателей разных типов в общем случае не имеет смысла, так как компилятор волен хранить их по-разному
Y>Можно поподробнее где именно это утверждается?
Стандарт работает наоборот
Все, что не запрещено, разрешено.
Если явно не запрещено хранить по-разному — значит, разрешено.
Здравствуйте, andyp, Вы писали:
A>Здравствуйте, Yotce, Вы писали:
Y>>Можно поподробнее где именно это утверждается?
A>9.5 Unions [class.union] A>1 In a union, at most one of the non-static data members can be active at any time, that is, the value of at A>most one of the non-static data members can be stored in a union at any time.
тут говорится о том что если
void f()
{
union
{
int a;
const char* p;
};
a = 1;
p = "Jennifer";
/// то a уже не можем быть равна 1char buf[100];
memcpy( &a, p, strlen("Jennifer\0") )
///*buf == *p;
}
Re[7]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
Y>тут говорится о том что если
Y>
Y>void f()
Y>{
Y> union
Y> {
Y> int a;
Y> const char* p;
Y> };
Y> a = 1;
Y> p = "Jennifer";
Y> /// то a уже не можем быть равна 1
Y>}
Y>
Чему же в этом случае будет равно a? Что будет, если прочитать а? (hint — будет неопределенное поведение, так как a уже фактически нет). Прочитайте, чуть подальше (9.5.4)- там для того, чтобы переключить активный член union в общем случае требуют вызвать деструктор для другого, проинициализированного до этого, члена. Что же будет, если скопировать в переменную другую, для которой же вызван деструктор?
Re[7]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
Y>Здравствуйте, andyp, Вы писали:
A>>Здравствуйте, Yotce, Вы писали:
Y>>>Можно поподробнее где именно это утверждается?
A>>9.5 Unions [class.union] A>>1 In a union, at most one of the non-static data members can be active at any time, that is, the value of at A>>most one of the non-static data members can be stored in a union at any time.
Y>тут говорится о том что если
Y>
Y>void f()
Y>{
Y> union
Y> {
Y> int a;
Y> const char* p;
Y> };
Y> a = 1;
Y> p = "Jennifer";
Y> /// то a уже не можем быть равна 1
Y> char buf[100];
Y> memcpy( &a, buf, strlen("Jennifer\0") ) /// в предыдущем посте тут ошибочку зделал
Y> ///*buf == *p;
Y>}
Y>
Re: union { int (Foo::*cFunc ) (); void *void_func;}
Y>>void f()
Y>>{
Y>> union
Y>> {
Y>> int a;
Y>> const char* p;
Y>> };
Y>> a = 1;
Y>> p = "Jennifer";
Y>> /// то a уже не можем быть равна 1
Y>> char buf[100];
Y>> memcpy( &a, buf, strlen("Jennifer\0") ) /// в предыдущем посте тут ошибочку зделал
Y>> ///*buf == *p;
Y>>}
Y>>
"ошибочку зделал", но так и не исправил полностью
Но вобщем понятно, что хотел показать. Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1. Даже если сейчас всё работает, то с обновлением компилятора может внезапно прострелить тебе ногу.
Re: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, rusted, Вы писали:
R> ... R>"ошибочку зделал", но так и не исправил полностью R>Но вобщем понятно, что хотел показать. Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1. Даже если сейчас всё работает, то с обновлением компилятора может внезапно прострелить тебе ногу.
R> Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1.
Быть всё таки такого не может два объекта в нашем мире не могу занимать одно и тоже пространство.
По стандарту адрес a и адрес *p равны
R> Даже если сейчас всё работает, то с обновлением компилятора может внезапно прострелить тебе ногу.
Меня и интересует это, в каких случаях это будет работать в каких нет И правильно ли это по стандарту ?
Re[10]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
R>> Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1. Y>Быть всё таки такого не может два объекта в нашем мире не могу занимать одно и тоже пространство.
Сказано же — с оптимизацией. Разработчики компилятора могли прочитать стандарт буквально, и компилятор может подразумевать, что переменные a и p не связаны логически.
Оптимизация может, например, поместить копию значения из а в регистр процессора, чтобы два раза и памяти не читать.
Y>По стандарту адрес a и адрес *p равны
По стандарту равны адреса членов юниона — т.е. в вашем случае это a и p.
В вашем "ошибочку зделал" вы переписываете строку "Jennifer\0" прямо поверх содержимого юниона (а не по адресу, хранящемуся в а, как вам, очевидно, хотелось бы). При этом вы практически гарантированно выходите за его границу и перетираете данные, расположенные далее в стеке (длина строки Jennifer — 8 байт + терминальный нуль, что как минимум на 1 байт больше, чем размер пойнтера в 64-битной машине).
Кстати, у строковых констант терминальный нуль указывать не нужно, он автоматически вставляется компилятором.
R>> Даже если сейчас всё работает, то с обновлением компилятора может внезапно прострелить тебе ногу. Y>Меня и интересует это, в каких случаях это будет работать в каких нет И правильно ли это по стандарту ?
По стандарту это правильно.
В каких случаях это будет работать? Зависит от того, что именно вы подразумеваете под словами "это" и "работать".
Если "это" означает, что вы пишете в один из членов юниона, а потом читаете из другого — это может работать, а может и нет. Зависит от того, какой именно результат вы хотите получить и как именно вы хотите его использовать.
В качестве примера работающего кода, использующего юнионы, могу предложить такую вот функцию:
bool IsLittleEndianCPU()
{
union {
int a;
char b[sizeof(int)];
} u;
u.a = 1;
return u.b[0] != 0;
}
Этот код работает, и даже почти безопасен (с оговоркой про гипотетические оптимизации компилятора).
Жопа этого кода в том, что он бесполезен и не нужен в реальной жизни: порядок байт в слове известен еще в процессе компиляции, и нет никакого смысла его определять в рантайме.
Но здесь, заметим, работа идет исключительно с целыми числами. Работать так с пойнтерами небезопасно, а работать так с пойнтерами на функции-члены класса (да еще и не совсем понимая, что такое пойнтер и какие они бывают) — прямой путь к глюкам.
Re[11]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Хреннос, Вы писали:
Х>Здравствуйте, Yotce, Вы писали:
R>>> Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1. Y>>Быть всё таки такого не может два объекта в нашем мире не могу занимать одно и тоже пространство.
Х>Сказано же — с оптимизацией. Разработчики компилятора могли прочитать стандарт буквально, и компилятор может подразумевать, что переменные a и p не связаны логически.
Да они могут быть не связаны логически они связаны одним адресом
Х>Оптимизация может, например, поместить копию значения из а в регистр процессора, чтобы два раза и памяти не читать.
Y>>По стандарту адрес a и адрес *p равны
Х>По стандарту равны адреса членов юниона — т.е. в вашем случае это a и p.
да согласен.
Х>В вашем "ошибочку зделал" вы переписываете строку "Jennifer\0" прямо поверх содержимого юниона (а не по адресу, хранящемуся в а, как вам, очевидно, хотелось бы). При этом вы практически гарантированно выходите за его границу и перетираете данные, расположенные далее в стеке (длина строки Jennifer — 8 байт + терминальный нуль, что как минимум на 1 байт больше, чем размер пойнтера в 64-битной машине). Х>Кстати, у строковых констант терминальный нуль указывать не нужно, он автоматически вставляется компилятором.
Кто — то ошибку заметил Rusted, улыбнулся и понял что хотели сказать...
Х>По стандарту это правильно. Х>В каких случаях это будет работать? Зависит от того, что именно вы подразумеваете под словами "это" и "работать". Х>Если "это" означает, что вы пишете в один из членов юниона, а потом читаете из другого — это может работать, а может и нет. Зависит от того, какой именно результат вы хотите получить и как именно вы хотите его использовать.
Х>В качестве примера работающего кода, использующего юнионы, могу предложить такую вот функцию:
Х>
Х>Этот код работает, и даже почти безопасен (с оговоркой про гипотетические оптимизации компилятора). Х>Жопа этого кода в том, что он бесполезен и не нужен в реальной жизни: порядок байт в слове известен еще в процессе компиляции, и нет никакого смысла его определять в рантайме.
Причем здесь попугаи ой порядок следования байт?
Х>Но здесь, заметим, работа идет исключительно с целыми числами. Работать так с пойнтерами небезопасно, а работать так с пойнтерами на функции-члены класса (да еще и не совсем понимая, что такое пойнтер и какие они бывают) — прямой путь к глюкам.
Здравствуйте, Yotce, Вы писали:
Y>Да они могут быть не связаны логически они связаны одним адресом
Стандарт, вроде бы, ясно дает понять: на связь "одним адресом" в юнионах закладываться не стоит.
Y>Кто — то ошибку заметил Rusted, улыбнулся и понял что хотели сказать...
Я тоже заметил и тоже понял.
Но это, к сожалению, не просто опечатка, т.к. ровно ту же ошибку вы воспроизвели в "исправленном" варианте.
Кроме того, из ваших пояснений к коду видно, что ваше понимание пойнтеров и работы с ними, скажем так, не отличается кристальной ясностью (путаете адрес переменной и ее содержимое, не видите проблемы в том, что размеры указателей разных типов могут отличаться, спокойно используете int в качестве контейнера для указателя). Именно поэтому я не прошел мимо, как Rusted, а попытался объяснить подробнее.
Y>Причем здесь попугаи ой порядок следования байт?
Это была иллюстрация "почти легального" использования юнионов для получения машинно-зависимой информации.
Х>>Но здесь, заметим, работа идет исключительно с целыми числами. Работать так с пойнтерами небезопасно, а работать так с пойнтерами на функции-члены класса (да еще и не совсем понимая, что такое пойнтер и какие они бывают) — прямой путь к глюкам.
Y>А где написано что так делать нельзя?
"Делать так" — это, возвращаясь к вашему первому посту, использовать обычный указатель, совмещенный с областью хранения указателя на нестатическую функцию-член класса, для чтения и модификации этого указателя на нестатическую функцию-член класса.
В приведенных мной ссылках явно написано, что эти два типа указателей несовместимы между собой, и то, что вы хотите, в общем случае работать не будет (в частных — может и сработать, если повезет).
Текст стандарта только подтверждает вышесказанное.
Y>здесь
Нельзя ли вкратце пояснить, что сей код иллюстрирует, и каким именно образом?
Re[13]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Хреннос, Вы писали:
Х>Здравствуйте, Yotce, Вы писали:
Y>>Да они могут быть не связаны логически они связаны одним адресом
Х>Стандарт, вроде бы, ясно дает понять: на связь "одним адресом" в юнионах закладываться не стоит.
Можно конкретные где это написано в стандарте?
Y>>Кто — то ошибку заметил Rusted, улыбнулся и понял что хотели сказать...
Х>Я тоже заметил и тоже понял. Х>Но это, к сожалению, не просто опечатка, т.к. ровно ту же ошибку вы воспроизвели в "исправленном" варианте. Х>Кроме того, из ваших пояснений к коду видно, что ваше понимание пойнтеров и работы с ними, скажем так, не отличается кристальной ясностью (путаете адрес переменной и ее содержимое, не видите проблемы в том, что размеры указателей разных типов могут отличаться, спокойно используете int в качестве контейнера для указателя). Именно поэтому я не прошел мимо, как Rusted, а попытался объяснить подробнее.
Не будем разводить демагогию.
Y>>Причем здесь попугаи ой порядок следования байт?
Х>Это была иллюстрация "почти легального" использования юнионов для получения машинно-зависимой информации.
В моём понимании машина не знает да ей и все равно как компилятор представляет указатель на метод класса.
Х>>>Но здесь, заметим, работа идет исключительно с целыми числами. Работать так с пойнтерами небезопасно, а работать так с пойнтерами на функции-члены класса (да еще и не совсем понимая, что такое пойнтер и какие они бывают) — прямой путь к глюкам.
Y>>А где написано что так делать нельзя?
Х>"Делать так" — это, возвращаясь к вашему первому посту, использовать обычный указатель, совмещенный с областью хранения указателя на нестатическую функцию-член класса, для чтения и модификации этого указателя на нестатическую функцию-член класса.
Я НЕ где НЕ утверждал что это правильно.
union
{ int ( __stdcall IObject::*Method )( int, float );
void *void_func;
};
Я видел такое преобразование в библиотеки "Rpc — чего там" к сожалению не помню её названия, щас её найти не могу.
Вот и возник вопрос о том на какие грабли можно наступить если использовать такую запись. То что sizeof ( void_func ) != sizeof ( Methos ) и так понятно. Если-бы этого не знал то и вопроса не было-бы...
Но почему Вы говорите что нельзя скопировать?
Х>В приведенных мной ссылках явно написано, что эти два типа указателей несовместимы между собой, и то, что вы хотите, в общем случае работать не будет (в частных — может и сработать, если повезет).
Вы говорите, что Х>Текст стандарта только подтверждает вышесказанное.
можно увидеть текст стандарта где это написано, желательно с разъяснениями почему Вы понимаете это так?
Y>>здесь
Х>Нельзя ли вкратце пояснить, что сей код иллюстрирует, и каким именно образом?
В моем понимании этот код иллюстрирует сохранение указателя на функцию-член класса
Re[14]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
Х>>Стандарт, вроде бы, ясно дает понять: на связь "одним адресом" в юнионах закладываться не стоит.
Y>Можно конкретные где это написано в стандарте?
9.5 Unions [class.union]
1 In a union, at most one of the non-static data members can be active at any time, that is, the value of at
most one of the non-static data members can be stored in a union at any time.
Сие означает буквально следующее: писать в один дата мембер, а потом читать эти данные из другого — неопределенное поведение.
Y>>>Причем здесь попугаи ой порядок следования байт?
Х>>Это была иллюстрация "почти легального" использования юнионов для получения машинно-зависимой информации.
Y>В моём понимании машина не знает да ей и все равно как компилятор представляет указатель на метод класса.
К чему это?
В приведенном мной примере нет ни одного указателя.
Y>Я НЕ где НЕ утверждал что это правильно. Y>
Y> union
Y> { int ( __stdcall IObject::*Method )( int, float );
Y> void *void_func;
Y> };
Y>
Y>Я видел такое преобразование в библиотеки "Rpc — чего там" к сожалению не помню её названия, щас её найти не могу. Y>Вот и возник вопрос о том на какие грабли можно наступить если использовать такую запись. То что sizeof ( void_func ) != sizeof ( Methos ) и так понятно. Если-бы этого не знал то и вопроса не было-бы...
В таком случае ответ для вас должен быть очевиден, и я не понимаю, зачем вы здесь разводите дискуссию в стиле "поднимите мне веки и процитируйте стандарт".
Грабли — как обычно:
1. На одном компиляторе этот код работает, при попытке перейти на другой компилятор/ОС/архитектуру процессора он может привести к глюкам программы.
2. Даже если код заработает под другим компилятором в тестовом примере, не факт, что в реальном большом проекте он не вызовет появление странных глюков.
В принципе, этого достаточно, чтобы закрыть дискуссию о том, имеет ли смысл использовать такие трюки с юнионами в рабочем коде.
Что касается кода библиотеки — там свои причуды. Она, скорее всего, разрабатывалась под конкретный компилятор и ОС, поэтому авторы не сильно пеклись о переносимости кода и о соблюдении стандартов. Работает — и ладно.
Y>Но почему Вы говорите что нельзя скопировать?
Потому что в приведенном вами коде копирование происходит потенциально неполное: копируется только часть "большого" указателя (то, что поместилось в обычный).
Х>>В приведенных мной ссылках явно написано, что эти два типа указателей несовместимы между собой, и то, что вы хотите, в общем случае работать не будет (в частных — может и сработать, если повезет).
Y>Вы говорите, что Х>>Текст стандарта только подтверждает вышесказанное. Y>можно увидеть текст стандарта где это написано, желательно с разъяснениями почему Вы понимаете это так?
О, мне не трудно, я повторю еще раз:
9.5 Unions [class.union]
1 In a union, at most one of the non-static data members can be active at any time, that is, the value of at
most one of the non-static data members can be stored in a union at any time.
Для меня сие означает буквально следующее: писать в один дата мембер, а потом читать эти данные из другого — неопределенное поведение.
Y>>>здесь
Х>>Нельзя ли вкратце пояснить, что сей код иллюстрирует, и каким именно образом? Y> В моем понимании этот код иллюстрирует сохранение указателя на функцию-член класса
Искал там слово union, не нашел.
Re[15]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Хреннос, Вы писали:
Х>Здравствуйте, Yotce, Вы писали:
Х>>>Стандарт, вроде бы, ясно дает понять: на связь "одним адресом" в юнионах закладываться не стоит.
Y>>Можно конкретные где это написано в стандарте?
Х>Вам уже писали
Х>9.5 Unions [class.union]
Х>1 In a union, at most one of the non-static data members can be active at any time, that is, the value of at
Х>most one of the non-static data members can be stored in a union at any time.
Х>Сие означает буквально следующее: писать в один дата мембер, а потом читать эти данные из другого — неопределенное поведение.
Для меня сие означает, В объединение, не более одного из не-статические члены данных может быть активен в любое время, то есть,
в любое время может хранить не более одного из не-статических членов данных.
Я не претендую что это дословный перевод, но тут негде не говорится что писать в один дата мембер, а потом читать эти данные из другого — неопределенное поведение.
В моём понимании тут говорят про то что если
union __u_t
{
static int s_a; /// static memberint a; /// non-static memberchar c; /// non-static member
} obj;
int __u_t::s_a = 10;
obj.a= 100;
obj.c='c';
/// И для меня сие означает что нельзя рассчитывать на то что a=100;
Здравствуйте, Yotce, Вы писали:
Y>Для меня сие означает, В объединение, не более одного из не-статические члены данных может быть активен в любое время, то есть, Y>в любое время может хранить не более одного из не-статических членов данных.
"Активен" подразумевает доступ к данным, а не просто их пассивное хранение. Впрочем, это несущественная тонкость.
По сути, запись данных через один мембер, а чтение через другой, эквивалентно приведению "сырого" указателя на данные к целевому типу с последующим разыменованием.
Т.е. примерно так:
#include <iostream>
using namespace std;
int main()
{
union {
float a;
int b;
} u;
u.a = 100.0;
cout << "u.b=" << u.b << endl;
// эквивалентный метод доступа - через касты по сырому указателю
cout << "casts=" << *reinterpret_cast<int*>(reinterpret_cast<void*>(&u.a)) << endl;
}
Вывод:
u.b=1120403456
casts=1120403456
Тут надо обратить внимание на то, что данные берутся "сырые" — т.е. компилятор не знает ничего об этих данных, и берет их "как есть". В некоторых случаях это приводит к неопределенному поведению (где конкретно — говорит стандарт
).
Например, при работе с указателями вовнутрь объекта с множественным наследованием, компилятор иногда пользуется своим знанием об объекте-потомке. При этом компилятор может подправить указатель так, чтобы он смотрел на нужную часть объекта. Если лезть к указателю через поле в юнионе, никаких аджастментов не будет. Пример правки указателей компилятором — тут.
И кстати, если в юнионе хранится нетривиальный объект (с конструктором), то никаких автоматических вызовов конструктора-деструктора при первом доступе к полю компилятор не сгенерирует (я это к содержимому вот этого вашего поста
). Надеюсь, вы в том посте пошутили, и в голове у вас все-таки не такой беспросветный мрак.
В юнионе нельзя хранить нетривиальные объекты (non-POD). Точнее, в стандарте С++11 кое-какие поблажки сделали, но несущественные (никаких автоматических вызовов конструктора нет и не будет).
Re[15]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Fuz, Вы писали:
Fuz>Здравствуйте, Yotce, Вы писали:
Х>>>Текст стандарта только подтверждает вышесказанное. Y>>можно увидеть текст стандарта где это написано, желательно с разъяснениями почему Вы понимаете это так?
Fuz>Вот у тебя есть например: Fuz>
Fuz>Тебе кладут туда бомбу, а ты думаешь что это картофелина и кладешь на сковороду... а по факту там бомба.
В том-то и дело туда с начало положили бомбу потом её заменили на картофель, почему там должна быть бомба?
Fuz>В стандарте это описано тут:
Fuz>
3.10/10
Fuz>If a program attempts to access the stored value of an object through a glvalue of other than one of the
Fuz>following types the behavior is undefined:
Fuz> — the dynamic type of the object,
Fuz> — a cv-qualified version of the dynamic type of the object,
Fuz> — a type similar (as defined in 4.4) to the dynamic type of the object,
Fuz> — a type that is the signed or unsigned type corresponding to the dynamic type of the object,
Fuz> — a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type
Fuz> of the object,
Fuz> — an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic
Fuz> data members (including, recursively, an element or non-static data member of a subaggregate
Fuz> or contained union),
Fuz> — a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
Fuz> — a char or unsigned char type.
Не понял причём тут это?
Re[15]: union { int (Foo::*cFunc ) (); void *void_func;}
Здравствуйте, Yotce, Вы писали:
Y>Здравствуйте, Fuz, Вы писали:
Fuz>>Здравствуйте, Yotce, Вы писали:
Х>>>>Текст стандарта только подтверждает вышесказанное. Y>>>можно увидеть текст стандарта где это написано, желательно с разъяснениями почему Вы понимаете это так?
Fuz>>Вот у тебя есть например: Fuz>>
Fuz>>Тебе кладут туда бомбу, а ты думаешь что это картофелина и кладешь на сковороду... а по факту там бомба.
Y>В том-то и дело туда с начало положили бомбу потом её заменили на картофель, почему там должна быть бомба?
Я про этот случай (здесь замены нет):
union {
float a;
int b;
} u;
u.a = 100.0; // положили бомбуint v = u.b; // ... а достаем как картошку. Но по факту там бомба
'u.b' — это (в нашем случае) initializer expression, которое представляет собой lvalue типа 'int' (5.2.5/3), но объект, на который оно ссылается('u.a') имеет тип 'float' (в union ведь может только один объект храниться)
При выполнении value initialization будет выполнено lvalue-to-rvalue преобразование. Здесь появится UB, т.к. типы не соответствуют (ожидается 'int', а на входе 'float').
Re[17]: union { int (Foo::*cFunc ) (); void *void_func;}
Если Кто-то утверждает что трава "зелёная-зелёная" кто-то поверит на слова, а кто-то попытается разобраться в этом. И выяснят что трава "зелёная-зеленая" при определённых обстоятельствах, а ещё бывает "зеленая"... .
Re[16]: union { int (Foo::*cFunc ) (); void *void_func;}