#ifndef _library__dispatch__hpp__included__ #define _library__dispatch__hpp__included__ #include #include #include #include #include #include "threadtypes.hpp" mutex_class& dispatch_global_init_lock(); template struct dispatcher; template struct dispatch_target { dispatch_target() { src = NULL; fn = dummy; } inline ~dispatch_target(); inline void set(dispatcher& d, std::function _fn); private: static void dummy(T... args) {}; void set_source(dispatcher* d) { src = d; } void call(T... args) { fn(args...); } dispatcher* src; std::function fn; friend class dispatcher; }; template struct dispatcher { dispatcher(const char* _name) { init(); name = _name; } ~dispatcher() { delete _targets; delete lck; lck = NULL; } void operator()(T... args) { init(); uint64_t k = 0; typename std::map*>::iterator i; lck->lock(); i = targets().lower_bound(k); while(i != targets().end()) { k = i->first + 1; dispatch_target* t = i->second; lck->unlock(); try { t->call(args...); } catch(std::exception& e) { (*errstrm) << name << ": Error in handler: " << e.what() << std::endl; } lck->lock(); i = targets().lower_bound(k); } lck->unlock(); } void connect(dispatch_target& target) { init(); umutex_class h(*lck); targets()[next_cbseq++] = ⌖ target.set_source(this); } void disconnect(dispatch_target& target) { init(); if(!lck) return; umutex_class h(*lck); for(auto i = targets().begin(); i != targets().end(); i++) if(i->second == &target) { targets().erase(i); break; } target.set_source(NULL); } void errors_to(std::ostream* to) { errstrm = to ? to : &std::cerr; } private: void init() { if(inited) return; umutex_class h(dispatch_global_init_lock()); if(inited) return; errstrm = &std::cerr; next_cbseq = 0; name = "(unknown)"; _targets = NULL; lck = new mutex_class; inited = true; } mutex_class* lck; std::map*>& targets() { if(!_targets) _targets = new std::map*>; return *_targets; } std::map*>* _targets; uint64_t next_cbseq; std::ostream* errstrm; const char* name; bool inited; dispatcher(const dispatcher&); dispatcher& operator=(const dispatcher&); }; template dispatch_target::~dispatch_target() { if(src) src->disconnect(*this); } template void dispatch_target::set(dispatcher& d, std::function _fn) { fn = _fn; src = &d; if(src) src->connect(*this); } #endif