Потеря таблицы виртуальных функций
От: slavo  
Дата: 05.09.07 14:24
Оценка:
День добрый, миру конец.

Есть у меня 3 класс (два интерфейса и один их наследник):

class IA // интерфейс 1
{
public:
    virtual void FooA1() = 0;
    virtual void FooA2() = 0;
};

class IB // интерфейс 2
{
public:
    virtual void FooB1() = 0;
    virtual void FooB2() = 0;
};

class CC: public IA, public IB // наследник интерфейсов
{
public:
    // реализация интерфейсных функций
    void FooA1() {    int a1 = 1;}
    void FooA2() {    int a2 = 2;}
    void FooB1() {    int b1 = 1;}
    void FooB2() {    int b2 = 2;}
};


Есть функция main.

void main(void)
{
    CC * pC = new CC();  // создаем экземпляр класса CC
    void * pvC = pC;     // приводим его к void*
    ((IB*)pvC)->FooB1(); // приводим из void* к IB* и пытаемся вызвать FooB1 , но вызывается FooA1 !!!
}


Почему вызывается FooA1 вместо FooB1 ?!

Смысл извращений в том, что из своего кода я возвращаю клиентскому коду указатель на СС, но приведенный к void*. Потом клиентский код возвращает мне его в виде void*, а я привожу его к интерфейсу IB, но вызываются методы интерфейса IA. Как-будто если указатель побывал воидом, то обратной дороги нет
Re: Потеря таблицы виртуальных функций
От: Erop Россия  
Дата: 05.09.07 14:29
Оценка:
Здравствуйте, slavo, Вы писали:

S>
S>    CC * pC = new CC();  // создаем экземпляр класса CC
S>    void * pvC = pC;     // приводим его к void*
S>    ((IB*)pvC)->FooB1(); // приводим из void* к IB* и пытаемся вызвать FooB1 , но вызывается FooA1 !!!
S>


Так нельзя!!!
из void* надо возвращаться туда, откуда переходил к void*
Так вот будет работать:
((CC*)pvC)->FooB1()
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Обратная дорога.
От: Erop Россия  
Дата: 05.09.07 14:35
Оценка: 6 (1)
Здравствуйте, slavo, Вы писали:

class IObj {
public:
    virtual ~IObj() = 0 {}

    void* ToVoid() { return this; }
    template<typename T> 
        static T* FromVoid( void* obj )
            { return dynamic_cast<T*>( static_cast<IObj*>( obj ) ); }
};

class CA : virtual public IObj {
    // тут реализация
};

class CB : virtual public IObj {
    // тут реализация
};

class CC : public CA, public CB {
    // тут реализация
};



Ну и теперь пишешь себе на здоровье:
CC* pC = new CC;
void* pV = pC->ToVoid();
CB* pB = IObj::FromVoid<CB>( pV );
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Потеря таблицы виртуальных функций
От: remark Россия http://www.1024cores.net/
Дата: 05.09.07 14:36
Оценка:
Здравствуйте, slavo, Вы писали:

S>
S>void main(void)
S>{
S>    CC * pC = new CC();  // создаем экземпляр класса CC
S>    void * pvC = pC;     // приводим его к void*
S>    ((IB*)(CC*)pvC)->FooB1(); // приводим из void* к IB* и пытаемся вызвать FooB1 , но вызывается FooA1 !!!
S>}
S>



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Потеря таблицы виртуальных функций
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 05.09.07 14:40
Оценка: -1 :)
Здравствуйте, slavo, Вы писали:

S>День добрый, миру конец.


Как много букв и горя

S>Почему вызывается FooA1 вместо FooB1 ?!


Потому что то вызов FooB1 идет через таблицу виртуальных функций, которую ты варварским способом заимствовал у IA.

Поменяй порядок наследования
class CC:public IB, public IA

и подумай о вечном. О бабах.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[2]: Потеря таблицы виртуальных функций
От: slavo  
Дата: 05.09.07 14:41
Оценка:
Здравствуйте, Erop, Вы писали:

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


S>>
S>>    CC * pC = new CC();  // создаем экземпляр класса CC
S>>    void * pvC = pC;     // приводим его к void*
S>>    ((IB*)pvC)->FooB1(); // приводим из void* к IB* и пытаемся вызвать FooB1 , но вызывается FooA1 !!!
S>>


E>Так нельзя!!!

E>из void* надо возвращаться туда, откуда переходил к void*
E>Так вот будет работать:
((CC*)pvC)->FooB1()


Проблема как раз в том, что не знаю, откуда он переходил .
Re[2]: Потеря таблицы виртуальных функций
От: Аноним  
Дата: 05.09.07 14:46
Оценка:
КД>Поменяй порядок наследования
КД>
КД>class CC:public IB, public IA
КД>

КД>и подумай о вечном. О бабах.
точно точно, тогда будет иметь всегда вызовы для IB.
Re[2]: Потеря таблицы виртуальных функций
От: slavo  
Дата: 05.09.07 14:49
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Здравствуйте, slavo, Вы писали:


S>>День добрый, миру конец.


КД>Как много букв и горя


S>>Почему вызывается FooA1 вместо FooB1 ?!


КД>Потому что то вызов FooB1 идет через таблицу виртуальных функций, которую ты варварским способом заимствовал у IA.


КД>Поменяй порядок наследования

КД>
КД>class CC:public IB, public IA
КД>

КД>и подумай о вечном. О бабах.

А если мне надо будет тоже самое для интерфейса IA. Взад все переделывать ?
Re[3]: Потеря таблицы виртуальных функций
От: Erop Россия  
Дата: 05.09.07 14:50
Оценка:
Здравствуйте, slavo, Вы писали:

S>Проблема как раз в том, что не знаю, откуда он переходил .

А ты к CA когда-то хочешь переходить? Если нет, то переходи всегда к void* через CB*.
А если ты хочешь большего, то есть регулярный выход
Автор: Erop
Дата: 05.09.07
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Обратная дорога.
От: slavo  
Дата: 05.09.07 14:51
Оценка:
Здравствуйте, Erop, Вы писали:

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


E>
E>class IObj {
E>public:
E>    virtual ~IObj() = 0 {}

E>    void* ToVoid() { return this; }
E>    template<typename T> 
E>        static T* FromVoid( void* obj )
E>            { return dynamic_cast<T*>( static_cast<IObj*>( obj ) ); }
E>};

E>class CA : virtual public IObj {
E>    // тут реализация
E>};

E>class CB : virtual public IObj {
E>    // тут реализация
E>};

E>class CC : public CA, public CB {
E>    // тут реализация
E>};
E>



E>Ну и теперь пишешь себе на здоровье:

E>
E>CC* pC = new CC;
E>void* pV = pC->ToVoid();
E>CB* pB = IObj::FromVoid<CB>( pV );
E>


Это хорошо, а без dynamic_cast можно?
Re[3]: Обратная дорога.
От: Erop Россия  
Дата: 05.09.07 14:54
Оценка:
Здравствуйте, slavo, Вы писали:

S>Это хорошо, а без dynamic_cast можно?

Тогда надо знать о CC. Именно dynamic_cast инкапсулирует тут это знание...
Ещё есть вариант отказаться от виртуального наследования и выводить CC непосредстенно из IObj, а CA и CB сделать просто классами. Но вот от dynamic_cast на таком пути отказаться не получится, да и опасно...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Обратная дорога.
От: slavo  
Дата: 05.09.07 15:08
Оценка:
Здравствуйте, Erop, Вы писали:

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


S>>Это хорошо, а без dynamic_cast можно?

E>Тогда надо знать о CC. Именно dynamic_cast инкапсулирует тут это знание...
E>Ещё есть вариант отказаться от виртуального наследования и выводить CC непосредстенно из IObj, а CA и CB сделать просто классами. Но вот от dynamic_cast на таком пути отказаться не получится, да и опасно...

во, какое решение

class IReiter // вводим новый интерфейс, который будет делать кастование
{
public:
    virtual void AsIA() = 0;
    virtual void AsIB() = 0;
}

class IA // интерфейс 1
{
public:
    virtual void FooA1() = 0;
    virtual void FooA2() = 0;
};

class IB // интерфейс 2
{
public:
    virtual void FooB1() = 0;
    virtual void FooB2() = 0;
};

class CC: public IReinter, public IA, public IB // наследник интерфейсов
{
public:
    // реализация интерфейса кастования
    IA * AsIA() { return this;};
    IB * AsIB() { return this;};

    // реализация интерфейсных функций
    void FooA1() {    int a1 = 1;}
    void FooA2() {    int a2 = 2;}
    void FooB1() {    int b1 = 1;}
    void FooB2() {    int b2 = 2;}
};

 
void main(void)
{
    CC * pC = new CC();  // создаем экземпляр класса CC
    void * pvC = pC;     // приводим его к void*
    IB * pIB = ((IReinter*)pvC)->AsIB(); // т.к. IReinter первый в списке наследования, то к нему приводим без проблем
    pIB->FooB1(); // вызываем FooB1. Работает
}
Re: Потеря таблицы виртуальных функций
От: d.4 Россия  
Дата: 05.09.07 15:09
Оценка: 4 (1)
Здравствуйте, slavo, Вы писали:

S>

S>class CC: public IA, public IB // наследник интерфейсов
S>{
S>public:
S>    // ...
S>};
S>


S>Есть функция main.


S>
S>void main(void)
S>{
S>    CC * pC = new CC();  // создаем экземпляр класса CC
      void * pvC = (void*)static_cast<IB*>(pC);     // сначала приводим его к IB*, а уже потом к void*
S>    ((IB*)pvC)->FooB1(); 
S>}
S>


Все ок: вызывающая сторона не обязана ничего знать о том, какой объект ей передали, главное, чтобы адрес виртуальной таблицы был верный.

Почитайте про COM и посмотрите реализацию IUnknown::QueryInterface в примерах — увидите то же самое.
Re[5]: Решение
От: slavo  
Дата: 05.09.07 15:14
Оценка:
В прошлом посте ошибка. Надо так:
class IA;
class IB;

class IReinter
{
public:

    virtual IA * AsIA() = 0;
    virtual IB * AsIB() = 0;
};

...
Re[5]: Обратная дорога.
От: Erop Россия  
Дата: 05.09.07 15:14
Оценка:
Здравствуйте, slavo, Вы писали:

S>>>Это хорошо, а без dynamic_cast можно?

S>во, какое решение
Так конечно можно, но только в случае, когда у тебя всего два интерфейса в деле IA и IB.
Если их всего два и они всегда ходят парой, то есть ещё более простое решение
[c]
struct IAB : IA, IB {};
class CC : public IAB {
// Тут реализация
};[c] И приводиться к/от void* всегда через IAB*.

А вот если у тебя интерфейсов много, и пар их тоже много бывает, то твой IReter превратииться в "dynamic_cast своими силами". ИМХО оно того не стоит.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Потеря таблицы виртуальных функций
От: Кодт Россия  
Дата: 05.09.07 15:58
Оценка: +1
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>и подумай о вечном. О бабах.


Бабах — это имеется в виду вылет по Access Violation?



Некоторые библиотеки (MFC) нетерпимы к произвольному порядку баз. MFC-шный класс может быть только первой базой, иначе где-нибудь да получим такую фигню, которую наблюдает slavo.
Поэтому здесь, без вариантов, переделываем программу под это требование.

Если дело не в этом, то встаёт вопрос: зачем вообще было обезличивать тип объекта, превращая его в void*?
Либо — если уж обезличивать, то делать это грамотно
CC* pc = new CC;
void* pv = (void*)(IB*)pC;
.....
((IB*)pv)->FooB1(123);

Либо — идти вообще до конца и обезличить ещё и метод.
CC* pc = new CC;
boost::function<void(*)(int)> f = boost::bind(&IB::FooB1, pc, _1);
.....
f(123);
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[3]: Потеря таблицы виртуальных функций
От: alzt  
Дата: 05.09.07 16:09
Оценка:
Здравствуйте, slavo, Вы писали:

КД>>Как много букв и горя


S>>>Почему вызывается FooA1 вместо FooB1 ?!


КД>>Потому что то вызов FooB1 идет через таблицу виртуальных функций, которую ты варварским способом заимствовал у IA.


КД>>Поменяй порядок наследования

КД>>
КД>>class CC:public IB, public IA
КД>>

КД>>и подумай о вечном. О бабах.

S>А если мне надо будет тоже самое для интерфейса IA. Взад все переделывать ?


Можно создать два класса (один для IA, другой для IB) и использовать их в зависимости от ситуации.
Re[3]: Потеря таблицы виртуальных функций
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 05.09.07 17:16
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Коваленко Дмитрий, Вы писали:


КД>>и подумай о вечном. О бабах.


К>Бабах — это имеется в виду вылет по Access Violation?


Нет. О бабах, это значит о бабах


К>Если дело не в этом, то встаёт вопрос: зачем вообще было обезличивать тип объекта, превращая его в void*?

Вот и я о том же.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Потеря таблицы виртуальных функций
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 05.09.07 17:21
Оценка:
Здравствуйте, slavo, Вы писали:

КД>>Поменяй порядок наследования

КД>>
КД>>class CC:public IB, public IA
КД>>

S>А если мне надо будет тоже самое для интерфейса IA. Взад все переделывать ?

Ну да. Ты раз 10 взад-назад это определение погоняешь и поймешь, что это ненормально.

И начнешь думать
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.