Замещена ли виртуальная функция?

Тема в разделе "LANGS.C", создана пользователем _DEN_, 31 мар 2009.

  1. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Нужно узнать, замещена ли виртуальная функция. Первое что приходит в голову - возвращаемое значение:

    Код (Text):
    1. class base
    2. {
    3. public:
    4.     virtual bool process()
    5.     {
    6.         return false;
    7.     }
    8. };
    9.  
    10. class derived : public base
    11. {
    12. public:
    13.     virtual bool process()
    14.     {
    15.         return true;
    16.     }
    17. };
    18.  
    19. std::string request_handler(base_ptr b)
    20. {
    21.     if(b->process())
    22.     {
    23.         return "Request processed";
    24.     }
    25.     else
    26.     {
    27.         return "Error: request handler not implemented";
    28.     }
    29. }
    Второе, что приходит в голову - это исключение. Но как приходит, так и уходит - отсутствие замещения не является нарушением инвариантов, это вполне законная ситуация.

    Возвращаемое значение через передаваемую ссылку - вообще ахтунг.
     
  2. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Хочется чтобы это было так:

    Код (Text):
    1. struct A
    2. {
    3.     virtual void foo() {}
    4. };
    5.  
    6. struct B : A
    7. {
    8.     virtual void foo() {}
    9. };
    10.  
    11. bool is_foo_implemented(A& a)
    12. {
    13.     return &a.foo != &A::foo;
    14. }
    но чтобы при этом компилировалось )))
     
  3. letopisec

    letopisec New Member

    Публикаций:
    0
    Регистрация:
    8 авг 2004
    Сообщения:
    228
  4. KeSqueer

    KeSqueer Сергей

    Публикаций:
    0
    Регистрация:
    19 июл 2007
    Сообщения:
    1.183
    Адрес:
    Москва
    _DEN_
    Не силен в C++, но
    virtual void foo() = 0
    не помогает?
     
  5. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    letopisec

    Ну так как именно?


    KeSqueer

    :)

    virtual void foo() = 0

    Эта штука не даст скомпилироваться если функция не замещена. А мне надо чтобы все компилилось. Чтобы в рантайме узнавать. Смысл в том, что базовый класс и его реализации - это разные устройства, которые отвечают на команды типа "запрос-ответ" формализованного протокола. У некоторых устройств нет некоторых так скажем функций.
     
  6. censored

    censored New Member

    Публикаций:
    0
    Регистрация:
    5 июл 2005
    Сообщения:
    1.615
    Адрес:
    деревня "Анонимные Прокси"
    _DEN_
    Может сделать какой нибудь список функций, к-рое класс "поддерживает". Что-нибудь вроде:
    Код (Text):
    1. enum {
    2.   IS_OPEN = 0x01,
    3.   IS_READ = 0x02,
    4.   IS_WRITE = 0x04,
    5.   ...
    6. };
    7.  
    8. ...
    9.  
    10. class Derived : public Base {
    11.   enum { FLAGS = IS_OPEN | IS_READ | IS_WRITE };
    12. public:
    13.   ...
    14.   uint32_t getFlags() const { return (uint32_t)FLAGS; }
    15. };
    16.  
    17. ...
    18. Derived d;
    19. if (d.getFlags() & IS_READ)
    20.   d.read(buf, size);
    21. ...
    Как-то так...
     
  7. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    [delete]
     
  8. letopisec

    letopisec New Member

    Публикаций:
    0
    Регистрация:
    8 авг 2004
    Сообщения:
    228
    _DEN_
    Ну как, динамикастом. В твоем случае, когда достаточно оличить одного потомка от родителя все просто.
    Если тебе нужно отличать базовый класс от множества потомков, то нужно добавить промежуточный пустой класс, от которого и рожать потомков, типа как-то так:

    Код (Text):
    1. struct A
    2. {
    3.     virtual void foo() {}
    4. };
    5.  
    6. struct AInter : A
    7. {
    8. };
    9.  
    10. struct B : AInter
    11. {
    12.     virtual void foo() {}
    13. };
    14.  
    15. struct C : AInter
    16. {
    17.     virtual void foo() {}
    18. };
    19.  
    20. bool is_foo_implemented(A* obj)
    21. {
    22.   return dynamic_cast<AInter*>(obj);
    23. }
    24.  
    25. int main ()
    26. {
    27.   bool imlemented;
    28.   A *oa = new A;
    29.   C *oc = new C;
    30.   B *ob = new B;
    31.  
    32.   imlemented = is_foo_implemented (oa);
    33.   imlemented = is_foo_implemented (oc);
    34.   imlemented = is_foo_implemented (ob);
    35.  
    36.  
    37.   return 0;
    38. }
     
  9. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    _DEN_
    Вот так можно:

    Код (Text):
    1. bool is_foo_implemented(A& a)
    2. {
    3.     enum
    4.     {
    5.         A_foo_pos = 0,
    6.     };
    7.     A _;
    8.     return ((void **&)a)[A_foo_pos] != ((void **&)_)[A_foo_pos];
    9. }
    :derisive:
    ---
    Сравнение с A::foo не катит из-за возможных thunks.
     
  10. RedLord

    RedLord Member

    Публикаций:
    0
    Регистрация:
    23 июн 2005
    Сообщения:
    183
    Адрес:
    Ukraine
    как вариант

    class base
    {
    public:
    bool process()
    {
    return do_process();
    }

    private:
    virtual bool do_process()
    {
    // если попали сюда - нас не перекрыли
    throw; //"Error: request handler not implemented";
    return false;
    }
    };

    class derived : public base
    {
    public:
    virtual bool do_process()
    {
    // здесь мы не можем дернуть базовый do_process - он приватен
    return true;
    }
    };

    void request_handler(base* b)
    {
    b->process();
    }
     
  11. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    censored

    Этот способ не подходит, потому что нужно узнавать о замещении функции через указатель на базовый класс.


    letopisec

    Проблема в том, что интерфейсных функций несколько, и реализация может замещать часть из них. Простого определения типа базовый / дочерний - не достаточно.


    green

    Этот способ тоже позволяет лишь определить, равны ли указатели на таблицы виртуальных функций. Этого недостаточно. Функций много и замещаться могут не все.


    RedLord

    Во-первых, не ясно зачем тут NVI, во-вторых в данном случае кидать исключение неправильно - незамещенная функция не является нарушением внутренних инвариантов - это вполне законная с точки зрения логики программы ситуация.
     
  12. RedLord

    RedLord Member

    Публикаций:
    0
    Регистрация:
    23 июн 2005
    Сообщения:
    183
    Адрес:
    Ukraine
    _DEN_
    не вопрос - убрать throw

    но в этом случае - можем определить замещение функции

    Код (Text):
    1. void request_handler(base* b)
    2. {
    3. b->process();
    4. }
    здесь не получиться у базового класса дернуть do_process. как иначе? сделать request_handler другом?
     
  13. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    _DEN_
    А ты проверь. :)
    --
    Если будешь добавлять другие виртуальные ф-ции в A, то добавляй их после foo (или корректируй A_foo_pos).
     
  14. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    green

    Ну это стремный способ. Компилятор не обязат придерживаться какого-то строгого правила в заполнении vft.
     
  15. halyavin

    halyavin New Member

    Публикаций:
    0
    Регистрация:
    13 май 2005
    Сообщения:
    252
    Адрес:
    Russia
    Можно использовать map<ключ (строка), объект заранее обговоренного типа (разного для разных строк) умеющий исполнять нужные функции>. Соотвественно реализации добавят туда то, что они поддерживают. Только лучше создавать объекты не для каждой функции, а для групп функций. Если нужно, в объекте может быть ссылка на исходный.