Для системы событий, которую я пишу, я хочу привязать обратные вызовы к списку функций.
Вот базовый c пример того, что я хочу сделать:
#include <iostream>
#include <functional>
#include <string>
class Base {
public:
virtual std::string getType() const = 0;
};
class Derived : public Base {
protected:
int some_data;
public:
Derived(int some_data): some_data(some_data) {}
virtual std::string getType() const {
return "Derived";
}
int getData() const {
return this->some_data;
}
};
class DerivedTwo : public Base {
protected:
double some_data;
public:
DerivedTwo(double some_data): some_data(some_data) {}
virtual std::string getType() const {
return "DerivedTwo";
}
// The type of data is not always the same.
double getData() const {
return this->some_data;
}
};
// The type of member should ALWAYS be Derived but then i can't store it in <callback>
void onDerivedEvent(Base& member) {
std::cout << member.getType() << std::endl;
// This is obviously not possible with member being a base class object
// member.getData();
}
// The type of member should ALWAYS be DerivedTwo but then i can't store it in <callback>
void onDerivedTwoEvent(Base& member) {
std::cout << member.getType() << std::endl;
}
int main() {
std::function<void(Base&)> callback;
callback = std::bind(onDerivedEvent, std::placeholders::_1);
callback(Derived(2));
callback = std::bind(onDerivedTwoEvent, std::placeholders::_1);
callback(DerivedTwo(3.0));
return 0;
}
Единственное, что я хотел бы изменить, это то, что onCallback()
должен принимать производного члена класса как аргумент вместо ссылки на базовый объект, поэтому я могу вызвать, например, getData()
.
В этом примере это будет означать:
void onCallback(Derived& derived);
Однако, если я сделаю это, я больше не смогу bind()
метод к callback
, потому что типы аргументов не совпадают.
Кто-нибудь знает, как заставить это работать?
// РЕДАКТИРОВАТЬ
Извините за путаницу, я обновил исходный код с некоторыми дополнительными подробностями и примерами, чтобы, возможно, уточнить, что я здесь делаю.
Примечание:
Поскольку кажется, что это очень актуально, вот конкретный c пример использования того, что я пытаюсь сделать здесь:
Это часть системы событий для движка я строю Есть предопределенные базовые события c, но он должен быть расширен с помощью более конкретных событий c пользователем, использующим этот механизм. Так что нет окончательного списка производных классов. Затем некоторый объект может подписаться на указанный тип события c и всякий раз, когда центральная шина событий получает такое событие, она вызывает все подписанные функции обратного вызова с событием в качестве аргумента. Причина, по которой я не добавляю функцию-единицу для всех в производном классе, заключается в том, что события могут использоваться несколькими способами.
Ответы на некоторые вопросы из комментариев:
Что должно произойти, если вы передаете onCallback объект, который не указан, c Производные &? (ie, добавьте Derived2, у которого есть doStuff2. Передайте его обратному вызову. Что вы хотите, чтобы произошло? Это не должно быть возможно.
Возможно, я этого не проверял и также имел вводящая в заблуждение информация в начале, которую я редактировал с тех пор. Тип переданного производного класса всегда известен заранее. Например: onKeyEvent
всегда будет получать объект KeyEvent, а не объект базового класса или любые другие производные варианты.
Однако переменная, с которой связана эта функция, должна иметь возможность хранить функции, которые принимают различные производные классы от Base
Это мое хранилище для всех событий:
std::map<EventType, std::list<std::function<void(const Event&)>>> listener_map;
Почему onCallback не метод в Base, который переопределяет Derived
Я ответил на это в комментарии. ...The reason i am not adding a one and for all handle function in the derived class is, the events an be used in multiple ways...
То есть, у меня может быть KeyEvent
, в котором есть данные для клавиша (какая клавиша нажата / отпущена / удержана) и функция (и) прослушивания может использовать эти данные для чего угодно (проверьте, не задана ли какая-либо указанная клавиша c) ssed, проверьте, нажата ли какая-либо случайная клавиша и т. д.) Некоторые другие события могут вообще не иметь никаких данных и просто уведомить слушателя о том, что что-то произошло или иметь несколько наборов данных и т. д. c.
Существует ли конечный ограниченный во время компиляции конечный список всех типов, которые являются производными от Base в любой точке вашего кода?
Теоретически да. Во время компиляции будет конечное число производных классов. Однако они могут отличаться для компиляции библиотеки и компиляции проекта с использованием этой библиотеки.