union { int (Foo::*cFunc ) (); void *void_func;}
От: Yotce  
Дата: 04.02.14 12:12
Оценка:
Не раз наталкиваюсь на токую запись

class IObject
{ 
public :
           IObject ( void ) {};
virtual ~IObject ( void ) {};
};

class TestCall :
    public IObject
    
{

private :
    int     m_i;
    float   m_f;

public :

             TestCall ( void ) { m_i = 999; m_f = 1.7777f; }
    virtual ~TestCall ( void ) {}

public :

    int __stdcall test_call_method ( int i, float f )
    {
        this->m_i = i;
        this->m_f = f;

        return m_i;
    }

};

///
int main ( int, char *[] )
{
    TestCall          test_call;
    IObject    *test_call_obj = &test_call;
    void             *v_func        = NULL;

    union
    {
        int ( __stdcall TestCall::*cFunc ) ( int, float );
        void *voidFunc;
    }; cFunc = ( &TestCall::test_call_method );
    v_func = voidFunc;


    for ( int i = 0; i < 10000; i++ )
    {
        union 
        { 
            int ( __stdcall IObject::*Method )( int, float );  
            void  *void_func; 
        }; void_func = v_func;
        int ret = ( ( test_call_obj )->* ( Method ) ) ( i, 7.999f );
        std::cout << " call method \"test_call_method\" ret = "  << ret << std::endl;
    }

    return 0;
}


Чем грозит такое приведение типов?
        union 
        { 
            int ( __stdcall IObject::*Method )( int, float );  
            void  *void_func; 
        };
Re: union { int (Foo::*cFunc ) (); void *void_func;}
От: rg45 СССР  
Дата: 04.02.14 12:59
Оценка: +1
Здравствуйте, Yotce, Вы писали:

Y>Не раз наталкиваюсь на токую запись

Y>...
Y>Чем грозит такое приведение типов?
Y>
Y>        union 
Y>        { 
Y>            int ( __stdcall IObject::*Method )( int, float );  
Y>            void  *void_func; 
Y>        };
Y>


Указатель на функцию-член может иметь размеры, отличные от указателя на данные со всеми вытекающими. Т.е. после преобразования туда-обратно ты не можешь рассчитывать на валидность указателя на функцию.
--
Справедливость выше закона. А человечность выше справедливости.
Re: union { int (Foo::*cFunc ) (); void *void_func;}
От: Хреннос  
Дата: 04.02.14 13:04
Оценка:
Здравствуйте, 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  
Дата: 04.02.14 13:49
Оценка:
Здравствуйте, Хреннос, Вы писали:

Х>Здравствуйте, 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;}
От: andyp  
Дата: 04.02.14 14:53
Оценка:
По стандарту в union может храниться только один член. Т.е. если записали один член, а считали другой, то это — неопределенное поведение. Исключение только для структур с одинаковыми полями в начале, на сколько помню.
Re[3]: union { int (Foo::*cFunc ) (); void *void_func;}
От: jazzer Россия Skype: enerjazzer
Дата: 04.02.14 15:04
Оценка: +3
Здравствуйте, 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
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[3]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Хреннос  
Дата: 04.02.14 15:09
Оценка:
Здравствуйте, 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;}
От: watchmaker  
Дата: 04.02.14 15:22
Оценка:
Здравствуйте, Yotce, Вы писали:

Y>Чем грозит такое приведение типов?


Например, в зависимости от заданного уровня оптимизации, твой пример, скомпилированный clang, то падает с SIGSEGV, то делает вид что работает. Как раз демонстрация того, как неопределённого поведение может некоторое время не проявлять себя, но привести к ошибке даже при малейшем изменении окружения. Ну а в чём тут именно заключается неопределённого поведение тебе уже раньше ответили.
Re[2]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Yotce  
Дата: 04.02.14 15:38
Оценка:
Здравствуйте, 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;}
От: jazzer Россия Skype: enerjazzer
Дата: 04.02.14 15:55
Оценка: +1
Здравствуйте, Yotce, Вы писали:

Y>Еще раз перечитал стандарт, "По стандарту в union может храниться только один член" это да. А вот с этим не согласен "если записали один член, а считали другой, то это — неопределенное поведение."


Y>void f() {
Y>union { int a; const char* p; };
Y>a = 1;
Y>p = "Jennifer";
Y>}


И где ты в этом примере видишь запеись в один член, а чтение из другого?

Y>Тут как раз и говорится что адреса ( *a == *p )


Да. И?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[3]: union { int (Foo::*cFunc ) (); void *void_func;}
От: andyp  
Дата: 04.02.14 15:58
Оценка:
Здравствуйте, 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;}
От: Yotce  
Дата: 04.02.14 17:16
Оценка:
Здравствуйте, jazzer, Вы писали:

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


Y>>Еще раз перечитал стандарт, "По стандарту в union может храниться только один член" это да. А вот с этим не согласен "если записали один член, а считали другой, то это — неопределенное поведение."


J>

Y>>void f() {
Y>>union { int a; const char* p; };
Y>>a = 1;
Y>>p = "Jennifer";
Y>>}


J>И где ты в этом примере видишь запеись в один член, а чтение из другого?


Y>>Тут как раз и говорится что адреса ( *a == *p )


J>Да. И?


Помоем вполне логично что если ( addr a == addr p ) то

void f() 
{
    union { int a; const char* p; };
    a = 1;
    p = "Jennifer\0";
    
    char buf[100];
    memcpy( &a, p, strlen("Jennifer\0") )

   *buf = *p;
}
Re[4]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Yotce  
Дата: 04.02.14 17:26
Оценка: :)
Здравствуйте, 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[5]: union { int (Foo::*cFunc ) (); void *void_func;}
От: andyp  
Дата: 04.02.14 17:33
Оценка:
Здравствуйте, 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;}
От: jazzer Россия Skype: enerjazzer
Дата: 04.02.14 17:34
Оценка:
Здравствуйте, Yotce, Вы писали:

Y>Помоем вполне логично что если ( addr a == addr p ) то


Y>
    
Y>    char buf[100];
Y>    memcpy( &a, p, strlen("Jennifer\0") )

Y>   *buf = *p;
Y>


Я не очень понимаю, что этот код должен был продемонстрировать, сорри. Он не имеет смысла даже безотносительно объединений.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[5]: union { int (Foo::*cFunc ) (); void *void_func;}
От: jazzer Россия Skype: enerjazzer
Дата: 04.02.14 17:36
Оценка:
Здравствуйте, Yotce, Вы писали:

A>>ps сравнение указателей разных типов в общем случае не имеет смысла, так как компилятор волен хранить их по-разному


Y>Можно поподробнее где именно это утверждается?


Стандарт работает наоборот
Все, что не запрещено, разрешено.
Если явно не запрещено хранить по-разному — значит, разрешено.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[6]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Yotce  
Дата: 04.02.14 17:47
Оценка:
Здравствуйте, 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 уже не можем быть равна 1

    char buf[100];
    memcpy( &a, p, strlen("Jennifer\0") )

    ///*buf == *p;
}
Re[7]: union { int (Foo::*cFunc ) (); void *void_func;}
От: andyp  
Дата: 04.02.14 18:00
Оценка:
Здравствуйте, 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  
Дата: 04.02.14 18:02
Оценка:
Здравствуйте, 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[8]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Yotce  
Дата: 04.02.14 18:38
Оценка: -1
Здравствуйте, 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 даже представить не могу...
наверно тоже самое что если записать что-то на вроде этого
   char *str = NULL;
   memcpy( str, "test\0", strlen( "test\0") );



/// теперь мы обращаемся к a
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;
Re: union { int (Foo::*cFunc ) (); void *void_func;}
От: lxa http://aliakseis.livejournal.com
Дата: 04.02.14 20:23
Оценка:
На всякий случай ссылки, возможно, пригодятся:
здесь
здесь
Re[2]: union { int (Foo::*cFunc ) (); void *void_func;}
От: lxa http://aliakseis.livejournal.com
Дата: 04.02.14 20:37
Оценка:
Ну, этот пример и не обязан работать не с MSVC. А так, конечно, хак, но ведь мог бы помочь писать тесты: здесь
Re[8]: union { int (Foo::*cFunc ) (); void *void_func;}
От: rusted Беларусь  
Дата: 04.02.14 20:43
Оценка:
Здравствуйте, Yotce, Вы писали:

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>>


"ошибочку зделал", но так и не исправил полностью
Но вобщем понятно, что хотел показать. Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1. Даже если сейчас всё работает, то с обновлением компилятора может внезапно прострелить тебе ногу.
Re: union { int (Foo::*cFunc ) (); void *void_func;}
От: nen777w  
Дата: 04.02.14 21:14
Оценка:
Уже много ответили, я просто добавлю это
Re[9]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Yotce  
Дата: 05.02.14 07:40
Оценка:
Здравствуйте, rusted, Вы писали:

R> ...

R>"ошибочку зделал", но так и не исправил полностью
R>Но вобщем понятно, что хотел показать. Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1. Даже если сейчас всё работает, то с обновлением компилятора может внезапно прострелить тебе ногу.

R> Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1.

Быть всё таки такого не может два объекта в нашем мире не могу занимать одно и тоже пространство.
По стандарту адрес a и адрес *p равны



R> Даже если сейчас всё работает, то с обновлением компилятора может внезапно прострелить тебе ногу.

Меня и интересует это, в каких случаях это будет работать в каких нет И правильно ли это по стандарту ?
Re[10]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Хреннос  
Дата: 05.02.14 09:47
Оценка:
Здравствуйте, 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  
Дата: 05.02.14 13:52
Оценка:
Здравствуйте, Хреннос, Вы писали:

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


R>>> Так вот — при сборке с оптимизацией вполне может оказаться, что a все еще будет равно 1.

Y>>Быть всё таки такого не может два объекта в нашем мире не могу занимать одно и тоже пространство.

Х>Сказано же — с оптимизацией. Разработчики компилятора могли прочитать стандарт буквально, и компилятор может подразумевать, что переменные a и p не связаны логически.

Да они могут быть не связаны логически они связаны одним адресом

Х>Оптимизация может, например, поместить копию значения из а в регистр процессора, чтобы два раза и памяти не читать.


Y>>По стандарту адрес a и адрес *p равны


Х>По стандарту равны адреса членов юниона — т.е. в вашем случае это a и p.


да согласен.

Х>В вашем "ошибочку зделал" вы переписываете строку "Jennifer\0" прямо поверх содержимого юниона (а не по адресу, хранящемуся в а, как вам, очевидно, хотелось бы). При этом вы практически гарантированно выходите за его границу и перетираете данные, расположенные далее в стеке (длина строки Jennifer — 8 байт + терминальный нуль, что как минимум на 1 байт больше, чем размер пойнтера в 64-битной машине).

Х>Кстати, у строковых констант терминальный нуль указывать не нужно, он автоматически вставляется компилятором.

Кто — то ошибку заметил Rusted, улыбнулся и понял что хотели сказать...

Х>По стандарту это правильно.

Х>В каких случаях это будет работать? Зависит от того, что именно вы подразумеваете под словами "это" и "работать".
Х>Если "это" означает, что вы пишете в один из членов юниона, а потом читаете из другого — это может работать, а может и нет. Зависит от того, какой именно результат вы хотите получить и как именно вы хотите его использовать.

Х>В качестве примера работающего кода, использующего юнионы, могу предложить такую вот функцию:


Х>
Х>bool IsLittleEndianCPU()
Х>{
Х>   union {
Х>      int a;
Х>      char b[sizeof(int)];
Х>  } u;
Х>  u.a = 1;
Х>  return u.b[0] != 0;
Х>}
Х>


Х>Этот код работает, и даже почти безопасен (с оговоркой про гипотетические оптимизации компилятора).

Х>Жопа этого кода в том, что он бесполезен и не нужен в реальной жизни: порядок байт в слове известен еще в процессе компиляции, и нет никакого смысла его определять в рантайме.

Причем здесь попугаи ой порядок следования байт?

Х>Но здесь, заметим, работа идет исключительно с целыми числами. Работать так с пойнтерами небезопасно, а работать так с пойнтерами на функции-члены класса (да еще и не совсем понимая, что такое пойнтер и какие они бывают) — прямой путь к глюкам.


А где написано что так делать нельзя?
здесь
Re[12]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Хреннос  
Дата: 05.02.14 14:26
Оценка:
Здравствуйте, Yotce, Вы писали:

Y>Да они могут быть не связаны логически они связаны одним адресом


Стандарт, вроде бы, ясно дает понять: на связь "одним адресом" в юнионах закладываться не стоит.

Y>Кто — то ошибку заметил Rusted, улыбнулся и понял что хотели сказать...


Я тоже заметил и тоже понял.
Но это, к сожалению, не просто опечатка, т.к. ровно ту же ошибку вы воспроизвели в "исправленном" варианте.
Кроме того, из ваших пояснений к коду видно, что ваше понимание пойнтеров и работы с ними, скажем так, не отличается кристальной ясностью (путаете адрес переменной и ее содержимое, не видите проблемы в том, что размеры указателей разных типов могут отличаться, спокойно используете int в качестве контейнера для указателя). Именно поэтому я не прошел мимо, как Rusted, а попытался объяснить подробнее.

Y>Причем здесь попугаи ой порядок следования байт?


Это была иллюстрация "почти легального" использования юнионов для получения машинно-зависимой информации.

Х>>Но здесь, заметим, работа идет исключительно с целыми числами. Работать так с пойнтерами небезопасно, а работать так с пойнтерами на функции-члены класса (да еще и не совсем понимая, что такое пойнтер и какие они бывают) — прямой путь к глюкам.


Y>А где написано что так делать нельзя?


"Делать так" — это, возвращаясь к вашему первому посту, использовать обычный указатель, совмещенный с областью хранения указателя на нестатическую функцию-член класса, для чтения и модификации этого указателя на нестатическую функцию-член класса.

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

Текст стандарта только подтверждает вышесказанное.

Y>здесь


Нельзя ли вкратце пояснить, что сей код иллюстрирует, и каким именно образом?
Re[13]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Yotce  
Дата: 05.02.14 15:29
Оценка:
Здравствуйте, Хреннос, Вы писали:

Х>Здравствуйте, 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;}
От: Хреннос  
Дата: 05.02.14 16:56
Оценка:
Здравствуйте, Yotce, Вы писали:

Х>>Стандарт, вроде бы, ясно дает понять: на связь "одним адресом" в юнионах закладываться не стоит.


Y>Можно конкретные где это написано в стандарте?


Вам уже писали
Автор: andyp
Дата: 04.02.14
:

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  
Дата: 05.02.14 19:17
Оценка:
Здравствуйте, Хреннос, Вы писали:

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


Х>>>Стандарт, вроде бы, ясно дает понять: на связь "одним адресом" в юнионах закладываться не стоит.


Y>>Можно конкретные где это написано в стандарте?


Х>Вам уже писали
Автор: andyp
Дата: 04.02.14
:


Х>

Х>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 member
              int  a;   /// non-static member
              char c;   /// non-static member
    } obj;
   int __u_t::s_a = 10;
   obj.a= 100;
   obj.c='c';
 /// И для меня сие означает что нельзя рассчитывать на то что a=100;


здесь
Re[14]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Fuz  
Дата: 05.02.14 20:58
Оценка: 1 (1)
Здравствуйте, 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[16]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Хреннос  
Дата: 06.02.14 08:00
Оценка:
Здравствуйте, 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

Тут надо обратить внимание на то, что данные берутся "сырые" — т.е. компилятор не знает ничего об этих данных, и берет их "как есть". В некоторых случаях это приводит к неопределенному поведению (где конкретно — говорит стандарт
Автор: Fuz
Дата: 06.02.14
).
Например, при работе с указателями вовнутрь объекта с множественным наследованием, компилятор иногда пользуется своим знанием об объекте-потомке. При этом компилятор может подправить указатель так, чтобы он смотрел на нужную часть объекта. Если лезть к указателю через поле в юнионе, никаких аджастментов не будет. Пример правки указателей компилятором — тут.

И кстати, если в юнионе хранится нетривиальный объект (с конструктором), то никаких автоматических вызовов конструктора-деструктора при первом доступе к полю компилятор не сгенерирует (я это к содержимому вот этого вашего поста
Автор: Yotce
Дата: 04.02.14
). Надеюсь, вы в том посте пошутили, и в голове у вас все-таки не такой беспросветный мрак.

В юнионе нельзя хранить нетривиальные объекты (non-POD). Точнее, в стандарте С++11 кое-какие поблажки сделали, но несущественные (никаких автоматических вызовов конструктора нет и не будет).
Re[15]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Yotce  
Дата: 06.02.14 08:02
Оценка:
Здравствуйте, Fuz, Вы писали:

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


Х>>>Текст стандарта только подтверждает вышесказанное.

Y>>можно увидеть текст стандарта где это написано, желательно с разъяснениями почему Вы понимаете это так?

Fuz>Вот у тебя есть например:

Fuz>

Fuz>union
Fuz>{
Fuz> бомба,
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;}
От: lxa http://aliakseis.livejournal.com
Дата: 06.02.14 08:06
Оценка:
Что-то не так! :)
Re[16]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Fuz  
Дата: 06.02.14 08:49
Оценка:
Здравствуйте, Yotce, Вы писали:

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


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


Х>>>>Текст стандарта только подтверждает вышесказанное.

Y>>>можно увидеть текст стандарта где это написано, желательно с разъяснениями почему Вы понимаете это так?

Fuz>>Вот у тебя есть например:

Fuz>>

Fuz>>union
Fuz>>{
Fuz>> бомба,
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;}
От: Yotce  
Дата: 06.02.14 08:50
Оценка:
Здравствуйте, Хреннос, Вы писали:

Если Кто-то утверждает что трава "зелёная-зелёная" кто-то поверит на слова, а кто-то попытается разобраться в этом. И выяснят что трава "зелёная-зеленая" при определённых обстоятельствах, а ещё бывает "зеленая"... .
Re[16]: union { int (Foo::*cFunc ) (); void *void_func;}
От: Yotce  
Дата: 06.02.14 08:50
Оценка:
Здравствуйте, lxa, Вы писали:

lxa>Что-то не так! :)


Понел. Спс.
Re[18]: union { int (Foo::*cFunc ) (); void *void_func;}
От: uzhas Ниоткуда  
Дата: 06.02.14 08:52
Оценка:
Здравствуйте, Yotce, Вы писали:

Y>трава "зелёная-зелёная"


бросай курить, вставай на лыжи
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.