Тема избитая, однако попробуем поднять планку ещё выше, т.е. ещё меньше "синтаксического шума" для пользователя.
Задача, что бы для пользователя это выглядело следующим образом:
// описываем класс сообщения
struct my_msg : msg<my_msg>
{
int data;
my_msg(int data) : data(data) {}
};
// описываем класс получателя
class my_agent : public agent<my_agent>
{
public:
my_agent(int data) : data(data) {}
void on_event(event<my_msg> ev)
{
std::cout << "on_event: my_agent::data=" << data << ", my_msg::data=" << ev.msg->data << std::endl;
}
private:
int data;
};
// собственно всё, дальше можем отправлять полиморфные сообщения полиморфному получателю
// т.е. ни тип сообщения, ни тип получателя при отправке уже не нужны
int main()
{
my_agent d (57);
my_msg m (113);
agent_base* b = &d;
msg_base* mb = &m;
b->send(*mb);
}
Всё, что требуется для регистрации обработчика, — это отнаследовать получателя от CRTP базы и объявить метод обработчика, который принимает специальный объект event<msg_t> (этот event помимо самого сообщения может так же содержать отправителя сообщения и другую контекстную информацию).
Ниже приводится реализация. Большая часть особого интереса не представляет, т.к. занимается раздачей уникальных идентификаторов типам сообщений и типам получателей. Наибольший интерес представляет класс agent<>::event<>, который как раз и регистрирует обработчик с помощью "конструктора класса" (
http://www.rsdn.ru/forum/message/3089286.1.aspxАвтор: remark
Дата: 03.09.08
). Суть приёма — метод класса регистрируется за счёт "упоминания" класса event<msg>; при этом класс получателя определяется автоматически за счёт того, что event<> не глобальный класс, а определен в CRTP базе (в противном случае пришлось бы писать void on_event(event<
my_agent, my_msg> ev)).
std::map<int, std::map<int, void(*)(void*, void const*)> > handlers;
struct msg_base
{
virtual int get_id() const = 0;
protected:
static int id_seq_;
};
struct agent_base
{
virtual int get_id() const = 0;
void send(msg_base const& m)
{
handlers[get_id()][m.get_id()](this, &m);
}
protected:
static int id_seq_;
};
template<typename derived_t>
class agent : public agent_base
{
protected:
template<typename msg_t>
struct event : class_initializer<event<msg_t> >
{
msg_t const* msg;
agent_base* sender;
private:
friend class class_initializer<event<msg_t> >;
static void static_ctor()
{
handlers[derived_t::static_id()][msg_t::static_id()] = &agent::thunk<msg_t>;
}
};
template<typename msg_t>
static void thunk(void* d, void const* m)
{
event<msg_t> ev;
ev.msg = static_cast<msg_t const*>(static_cast<msg_base const*>(m));
ev.sender = 0; // просто для примера
static_cast<derived_t*>(static_cast<agent_base*>(d))->on_event(ev);
}
static int id_;
virtual int get_id() const
{
return static_id();
}
public:
static int static_id()
{
if (id_ == 0)
id_ = ++agent_base::id_seq_;
return id_;
}
};
template<typename derived_t>
class msg : public msg_base
{
private:
static int id_;
virtual int get_id() const
{
return static_id();
}
public:
static int static_id()
{
if (id_ == 0)
id_ = ++msg_base::id_seq_;
return id_;
}
};
// определения статических членов (в порядке появления):
int msg_base::id_seq_;
int agent_base::id_seq_;
template<typename derived_t> int agent<derived_t>::id_;
template<typename derived_t> int msg<derived_t>::id_;
Если требуется наследование классов получателей/сообщений, то это решение необходимо скрестить с CRTP v3.0 (
http://www.rsdn.ru/forum/message/2677311.1.aspxАвтор: remark
Дата: 02.10.07
).
Единственная засада, которая пока видится, — если класс получателя шаблонный, то он не сможет называть event<T> просто как event<T>, т.к. он будет сидеть в шаблонной базе (не актуально для MSVC — она это позволяет). Решить можно добавлением "using agent<my_agent<T> >::event" в объявление шаблонного получателя.