Функция C ++ не принимает конкретную реализацию - PullRequest
6 голосов
/ 02 апреля 2012

Я пытаюсь реализовать типизированную шину событий.Я застрял с функцией EventBus::subscribe, потому что она не принимает мой конкретный обработчик событий.В более ранней версии мой AbstractEventHandler был реализован только как абстрактный класс, без шаблона.У меня не было проблем с этой реализацией.Вот почему я предполагаю, что фактическая проблема связана с абстрактным шаблоном.

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

enum содержит все различные событияимеется в наличии.Абстрактное событие - это основа, из которой происходят все конкретные события.Обработчик событий - это абстрактный шаблон с событием в качестве класса шаблона для обеспечения безопасности типов.Шина событий отвечает за распространение всех опубликованных событий среди соответствующих обработчиков.

enum EVENT_TYPE 
{
    ON_EVENT_1,
    ON_EVENT_2
};

class AbstractEvent 
{
public:
    AbstractEvent() {};
    virtual ~AbstractEvent() {};

    virtual EVENT_TYPE type() = 0;
};

template<class T>
class AbstractEventHandler 
{
public:
    AbstractEventHandler() {};
    virtual ~AbstractEventHandler() {};

    virtual void on_event(T *event) = 0;
};

class EventBus 
{
public:
    EventBus() {};
    virtual ~EventBus() {};

    void subscribe(EVENT_TYPE type, 
                    AbstractEventHandler<AbstractEvent> *eventHandler) {
        // Add handler to vector for further use
    }

    void publish(AbstractEvent *event) {
        // send event to each handler in respective vector
    }
};

Ниже приведены мой конкретный обработчик события и события и функция main ()

class ConcreteEvent : public AbstractEvent 
{
public:
    ConcreteEvent() {};
    virtual ~ConcreteEvent() {};

    EVENT_TYPE type() {
        return ON_EVENT_1;
    };
};

class ConcreteEventHandler : public AbstractEventHandler<ConcreteEvent> 
{
public:
    ConcreteEventHandler() {}
    virtual ~ConcreteEventHandler() {};

    void on_event(ConcreteEvent *event) {
        // Do something
    };
};

int main() 
{
    EventBus *eventBus = new EventBus();

    ConcreteEventHandler handler = ConcreteEventHandler();

    // This failes!
    eventBus->subscribe(ON_EVENT_1, &handler);
}

Компилятор возвращает сообщение об ошибке, сообщающее, что не существует соответствующей функции для вызова

EventBus::subscribe(EVENT_TYPE, ConcreteEventHandler*) 

и что единственными кандидатами являются

void EventBus::subscribe(EVENT_TYPE, AbstractEventHandler<AbstractEvent>*) 

Как я могу реализовать свой метод EventBus :: subscribe для принятия конкретных реализаций моего абстрактного класса?

Обновление: решение

Я изменил описание метода EventBus::subscribe на следующее, и теперь оно работает прекрасно:

template<typename T>
void subscribe(EVENT_TYPE type, AbstractEventHandler<T> *eventHandler) {

}

СпасибоРохан, за твои намеки!Они помогли мне найти это решение.

Ответы [ 3 ]

5 голосов
/ 02 апреля 2012

Причина в том, что ConcreteEventHandler является подклассом AbstractEventHandler<ConcreteEvent>, а не AbstractEventHandler<AbstractEvent>.

Это может показаться удивительным, но AbstractEventHandler<ConcreteEvent> не может быть подклассом AbstractEventHandler<AbstractEvent>, хотя ConcreteEvent является подклассом AbstractEvent.

Причина в том, что при использовании шаблонов шаблоны по вашему желанию не гарантируют безопасность типов. Давайте посмотрим на пример. Давайте рассмотрим стандартную парадигму базового класса Animal и подклассов Cat и Dog. Допустим, у нас есть список животных:

std::list<Animals>* animals;

и список кошек:

std::list<Cat> cats;

Следующее, НЕ является действительным приведением:

animals = &cats;

Причина в том, что, если я собираюсь это сделать,

animals->add(new Dog("Ben"));

Я бы на самом деле добавил Dog в список Cat с. cats.last() здесь на самом деле вернет Dog. Таким образом, в этом случае вы по существу добавляете Dog к списку Cat s. Я видел достаточно эпизодов Looney Tunes, чтобы понять, что это плохая идея:

cats.last().meow();

Вышесказанное определенно не соответствует действительности, поскольку все мы знаем, что Dog может только bowbow().

EDIT

Чтобы ответить на ваш вопрос, вот что я предлагаю вам сделать; Пусть ConcreteEventHandler наследуется от AbstractEventHandler<AbstractEvent>, и внутри кода, где бы вы ни использовали ConcreteEvent, используйте dynamic_case, чтобы привести AbstractEvent к ConcreteEvent. При этом будет использоваться интроспекция во время выполнения, что может немного повлиять на производительность (также я видел довольно много людей, выступающих против использования динамического приведения), но вы сможете успешно выполнить корректное преобразование типа данных.

1 голос
/ 02 апреля 2012

Рохан уже ответил, почему код не компилируется, однако я хотел бы предложить другой подход.

Вы можете реализовать это так, чтобы Eventhandler подписывался непосредственно на EventGenerator . Таким образом, существует прямая связь между генерацией и обработкой события.
Затем событие должно содержать ссылку на свой генератор, чтобы позволить ему получить доступ к подписанным обработчикам, и шина событий вызывает метод для события, чтобы позволить ему обрабатывать себя.

Таким образом, eventbus не знает о обработчиках событий, и вам даже не нужно перечисление eventtype.
Однако вам нужны разные генераторы событий, которые должны быть доступны различным обработчикам событий, а не одной шине событий. Каждый обработчик событий может обрабатывать только одно событие, поэтому, если требуется больше событий, обработчики событий должны быть агрегированы (путем делегирования или наследования).

0 голосов
/ 02 апреля 2012

Ваш AbstractEventHandler<T> класс должен наследовать AbstractEvent. Вероятно, это было ваше намерение, вы просто забыли написать это.

template<class T>
class AbstractEventHandler
    :public AbstractEvent
{
public:
    AbstractEventHandler() {};
    virtual ~AbstractEventHandler() {};

    virtual void on_event(T *event) = 0;
}
...