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;
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.