Нужно узнать, замещена ли виртуальная функция. Первое что приходит в голову - возвращаемое значение: Код (Text): class base { public: virtual bool process() { return false; } }; class derived : public base { public: virtual bool process() { return true; } }; std::string request_handler(base_ptr b) { if(b->process()) { return "Request processed"; } else { return "Error: request handler not implemented"; } } Второе, что приходит в голову - это исключение. Но как приходит, так и уходит - отсутствие замещения не является нарушением инвариантов, это вполне законная ситуация. Возвращаемое значение через передаваемую ссылку - вообще ахтунг.
Хочется чтобы это было так: Код (Text): struct A { virtual void foo() {} }; struct B : A { virtual void foo() {} }; bool is_foo_implemented(A& a) { return &a.foo != &A::foo; } но чтобы при этом компилировалось )))
letopisec Ну так как именно? KeSqueer virtual void foo() = 0 Эта штука не даст скомпилироваться если функция не замещена. А мне надо чтобы все компилилось. Чтобы в рантайме узнавать. Смысл в том, что базовый класс и его реализации - это разные устройства, которые отвечают на команды типа "запрос-ответ" формализованного протокола. У некоторых устройств нет некоторых так скажем функций.
_DEN_ Может сделать какой нибудь список функций, к-рое класс "поддерживает". Что-нибудь вроде: Код (Text): enum { IS_OPEN = 0x01, IS_READ = 0x02, IS_WRITE = 0x04, ... }; ... class Derived : public Base { enum { FLAGS = IS_OPEN | IS_READ | IS_WRITE }; public: ... uint32_t getFlags() const { return (uint32_t)FLAGS; } }; ... Derived d; if (d.getFlags() & IS_READ) d.read(buf, size); ... Как-то так...
_DEN_ Ну как, динамикастом. В твоем случае, когда достаточно оличить одного потомка от родителя все просто. Если тебе нужно отличать базовый класс от множества потомков, то нужно добавить промежуточный пустой класс, от которого и рожать потомков, типа как-то так: Код (Text): struct A { virtual void foo() {} }; struct AInter : A { }; struct B : AInter { virtual void foo() {} }; struct C : AInter { virtual void foo() {} }; bool is_foo_implemented(A* obj) { return dynamic_cast<AInter*>(obj); } int main () { bool imlemented; A *oa = new A; C *oc = new C; B *ob = new B; imlemented = is_foo_implemented (oa); imlemented = is_foo_implemented (oc); imlemented = is_foo_implemented (ob); return 0; }
_DEN_ Вот так можно: Код (Text): bool is_foo_implemented(A& a) { enum { A_foo_pos = 0, }; A _; return ((void **&)a)[A_foo_pos] != ((void **&)_)[A_foo_pos]; } --- Сравнение с A::foo не катит из-за возможных thunks.
как вариант 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(); }
censored Этот способ не подходит, потому что нужно узнавать о замещении функции через указатель на базовый класс. letopisec Проблема в том, что интерфейсных функций несколько, и реализация может замещать часть из них. Простого определения типа базовый / дочерний - не достаточно. green Этот способ тоже позволяет лишь определить, равны ли указатели на таблицы виртуальных функций. Этого недостаточно. Функций много и замещаться могут не все. RedLord Во-первых, не ясно зачем тут NVI, во-вторых в данном случае кидать исключение неправильно - незамещенная функция не является нарушением внутренних инвариантов - это вполне законная с точки зрения логики программы ситуация.
_DEN_ не вопрос - убрать throw но в этом случае - можем определить замещение функции Код (Text): void request_handler(base* b) { b->process(); } здесь не получиться у базового класса дернуть do_process. как иначе? сделать request_handler другом?
_DEN_ А ты проверь. -- Если будешь добавлять другие виртуальные ф-ции в A, то добавляй их после foo (или корректируй A_foo_pos).
green Ну это стремный способ. Компилятор не обязат придерживаться какого-то строгого правила в заполнении vft.
Можно использовать map<ключ (строка), объект заранее обговоренного типа (разного для разных строк) умеющий исполнять нужные функции>. Соотвественно реализации добавят туда то, что они поддерживают. Только лучше создавать объекты не для каждой функции, а для групп функций. Если нужно, в объекте может быть ссылка на исходный.