Listener object is better than 3 lambdas + handle

This commit is contained in:
Ilari Liusvaara 2014-05-12 23:44:12 +03:00
parent d2334af286
commit 30d7196cd4
4 changed files with 188 additions and 121 deletions

View file

@ -10,9 +10,34 @@
namespace command
{
class set;
class base;
class factory_base;
/**
* Set add/drop listener.
*/
class set_listener
{
public:
/**
* Dtor.
*/
virtual ~set_listener();
/**
* New item in set.
*/
virtual void create(set& s, const std::string& name, factory_base& cmd) = 0;
/**
* Deleted item from set.
*/
virtual void destroy(set& s, const std::string& name) = 0;
/**
* Destroyed the entiere set.
*/
virtual void kill(set& s) = 0;
};
/**
* A set of commands.
*/
@ -38,20 +63,15 @@ public:
/**
* Add a notification callback and call ccb on all.
*
* Parameter ccb: The create callback function.
* Parameter dcb: The destroy callback function.
* Parameter tcb: The terminate callback function.
* Returns: Callback handle.
* Parameter listener: The listener to add.
*/
uint64_t add_callback(std::function<void(set& s, const std::string& name, factory_base& cmd)> ccb,
std::function<void(set& s, const std::string& name)> dcb, std::function<void(set& s)> tcb)
throw(std::bad_alloc);
void add_callback(set_listener& listener) throw(std::bad_alloc);
/**
* Drop a notification callback and call dcb on all.
*
* Parameter handle: The handle of callback to drop.
* Parameter listener: The listener to drop.
*/
void drop_callback(uint64_t handle) throw();
void drop_callback(set_listener& listener) throw();
/**
* Obtain list of all commands so far.
*/
@ -121,6 +141,17 @@ public:
*/
void set_oom_panic(void (*fn)());
private:
class listener : public set_listener
{
public:
listener(group& _grp);
~listener();
void create(set& s, const std::string& name, factory_base& cmd);
void destroy(set& s, const std::string& name);
void kill(set& s);
private:
group& grp;
} _listener;
std::set<std::string> command_stack;
std::map<std::string, std::list<std::string>> aliases;
std::ostream* output;

View file

@ -74,6 +74,32 @@ struct keyspec
};
class invbind_set;
class invbind_info;
/**
* Set add/drop listener.
*/
class set_listener
{
public:
/**
* Dtor.
*/
virtual ~set_listener();
/**
* New item in set.
*/
virtual void create(invbind_set& s, const std::string& name, invbind_info& ibinfo) = 0;
/**
* Deleted item from set.
*/
virtual void destroy(invbind_set& s, const std::string& name) = 0;
/**
* Destroyed the entiere set.
*/
virtual void kill(invbind_set& s) = 0;
};
/**
* Keyboard mapper. Maps keyboard keys into commands.
@ -215,6 +241,17 @@ private:
key* _key;
unsigned subkey;
};
class listener : public set_listener
{
public:
listener(mapper& _grp);
~listener();
void create(invbind_set& s, const std::string& name, invbind_info& ibinfo);
void destroy(invbind_set& s, const std::string& name);
void kill(invbind_set& s);
private:
mapper& grp;
} _listener;
void change_command(const keyspec& spec, const std::string& old, const std::string& newc);
void on_key_event(modifier_set& mods, key& key, event& event);
void on_key_event_subkey(modifier_set& mods, key& key, unsigned skey, bool polarity);
@ -224,7 +261,7 @@ private:
std::map<std::string, ctrlrkey*> ckeys;
std::map<triplet, std::string> bindings;
std::set<key*> listening;
std::map<invbind_set*, uint64_t> invbind_set_cbs;
std::set<invbind_set*> invbind_set_cbs;
keyboard& kbd;
command::group& domain;
bool dtor_running;
@ -257,13 +294,11 @@ public:
/**
* Add a callback on new invese bind.
*/
uint64_t add_callback(std::function<void(invbind_set& s, const std::string& name, invbind_info& ib)> ccb,
std::function<void(invbind_set& s, const std::string& name)> dcb,
std::function<void(invbind_set& s)> tcb) throw(std::bad_alloc);
void add_callback(set_listener& listener) throw(std::bad_alloc);
/**
* Drop a callback on new inverse bind.
*/
void drop_callback(uint64_t handle);
void drop_callback(set_listener& listener);
};
/**

View file

@ -65,13 +65,6 @@ namespace
exit(1);
}
struct set_callbacks
{
std::function<void(set& s, const std::string& name, factory_base& cmd)> ccb;
std::function<void(set& s, const std::string& name)> dcb;
std::function<void(set& s)> tcb;
};
threads::rlock* global_lock;
threads::rlock& get_cmd_lock()
{
@ -81,21 +74,24 @@ namespace
struct set_internal
{
std::map<uint64_t, set_callbacks> callbacks;
integer_pool pool;
std::set<set_listener*> callbacks;
std::map<std::string, factory_base*> commands;
};
struct group_internal
{
std::map<std::string, base*> commands;
std::map<set*, uint64_t> set_handles;
std::set<set*> set_handles;
};
typedef stateobject::type<set, set_internal> set_internal_t;
typedef stateobject::type<group, group_internal> group_internal_t;
}
set_listener::~set_listener()
{
}
void factory_base::_factory_base(set& _set, const std::string& cmd) throw(std::bad_alloc)
{
threads::arlock h(get_cmd_lock());
@ -161,10 +157,10 @@ set::~set() throw()
//Call all DCBs on all factories.
for(auto i : state->commands)
for(auto j : state->callbacks)
j.second.dcb(*this, i.first);
j->destroy(*this, i.first);
//Call all TCBs.
for(auto j : state->callbacks)
j.second.tcb(*this);
j->kill(*this);
//Notify all factories that base set died.
for(auto i : state->commands)
i.second->set_died();
@ -183,7 +179,7 @@ void set::do_register(const std::string& name, factory_base& cmd) throw(std::bad
state.commands[name] = &cmd;
//Call all CCBs on this.
for(auto i : state.callbacks)
i.second.ccb(*this, name, cmd);
i->create(*this, name, cmd);
}
void set::do_unregister(const std::string& name, factory_base& cmd) throw(std::bad_alloc)
@ -195,43 +191,30 @@ void set::do_unregister(const std::string& name, factory_base& cmd) throw(std::b
state->commands.erase(name);
//Call all DCBs on this.
for(auto i : state->callbacks)
i.second.dcb(*this, name);
i->destroy(*this, name);
}
uint64_t set::add_callback(std::function<void(set& s, const std::string& name, factory_base& cmd)> ccb,
std::function<void(set& s, const std::string& name)> dcb, std::function<void(set& s)> tcb)
void set::add_callback(set_listener& listener)
throw(std::bad_alloc)
{
threads::arlock h(get_cmd_lock());
auto& state = set_internal_t::get(this);
set_callbacks cb;
cb.ccb = ccb;
cb.dcb = dcb;
cb.tcb = tcb;
uint64_t i = state.pool();
try {
state.callbacks[i] = cb;
} catch(...) {
state.pool(i);
throw;
}
state.callbacks.insert(&listener);
//To avoid races, call CCBs on all factories for this.
for(auto j : state.commands)
ccb(*this, j.first, *j.second);
return i;
listener.create(*this, j.first, *j.second);
}
void set::drop_callback(uint64_t handle) throw()
void set::drop_callback(set_listener& listener) throw()
{
threads::arlock h(get_cmd_lock());
auto state = set_internal_t::get_soft(this);
if(!state) return;
if(state->callbacks.count(handle)) {
if(state->callbacks.count(&listener)) {
//To avoid races, call DCBs on all factories for this.
for(auto j : state->commands)
state->callbacks[handle].dcb(*this, j.first);
state->callbacks.erase(handle);
state->pool(handle);
listener.destroy(*this, j.first);
state->callbacks.erase(&listener);
}
}
@ -244,6 +227,7 @@ std::map<std::string, factory_base*> set::get_commands()
}
group::group() throw(std::bad_alloc)
: _listener(*this)
{
oom_panic_routine = default_oom_panic;
output = &std::cerr;
@ -264,7 +248,7 @@ group::~group() throw()
//Drop all callbacks.
for(auto i : state->set_handles)
i.first->drop_callback(i.second);
i->drop_callback(_listener);
//We assume all bases that need destroying have already been destroyed.
group_internal_t::clear(this);
}
@ -441,23 +425,10 @@ void group::add_set(set& s) throw(std::bad_alloc)
{
threads::arlock h(get_cmd_lock());
auto& state = group_internal_t::get(this);
if(state.set_handles.count(&s))
return;
state.set_handles[&s] = 0xFFFFFFFFFFFFFFFF;
if(state.set_handles.count(&s)) return;
try {
state.set_handles[&s] = s.add_callback(
[this](set& s, const std::string& name, factory_base& cmd) {
cmd.make(*this);
}, [this](set& s, const std::string& name) {
auto state = group_internal_t::get_soft(this);
if(!state) return;
state->commands.erase(name);
}, [this](set& s) {
auto state = group_internal_t::get_soft(this);
if(!state) return;
state->set_handles.erase(&s);
}
);
state.set_handles.insert(&s);
s.add_callback(_listener);
} catch(...) {
state.set_handles.erase(&s);
}
@ -468,10 +439,39 @@ void group::drop_set(set& s) throw()
threads::arlock h(get_cmd_lock());
auto state = group_internal_t::get_soft(this);
if(!state) return;
if(!state->set_handles.count(&s))
return;
//Drop the callback. This unregisters all.
s.drop_callback(state->set_handles[&s]);
s.drop_callback(_listener);
state->set_handles.erase(&s);
}
group::listener::listener(group& _grp)
: grp(_grp)
{
}
group::listener::~listener()
{
}
void group::listener::create(set& s, const std::string& name, factory_base& cmd)
{
threads::arlock h(get_cmd_lock());
cmd.make(grp);
}
void group::listener::destroy(set& s, const std::string& name)
{
threads::arlock h(get_cmd_lock());
auto state = group_internal_t::get_soft(&grp);
if(!state) return;
state->commands.erase(name);
}
void group::listener::kill(set& s)
{
threads::arlock h(get_cmd_lock());
auto state = group_internal_t::get_soft(&grp);
if(!state) return;
state->set_handles.erase(&s);
}

View file

@ -16,22 +16,19 @@ namespace
return *global_lock;
}
struct set_callbacks
{
std::function<void(invbind_set& s, const std::string& name, invbind_info& cmd)> ccb;
std::function<void(invbind_set& s, const std::string& name)> dcb;
std::function<void(invbind_set& s)> tcb;
};
struct set_internal
{
std::map<std::string, invbind_info*> invbinds;
std::map<uint64_t, set_callbacks> callbacks;
integer_pool pool;
std::set<set_listener*> callbacks;
};
typedef stateobject::type<invbind_set, set_internal> set_internal_t;
}
set_listener::~set_listener()
{
}
std::string mapper::fixup_command_polarity(std::string cmd, bool polarity) throw(std::bad_alloc)
{
if(cmd == "" || cmd == "*")
@ -166,7 +163,7 @@ keyboard& mapper::get_keyboard() throw()
}
mapper::mapper(keyboard& _kbd, command::group& _domain) throw(std::bad_alloc)
: kbd(_kbd), domain(_domain)
: _listener(*this), kbd(_kbd), domain(_domain)
{
register_queue<mapper, invbind>::do_ready(*this, true);
register_queue<mapper, ctrlrkey>::do_ready(*this, true);
@ -178,6 +175,8 @@ mapper::~mapper() throw()
threads::arlock u(get_keymap_lock());
for(auto i : ibinds)
i.second->mapper_died();
for(auto i : invbind_set_cbs)
i->drop_callback(_listener);
register_queue<mapper, invbind>::do_ready(*this, false);
register_queue<mapper, ctrlrkey>::do_ready(*this, false);
}
@ -412,21 +411,10 @@ std::list<ctrlrkey*> mapper::get_controllerkeys_kbdkey(key* kbdkey)
void mapper::add_invbind_set(invbind_set& set)
{
threads::arlock u(get_keymap_lock());
if(invbind_set_cbs.count(&set))
return;
invbind_set_cbs[&set] = 0xFFFFFFFFFFFFFFFF;
if(invbind_set_cbs.count(&set)) return;
try {
invbind_set_cbs[&set] = set.add_callback(
[this](invbind_set& s, const std::string& name, invbind_info& ib) {
ib.make(*this);
}, [this](invbind_set& s, const std::string& name) {
if(dtor_running) return;
ibinds.erase(name);
}, [this](invbind_set& s) {
if(dtor_running) return;
this->invbind_set_cbs.erase(&s);
}
);
invbind_set_cbs.insert(&set);
set.add_callback(_listener);
} catch(...) {
invbind_set_cbs.erase(&set);
}
@ -434,14 +422,41 @@ void mapper::add_invbind_set(invbind_set& set)
void mapper::drop_invbind_set(invbind_set& set)
{
threads::arlock u(get_keymap_lock());
if(!invbind_set_cbs.count(&set))
return;
threads::arlock h(get_keymap_lock());
//Drop the callback. This unregisters all.
set.drop_callback(invbind_set_cbs[&set]);
set.drop_callback(_listener);
invbind_set_cbs.erase(&set);
}
mapper::listener::listener(mapper& _grp)
: grp(_grp)
{
}
mapper::listener::~listener()
{
}
void mapper::listener::create(invbind_set& s, const std::string& name, invbind_info& ibinfo)
{
threads::arlock h(get_keymap_lock());
ibinfo.make(grp);
}
void mapper::listener::destroy(invbind_set& s, const std::string& name)
{
threads::arlock h(get_keymap_lock());
if(grp.dtor_running) return;
grp.ibinds.erase(name);
}
void mapper::listener::kill(invbind_set& s)
{
threads::arlock h(get_keymap_lock());
if(grp.dtor_running) return;
grp.invbind_set_cbs.erase(&s);
}
invbind::invbind(mapper& kmapper, const std::string& _command, const std::string& _name, bool dynamic)
throw(std::bad_alloc)
: _mapper(&kmapper), cmd(_command), oname(_name)
@ -533,10 +548,10 @@ invbind_set::~invbind_set()
//Call all DCBs on all factories.
for(auto i : state->invbinds)
for(auto j : state->callbacks)
j.second.dcb(*this, i.first);
j->destroy(*this, i.first);
//Call all TCBs.
for(auto j : state->callbacks)
j.second.tcb(*this);
j->kill(*this);
//Notify all factories that base set died.
for(auto i : state->invbinds)
i.second->set_died();
@ -555,7 +570,7 @@ void invbind_set::do_register(const std::string& name, invbind_info& info)
state.invbinds[name] = &info;
//Call all CCBs on this.
for(auto i : state.callbacks)
i.second.ccb(*this, name, info);
i->create(*this, name, info);
}
void invbind_set::do_unregister(const std::string& name, invbind_info& info)
@ -567,43 +582,29 @@ void invbind_set::do_unregister(const std::string& name, invbind_info& info)
state->invbinds.erase(name);
//Call all DCBs on this.
for(auto i : state->callbacks)
i.second.dcb(*this, name);
i->destroy(*this, name);
}
uint64_t invbind_set::add_callback(std::function<void(invbind_set& s, const std::string& name, invbind_info& ib)> ccb,
std::function<void(invbind_set& s, const std::string& name)> dcb,
std::function<void(invbind_set& s)> tcb) throw(std::bad_alloc)
void invbind_set::add_callback(set_listener& listener) throw(std::bad_alloc)
{
threads::arlock u(get_keymap_lock());
auto& state = set_internal_t::get(this);
set_callbacks cb;
cb.ccb = ccb;
cb.dcb = dcb;
cb.tcb = tcb;
uint64_t i = state.pool();
try {
state.callbacks[i] = cb;
} catch(...) {
state.pool(i);
throw;
}
state.callbacks.insert(&listener);
//To avoid races, call CCBs on all factories for this.
for(auto j : state.invbinds)
ccb(*this, j.first, *j.second);
return i;
listener.create(*this, j.first, *j.second);
}
void invbind_set::drop_callback(uint64_t handle)
void invbind_set::drop_callback(set_listener& listener)
{
threads::arlock u(get_keymap_lock());
auto state = set_internal_t::get_soft(this);
if(!state) return;
if(state->callbacks.count(handle)) {
if(state->callbacks.count(&listener)) {
//To avoid races, call DCBs on all factories for this.
for(auto j : state->invbinds)
state->callbacks[handle].dcb(*this, j.first);
state->callbacks.erase(handle);
state->pool(handle);
listener.destroy(*this, j.first);
state->callbacks.erase(&listener);
}
}