Динамический полиморфизм приперегрузке функций
От: dip_2000 Россия  
Дата: 16.07.07 08:09
Оценка:
Тема конечно названа неудачно, надеюсь код просянит ситуацию:

class Base
{    
public:
    virtual Base* getSelf()
    {return this;}
};

class Derived1 : public Base
{
public :
    virtual Derived1* getSelf()
    {return this;};
};

class Derived2 : public Base
{    
public :
    virtual Derived2* getSelf()
    {return this;};
};

class Tester
{
public:
    virtual void Test(Derived1* Value)
    {
        ::MessageBox(NULL, _T("TEST Derived1"),_T("TEST Derived1"), MB_OK);
    }
    virtual void Test(Derived2* Value)
    {
        ::MessageBox(NULL, _T("TEST Derived2"),_T("TEST Derived2"), MB_OK);
    }    
};

...
//хочется что бы в точке использования этого хозяйства
Base* pBase1 = new Derived1;
Base* pBase2 = new Derived2;

Tester* pTester = new Tester;
pTester->Test(pBase1->getSelf());// вот здесь хочу вызов правильного Test'a а не ошибку компиляции


Если тема уже поднималась, то просто ткните меня носом в поиск. Спасибо!
Re: Динамический полиморфизм приперегрузке функций
От: Аноним  
Дата: 16.07.07 09:09
Оценка: 2 (2) +1
Методы getSelf в классах-потомках отличаются от метода getSelf класса Base возвращаемым типом — и, соответственно, не перегружают его, а скрывают (о чем компилятор наверняка пишет warning). Классы Derived1 и Derived2 не имеют перегруженного метода Base* getSelf(), и наследуют его от базового класса. Таким образом, в строчке, где возникает ошибка, всегда вызывается метод Base::getSelf.

Сделать то, что хочется, можно так (это, фактически, паттерн Visitor):

class Base
{    
public:
    virtual void testMe(Tester* t) = 0;
};

class Derived1 : public Base
{
public :
    virtual void testMe(Tester* t)
    { t->Test(this);}
};

class Derived2 : public Base
{    
public :
    virtual void testMe(Tester* t)
    { t->Test(this);}
};
Re[2]: Динамический полиморфизм приперегрузке функций
От: dip_2000 Россия  
Дата: 16.07.07 09:15
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Методы getSelf в классах-потомках отличаются от метода getSelf класса Base возвращаемым типом — и, соответственно, не перегружают его, а скрывают (о чем компилятор наверняка пишет warning). Классы Derived1 и Derived2 не имеют перегруженного метода Base* getSelf(), и наследуют его от базового класса. Таким образом, в строчке, где возникает ошибка, всегда вызывается метод Base::getSelf.


Извините, но вы не правы. Это называется ковариантными типами возращаемых значений. С++ это поддерживает. В этом месте проблеммы нет.
(Компилятор ворнинга не выдает )
Re: Динамический полиморфизм приперегрузке функций
От: ncode  
Дата: 16.07.07 13:16
Оценка:
Здравствуйте, dip_2000, Вы писали:

_>Тема конечно названа неудачно, надеюсь код просянит ситуацию:

_>...
_>//хочется что бы в точке использования этого хозяйства
_>Base* pBase1 = new Derived1;
_>Base* pBase2 = new Derived2;

_>Tester* pTester = new Tester;

_>pTester->Test(pBase1->getSelf());// вот здесь хочу вызов правильного Test'a а не ошибку компиляции
_>[/ccode]

1) Добавить в Tester следующий метод:
virtual void Test(Base* Value) {...}

2) Добавить метод Test() для каждого класса в иерархии от Base.
Второй вариант предпочтительней так как для разных классов можно использовать один метод Test().
Re[2]: Динамический полиморфизм приперегрузке функций
От: dip_2000 Россия  
Дата: 16.07.07 13:29
Оценка:
N>1) Добавить в Tester следующий метод:
N>
N>virtual void Test(Base* Value) {...}
N>


Простите, а что это даст ? Мне нужно что бы метод вызывался для потомков, а не для абстрактного(в общем случае) предка

N>2) Добавить метод Test() для каждого класса в иерархии от Base.


В моем примере сделано не так ?
Re[3]: Динамический полиморфизм приперегрузке функций
От: Аноним  
Дата: 16.07.07 13:32
Оценка:
_>Извините, но вы не правы. Это называется ковариантными типами возращаемых значений. С++ это поддерживает. В этом месте проблеммы нет.
_>(Компилятор ворнинга не выдает )

Да, в самом деле, getSelf для потомков перегружают Base::getSelf. Не знал про такую фичу
Но все равно, вы хотите, чтобы компилятор сделал выброр между функциями Tester::Test(Derived1*) и Tester::Test(Derived2*) на основании типа, который вернет функция getSelf, а этот тип будет известен только во время выполнения. В данном случае, видимо, значение, возвращаемое pBase1->getSelf(), неявно статически приводится к Base*, а потом компилятор не может привести Base* к Derived1* или Derived2*, поэтому и сообщает об ошибке.
Re[4]: Динамический полиморфизм приперегрузке функций
От: dip_2000 Россия  
Дата: 16.07.07 13:39
Оценка:
А>Да, в самом деле, getSelf для потомков перегружают Base::getSelf. Не знал про такую фичу
А>Но все равно, вы хотите, чтобы компилятор сделал выброр между функциями Tester::Test(Derived1*) и Tester::Test(Derived2*) на основании типа, который вернет функция getSelf, а этот тип будет известен только во время выполнения. В данном случае, видимо, значение, возвращаемое pBase1->getSelf(), неявно статически приводится к Base*, а потом компилятор не может привести Base* к Derived1* или Derived2*, поэтому и сообщает об ошибке.
Не правда! В случае когда есть одна функция (например
void Test(Derived1* Value)

) и в нее передаем Derived1 — все корректно.
Я понимаю почему возникает проблемма при компиляции, я спрашиваю есть ли хороший способ это обойти? Без switch и RTTI(для того что бы от них уйти и придумана такая иерархия)?

Еще раз: совершенно очевидно, что при статическом полиморфизме(перегрузке функций) у нас проблемм бы не возникло. Но, как верно замечено, мы узнаем тип только в момент исполнения. Но. Он нам известен. Известен конкретный тип. Как можно для него вызвать нужную функцию?
Re[3]: Динамический полиморфизм приперегрузке функций
От: ncode  
Дата: 16.07.07 13:54
Оценка:
Здравствуйте, dip_2000, Вы писали:

N>>1) Добавить в Tester следующий метод:

N>>
N>>virtual void Test(Base* Value) {...}
N>>


_>Простите, а что это даст ? Мне нужно что бы метод вызывался для потомков, а не для абстрактного(в общем случае) предка

Ну так этот метод и не будет вызываться для Base если он абстрактный, более того теперь можно спокойно скомпилировать код.
Для самоуспокоения можешь сделать так:
virtual void Test(Base* Value) {assert(false);}


N>>2) Добавить метод Test() для каждого класса в иерархии от Base.

_>В моем примере сделано не так ?
Я имел ввиду следующее:
class Base
{    
public:
    virtual Base* getSelf()
    {return this;}
    virtual void Test() {/*общий код*/};
};
class Derived1 : public Base
{
public :
    virtual Derived1* getSelf()
    {return this;};
    virtual void Test() {/*специфика для Derived1*/}
};
class Derived2 : public Base
{
public :
    virtual Derived1* getSelf()
    {return this;};
    //а тут например, можно не писать свой Test(), будет использоваться Base::Test()
};

Возможно в этом случае надобность в классе Tester отпадет.
Re: Динамический полиморфизм приперегрузке функций
От: tinytjan  
Дата: 16.07.07 14:01
Оценка:
Здравствуйте, dip_2000, Вы писали:

_>Тема конечно названа неудачно, надеюсь код просянит ситуацию:


_>...


CRTP ?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: Динамический полиморфизм приперегрузке функций
От: Аноним  
Дата: 16.07.07 14:07
Оценка:
_>Я понимаю почему возникает проблемма при компиляции, я спрашиваю есть ли хороший способ это обойти? Без switch и RTTI(для того что бы от них уйти и придумана такая иерархия)?

А чем не устраивает вариант, предложенный в первом ответе (http://www.rsdn.ru/forum/message/2586246.1.aspx
Автор:
Дата: 16.07.07
) ?
Re[5]: Динамический полиморфизм приперегрузке функций
От: AKh  
Дата: 16.07.07 14:10
Оценка:
Здравствуйте, dip_2000, Вы писали:

_>Еще раз: совершенно очевидно, что при статическом полиморфизме(перегрузке функций) у нас проблемм бы не возникло. Но, как верно замечено, мы узнаем тип только в момент исполнения. Но. Он нам известен. Известен конкретный тип. Как можно для него вызвать нужную функцию?


Мне кажется, что паттер "посетитель", который в самом начале предложил Аноним 93, очень подходит к данной проблеме. В нем, как раз и решается проблема статического типа. Был указатель на базовый объект. Теперь указатель this.
Re: Динамический полиморфизм приперегрузке функций
От: Programador  
Дата: 16.07.07 14:37
Оценка:
Здравствуйте, dip_2000, Вы писали:

_>Тема конечно названа неудачно, надеюсь код просянит ситуацию:


Возможно задача такая — имеется допустим по 10 классов в двух иерархиях. Как изобразить специиализации для некоторых из возможных 100 пар? Причем динамически (?).
Re[2]: Динамический полиморфизм приперегрузке функций
От: Programador  
Дата: 16.07.07 14:42
Оценка:
P>Здравствуйте, dip_2000,
Я так понял тестер желателен полиморфный, поскольку виртуал? Или это просто попытка решить какуюто другую задачу и он один
Re[5]: Динамический полиморфизм приперегрузке функций
От: alexeiz  
Дата: 16.07.07 22:29
Оценка:
Здравствуйте, dip_2000, Вы писали:

_>Я понимаю почему возникает проблемма при компиляции, я спрашиваю есть ли хороший способ это обойти? Без switch и RTTI(для того что бы от них уйти и придумана такая иерархия)?


По-моему, типичная ситуация для паттерна Visitor.
Re: Динамический полиморфизм приперегрузке функций
От: dip_2000 Россия  
Дата: 17.07.07 04:38
Оценка:
Спасибо Всем большое за ответы! Попробую ответить всем сразу :
Итак задача примерно такова классы из иерархии Base(конкретные экзампляеры Derived) передаются другому процессу при помощи WM_COPYDATA(способ не важен). В другом процессе у меня есть указатель void* который я безболезненно могу преобразовать к Base. Дальше у меня остается 2 варианта: использовать некий признак типа и строить большущий switch(и вызывать функции ProcessDerived1(), ProcessDerived2()) либо сделать, что то наподобии того что я описал. При этом класс Tester — класс в принимающем процессе, и функции нужно было назвать ProcessMess()
Паттерн Visitor не хотелось бы использовать из соображений инкапсуляции. Не хорошо, когда код клиента знает, что и как устроенно на сервере. Классы из иерархии Base — просто сообщения. Они не должны знать устройство их обработчика!
Re[4]: Динамический полиморфизм приперегрузке функций
От: dip_2000 Россия  
Дата: 17.07.07 04:46
Оценка:
_>>Простите, а что это даст ? Мне нужно что бы метод вызывался для потомков, а не для абстрактного(в общем случае) предка
N>Ну так этот метод и не будет вызываться для Base если он абстрактный, более того теперь можно спокойно скомпилировать код.
N>Для самоуспокоения можешь сделать так:
N>
N>virtual void Test(Base* Value) {assert(false);}
N>

По поводу варианта 1 — сделал вот так:

class Tester
{
public:
    virtual void Test (Base* Value)
    {
        ::MessageBox(NULL, _T("TEST0"),_T("TEST0"), MB_OK);
    }
    virtual void Test(Derived1* Value)
    {
        ::MessageBox(NULL, _T("TEST1"),_T("TEST1"), MB_OK);
    }
    virtual void Test(Derived2* Value)
    {
        ::MessageBox(NULL, _T("TEST2"),_T("TEST2"), MB_OK);
    }    
};

Разумеется начал вызываться void Test (Base* Value)

N>Я имел ввиду следующее:

N>
N>class Base
N>{    
N>public:
N>    virtual Base* getSelf()
N>    {return this;}
N>    virtual void Test() {/*общий код*/};
N>};
N>class Derived1 : public Base
N>{
N>public :
N>    virtual Derived1* getSelf()
N>    {return this;};
N>    virtual void Test() {/*специфика для Derived1*/}
N>};
N>class Derived2 : public Base
N>{
N>public :
N>    virtual Derived1* getSelf()
N>    {return this;};
N>    //а тут например, можно не писать свой Test(), будет использоваться Base::Test()
N>};
N>

N>Возможно в этом случае надобность в классе Tester отпадет.

вот здесь я постарался объяснить общую суть проблеммы, и то, почему на мой взгляд паттерн Visitor не подходит... http://www.rsdn.ru/forum/message/2587074.1.aspx
Автор: dip_2000
Дата: 17.07.07
Re[3]: Динамический полиморфизм приперегрузке функций
От: dip_2000 Россия  
Дата: 17.07.07 04:56
Оценка:
Здравствуйте, Programador, Вы писали:

P>>Здравствуйте, dip_2000,

P>Я так понял тестер желателен полиморфный, поскольку виртуал? Или это просто попытка решить какуюто другую задачу и он один

Постарался вот здесь http://www.rsdn.ru/forum/message/2587074.1.aspx
Автор: dip_2000
Дата: 17.07.07
еще раз описать проблемму
Re[2]: Динамический полиморфизм приперегрузке функций
От: dip_2000 Россия  
Дата: 17.07.07 05:06
Оценка:
Опять же попробую кодом проиллюстрировать:
есть вот так

class Base// сообщения
{    
public:
int SomeTypeID;
    virtual Base* getSelf()
    {return this;}
};

class Derived1 : public Base//первоее сообщение
{
public :
    virtual Derived1* getSelf()
    {return this;};
};

class Derived2 : public Base//второе
{    
public :
    virtual Derived2* getSelf()
    {return this;};
};


class Tester
{

void ProcessDerived1(Derived1* Val) {};
void ProcessDerived2(Derived2* Val) {};
...
void CommonProcessMess(void* Val)
{
Base* pBase = (Base*)Val;//точно знаем что это он
switch(pBase->SomeTypeID)
{
case TypeIDDerived1:
ProcessDerived1((Derived1 *)pBase );
break;
case TypeIDDerived2:
ProcessDerived2((Derived2 *)pBase );
break;

};
}
...
};

Хочется :

class Tester
{
ProcessDeriverd(Derived1* Val){};
ProcessDeriverd(Derived2* Val){};


void CommonProcessMess(void* Val)
{
Base* pBase = (Base*)Val;//точно знаем что это он
//и вместо switch вызываем
ProcessDeriverd(pBase->getSelf());//b получаем ошибку компилляции
};

}


Re[3]: Динамический полиморфизм приперегрузке функций
От: dip_2000 Россия  
Дата: 19.07.07 04:59
Оценка:
шепотом up!
Re[3]: Динамический полиморфизм приперегрузке функций
От: _Dreamer Россия  
Дата: 19.07.07 07:16
Оценка:
Здравствуйте, dip_2000, Вы писали:

_>Опять же попробую кодом проиллюстрировать:

_>есть вот так ...

_>

_>

ну если так не хочется Visitor применять, то я вот могу предложить только тот самый case() переделать, скажем вот так
class Base// сообщения
{    
protected :

    Base( int id ) : SomeTypeID( id ) {}

public:
    int SomeTypeID;
    virtual Base* getSelf()
    {return this;}

    virtual Base* querySelf( int type ) 
    { return NULL; }            
};

template < class Owner > 
struct QueryImpl : public Base
{
    QueryImpl( int id ) : Base( id ) {}

    virtual Base* querySelf( int type ) 
    { return (type == Owner::TYPE ? this->getSelf() : Base::querySelf(type) ); }
};

class Derived1 : public QueryImpl< Derived1 > //первое сообщение
{
    typedef QueryImpl< Derived1 > base;
public :

    static const int TYPE = 1;

    Derived1() : base( TYPE ) {}    

    virtual Derived1* getSelf()
    {return this;};    
};

class Derived2 : public QueryImpl< Derived2 > //второе
{    
    typedef QueryImpl< Derived2 > base;
public :

    static const int TYPE = 2;

    Derived2() : base( TYPE ) {}    

    virtual Derived2* getSelf()
    {return this;};
};

struct MessageProcessor 
{
    typedef boost::mpl::list< Derived1, Derived2 > messages_list;
    typedef boost::mpl::end< messages_list >::type end_iterator;
    typedef boost::mpl::begin< messages_list >::type begin_iterator;

    void processDerived( Derived1 * ) {}
    void processDerived( Derived2 * ) {}            

    template < class Iter >
    bool process( Base * base )
    {
        typedef typename boost::mpl::deref<Iter>::type message_type;

        Base * tmp = base->querySelf( message_type::TYPE );
        if ( tmp != NULL )
        {
            this->processDerived( (message_type *)tmp );
            // эквивалентно по идее такому
            // this->processDerived( (message_type *)base );

            return true;
        }

        return process< typename boost::mpl::next< Iter >::type >( base );
    }

    template <>
    bool process<end_iterator>( Base * base){ return false; }

    void commonProcess( void * arg )
    {
        Base * base = (Base *)arg;        
        if ( this->process< boost::mpl::begin< messages_list >::type >( base ) ) 
        {
            // удалось обработать сообщение
        }
        else
        {
            // не получилось обработать
        }
    }
};

void test_messages()
{
    MessageProcessor processor;

    Derived1 der1;
    Derived2 der2;
    //Base base;

    processor.commonProcess( static_cast< Base *>(&der1) );
    processor.commonProcess( static_cast< Base *>(&der2) );
    //processor.commonProcess( &base );
}

но сам принцип остается. ведь у Вас есть только базовый класс сообщения — Base, и что он такое — известно только в рантайме.
а вообще, можно же выделить интерфейс визитора, и именно о нем будут знать сообщения, а классу Tester только реализовать этот интерфейс.
class Derived1;
class Derived2;

struct IProcessor 
{
    virtual void process( Derived1 * ) = 0;
    virtual void process( Derived2 * ) = 0;
};

class Base// сообщения
{    
protected :

    Base( int id ) : SomeTypeID( id ) {}

public:
    int SomeTypeID;
    virtual Base* getSelf()
    {return this;}        

    virtual void startProcess( IProcessor * processor ) = 0;
};

class Derived1 : public QueryImpl< Derived1 > //первое сообщение
{
public :

    static const int TYPE = 1;

    Derived1() : Base( TYPE ) {}    

    virtual Derived1* getSelf()
    {return this;};    

    virtual void startProcess( IProcessor * processor )
    {
        processor->process( this );
    }
};

class Derived2 : public Base //второе
{    
public :

    static const int TYPE = 2;

    Derived2() : Base( TYPE ) {}    

    virtual Derived2* getSelf()
    {return this;};

    virtual void startProcess( IProcessor * processor )
    {
        processor->process( this );
    }
};
Re[4]: Динамический полиморфизм приперегрузке функций
От: dip_2000 Россия  
Дата: 19.07.07 08:22
Оценка:
Здравствуйте, _Dreamer, Вы писали:

Спасибо! Обдумаю оба варианта.
Вариант 1(правда в более упрощенном варианте) рассматривал — не совсем ясно как это будет по производительности, по сравнению с этим большим swicth/case.
Вобщем то вариант с Visitor'ом и абстрактным интерфесом наверное побыстрее будет...
Спасибо!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.