2016-06-02 6 views
4

Предположим, у нас есть абстрактный базовый класс, который наследуется:Есть ли способ определить, переопределена ли функция?

class Base 
{ 
    protected: 
     Base() {} 
     virtual ~Base() {} 
     virtual void on_event_foo(int) {} 
     virtual void on_event_bar(int) {} 
}; 

struct Concrete : public Base 
{ 
     virtual void on_event_foo(int value) {/*do some stuff with @value*/} 
}; 

ли способ узнать (во время компиляции будет лучшим) в virtual функции из Base, что было преодолено (с некоторым кодом в конструкторе, или со специальным рисунком)?

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

Я хочу, чтобы пользователь мог выбрать функцию, которую он хочет переопределить. Тогда в моем коде я создам обратные вызовы только для переопределенных функций. Чистые функции virtual не являются решением, поскольку они не могут позволить создать конкретный класс без переопределения всех них.

В конструкторе Base, сейчас я подключить много статических функций обратного вызова из Base в C API. В этих функциях я вызываю соответствующую функцию-член. Например, функция обратного вызова - static Base::EventFoo(/* ... */), которая вызывает внутри object->on_event_foo(/* .. */). Это связано с тем, что я не могу предоставить функцию-член как обратный вызов библиотеки C. Но создание слишком большого количества обратных вызовов делает мою упаковку медленнее. Итак, я хочу подключить только обратный вызов, который хочет пользователь, т. Е. Знание функций, которые его преодолевают.

+0

Сделать функции в базовом классе чистыми виртуальными: 'virtual void on_event_foo (int) = 0;'. –

+0

вы * можете * быть в состоянии сделать что-то со сравнением указателей между объектами – vu1p3n0x

+0

@ vu1p3n0x как сравнение 'Base :: on_event_bar' и' Concrete :: on_event_bar', вы имеете в виду? – Boiethios

ответ

6

Если вы готовы изменить несколько вещей, вы можете использовать curiously recursive template pattern, чтобы определить, является ли функция переопределяется

#include <iostream> 

template <class Derived> 
struct Base 
{ 
    virtual void on_event() {} 

    void raise_event() 
    { 
     if (&Derived::on_event == &Base::on_event) 
      std::cout << "not overridden" << std::endl; 
     else 
      std::cout << "overridden" << std::endl; 
    } 
}; 

struct Concrete1 : Base<Concrete1> 
{ 
    virtual void on_event() override {} 
}; 

struct Concrete2 : Base<Concrete2> 
{ 
    // no override 
}; 

int main() 
{ 
    Concrete1 c1; 
    Concrete2 c2; 

    c1.raise_event(); // prints overridden 
    c2.raise_event(); // prints not overridden 

    return 0; 
} 

Заявление &Derived::on_event == &Base::on_event должен быть решен во время компиляции (если это то, что вы беспокоиться), и if можно оптимизировать.

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

+0

Хм, я это увижу. Это кажется хорошей идеей: в конструкторе 'Base' я проверяю, есть ли переопределенное, а затем подключаю обратный вызов. – Boiethios

+0

Можете ли вы static_assert на основе offsetof? Затем вы получаете сбой времени компиляции. – Bathsheba

+0

@ Bathsheba возможно, но я не вижу, как это будет намного лучше, чем у меня. Я изначально предполагал это без необходимости в шаблоне, но, видимо, вы не можете делать '& on_event' или' this-> on_event', чтобы получить текущую ссылку на виртуальную функцию-член – vu1p3n0x

1

Не использовать методы virtual. Если вам нужно всего лишь указать конкретный тип, Concrete, чтобы связать его с кучей обратных вызовов на основе присутствия функций-членов, мы можем использовать шаблоны.

Для данного типа и имени функции мы можем определить, существует ли во время компиляции &T::func. Если это так, добавим этот обратный вызов. Таким образом, мы в конечном итоге с целой кучей вещей, как:

template <class T> 
void setup_cbs(T& object) { 
    T* ptr_to_object = ...; // store somewhere 

    static_if<has_on_event_foo<T>>(
     [](auto ptr){ 
      add_event_foo_callback(ptr, [](void* p, int i) { 
       using U = decltype(ptr); 
       static_cast<U>(p)->on_event_foo(i); 
      }) 
     }), 
     [](auto){} 
     )(ptr_to_object); 

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

+0

Это, вероятно, лучшее решение проблемы OP. Вы можете добавить, как реализовать 'has_on_event_foo <>' или по крайней мере ссылку – vu1p3n0x

+0

@ vu1p3n0x Здесь так много вопросов о том, как это сделать. – Barry

Смежные вопросы