Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Петр Манохин: Приемы event driven programming н...

Avatar for ekbcpp ekbcpp
September 07, 2013
710

Петр Манохин: Приемы event driven programming на C++

Avatar for ekbcpp

ekbcpp

September 07, 2013
Tweet

Transcript

  1. 2 Введение • Что такое event driven programming • Преимущества

    • Логическое разделение компонентов • Повторное использование генераторов событий • Уменьшение количества излишних действий • Event vs polling
  2. 4 Способ №1 class EventGenerator { public: void generateEvent(); virtual

    void eventReaction()=0; }; void EventGenerator::generateEvent() { //... eventReaction(); //... }
  3. 5 Способ №1 • Проблемы • связанность • создание нескольких

    обработчиков • невозможность влиять на создание класса • Но используется • как шаблонный метод
  4. 6 Способ №2 class EventGenerator { public: class EventHandler {

    public: virtual void reaction()=0; }; void generateEvent(); EventHandler* handler; }; void EventGenerator::generateEvent() { //... event->reaction(); //... }
  5. 7 Способ №2 • Уже лучше • создание обработчика независимо

    • возможность подключения нескольких обработчиков • Но • связанность • излишний код для обработчика • отсутствие универсального решения для нескольких обработчиков
  6. 8 Как это используется void setHandler() { class Handler: public

    EventGenerator::EventHandler { public: virtual void rection() { reactor->doJob(); } Reactor* reactor; }; eventGenerator->setHandler(new Handler(reactor)); }
  7. 9 Как хотелось бы void setHandler() { connect(eventGenerator->event,reactor,&Reactor::doJob); // or

    connect(eventGenerator->event,doJob); } void EventGenerator::generateEvent() { //... event(); //... }
  8. 10 Обычный callback class EventGenerator { public: typedef void (*Callback)();

    void generateEvent(); Callback reaction; }; void EventGenerator::generateEvent() { //... reaction(); //... }
  9. 11 Обычный callback • Преимущества • независимость компонентов • Проблемы

    • множественные обработчики • один обработчик для множества одинаковых сущностей
  10. 12 «Closure» callback class EventGenerator { public: typedef void (*Callback)(void*);

    void generateEvent(); void* callbackData; Callback reaction; }; void EventGenerator::generateEvent() { //... reaction(callbackData); //... }
  11. 13 «Closure» callback • Стандартное решение для plain C •

    Недостатки • необходимость хранить параметр callback`а • не типобезопасный
  12. 14 «Silver Bullet» • Обобщенный callback или functor • более

    легковесный • задел на функциональное программирование • Объект-событие • легкое подключение/отключение обработчиков
  13. 15 Интерфейс functor template <class Arg1=NT, class Arg2=NT> class Func;

    template <> class Func<NT,NT> { public: virtual void operator () ()=0; }; template <class Arg1> class Func<Arg1,NT> { public: virtual void operator () (Arg1)=0; }; template <class Arg1, class Arg2> class Func { public: virtual void operator () (Arg1,Arg2)=0; };
  14. 16 Указатель на функцию template <class F> class FreeFunc; template

    <class Arg1> class FreeFunc<typename void (*)(Arg1)>: public Func<Arg1>{ public: typedef typename Func<Arg1> Base; typedef typename void (*Callee)(Arg1); virtual void operator () (Arg1 arg1){callee(arg1);} Callee callee; }; template <class F> std::auto_ptr<FreeFunc<Func>::Base> wrap(F callee) { return new FreeFunc<Func>(callee); }
  15. 17 Указатель на метод template <class F> class MethodFunc; template

    <class Host, class Arg1> class MethodFunc<typename void (Host::*)(Arg1)>: public Func<Arg1> { typedef typename Func<Arg1> Interface; typedef typename void (Host::*Method)(Arg1); virtual void operator () (Arg1 arg1){ (host->*method)(arg1); } std::auto_ptr<Host> host; Method method; }; template <class Host, class Method> std::auto_ptr<MethodFunc<Func>::Interface> wrap(Host host, Method method) { return new MethodFunc<Func>(host,method); }
  16. 18 Несколько обработчиков template <class F> class MultiFunc; template <class

    Arg1> class MultiFunc<typename Func<Arg1> >: public Func<Arg1> { public: virtual void operator () (Arg1 arg1) { (*f1)(arg1); (*f2)(arg1); } std::auto_ptr<Func<Arg1> > f1; std::auto_ptr<Func<Arg1> > f2; };
  17. 19 События template <class Arg1> class Event<Arg1>: public Func<Arg1> {

    public: virtual void operator () (Arg1 arg1) { //foreach in handlers { (*each)(arg1); } } std::container<std::auto_ptr<Func<Arg1> > > handlers; };
  18. 20 Асинхронная обработка • Можно вызывать обработчики • не сразу

    • в другом потоке • в другом процессе • на другом хосте • Ценой • организации активной выборки (главный цикл) • временного сохранения параметров события
  19. 21 Event loop void main() { while (alive) { if

    (checkForEvents()) { processEvents(); } else { wait(); } } }
  20. 23 Интерфейс потока class Stream; Stream& operator << (Stream& stream,

    int value); Stream& operator << (Stream& stream, float value); //... Stream& operator >> (Stream& stream, int& value); Stream& operator >> (Stream& stream, float& value); //...
  21. 24 Передатчик template <class Arg1, class Arg2> class Sender<Arg1,Arg2>: public

    Func<Arg1,Arg2> { public: virtual void operator () (Arg1 arg1, Arg2 arg2){ getStream() << messageId << arg1 << arg2; } MessageId messageId; };
  22. 25 Приемник class Receiver { public: virtual void receive(Stream& stream)=0;

    }; class Dispathcer { public: void processMessages() { Stream& stream=getStream(); MessageId messageId; stream >> messageId; // find receiver receiver->receive(stream); } std::map<MessageId,std::auto_ptr<Receiver> > receivers; };
  23. 26 Приемник template <class Arg1> class FuncReceiver<typename Func<Arg> >: public

    Receiver { public: virtual void receive(Stream& stream) { Arg1 arg1; stream >> arg1; (*callee)(arg1); } std::auto_ptr<Func<Arg1> > callee; };
  24. 27 Потокобезопасный буфер • lock free (защита через volatile) •

    закольцован • необходимо сохранять размер сообщения • для чтения/записи реализует интерфейс Stream
  25. 28 Полезный прием • Хочется • регистрировать по строке •

    иметь быстрый (O(1)) доступ • Как сделать • назначать индекс на отправляющей стороне • Регистрация на принимающей стороне только в обработчике
  26. 29 «Живой» пример server->onSwitchChange=wrap( client, &ClientRails::setSwitchState ); server->onSwitchChange= MessageSender<uint32,uint32>::create( netServer->sender,

    "switchChange" ); netClient->addHandler( "switchChange", createReceiver( wrap(client,&ClientRails::setSwitchState) ) );