-
-
Save rioki/1290004d7505380f2b1d to your computer and use it in GitHub Desktop.
| // | |
| // Copyright (c) 2014 Sean Farrell | |
| // | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy | |
| // of this software and associated documentation files (the "Software"), to deal | |
| // in the Software without restriction, including without limitation the rights | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| // copies of the Software, and to permit persons to whom the Software is | |
| // furnished to do so, subject to the following conditions: | |
| // | |
| // The above copyright notice and this permission notice shall be included in | |
| // all copies or substantial portions of the Software. | |
| // | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| // SOFTWARE. | |
| // | |
| #include "EventEmitter.h" | |
| #include <stdexcept> | |
| EventEmitter::EventEmitter() {} | |
| EventEmitter::~EventEmitter() {} | |
| unsigned int EventEmitter::add_listener(unsigned int event_id, std::function<void ()> cb) | |
| { | |
| if (!cb) | |
| { | |
| throw std::invalid_argument("EventEmitter::add_listener: No callbak provided."); | |
| } | |
| std::lock_guard<std::mutex> lock(mutex); | |
| unsigned int listener_id = ++last_listener; | |
| listeners.insert(std::make_pair(event_id, std::make_shared<Listener<>>(listener_id, cb))); | |
| return listener_id; | |
| } | |
| unsigned int EventEmitter::on(unsigned int event_id, std::function<void ()> cb) | |
| { | |
| return add_listener(event_id, cb); | |
| } | |
| void EventEmitter::remove_listener(unsigned int listener_id) | |
| { | |
| std::lock_guard<std::mutex> lock(mutex); | |
| auto i = std::find_if(listeners.begin(), listeners.end(), [&] (std::pair<const unsigned int, std::shared_ptr<ListenerBase>> p) { | |
| return p.second->id == listener_id; | |
| }); | |
| if (i != listeners.end()) | |
| { | |
| listeners.erase(i); | |
| } | |
| else | |
| { | |
| throw std::invalid_argument("EventEmitter::remove_listener: Invalid listener id."); | |
| } | |
| } |
| // | |
| // Copyright (c) 2014 Sean Farrell | |
| // | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy | |
| // of this software and associated documentation files (the "Software"), to deal | |
| // in the Software without restriction, including without limitation the rights | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| // copies of the Software, and to permit persons to whom the Software is | |
| // furnished to do so, subject to the following conditions: | |
| // | |
| // The above copyright notice and this permission notice shall be included in | |
| // all copies or substantial portions of the Software. | |
| // | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| // SOFTWARE. | |
| // | |
| #ifndef _EVENT_EMITTER_H_ | |
| #define _EVENT_EMITTER_H_ | |
| #include <functional> | |
| #include <map> | |
| #include <memory> | |
| #include <mutex> | |
| #include <list> | |
| #include <algorithm> | |
| class EventEmitter | |
| { | |
| public: | |
| EventEmitter(); | |
| ~EventEmitter(); | |
| template <typename... Args> | |
| unsigned int add_listener(unsigned int event_id, std::function<void (Args...)> cb); | |
| unsigned int add_listener(unsigned int event_id, std::function<void ()> cb); | |
| template <typename... Args> | |
| unsigned int on(unsigned int event_id, std::function<void (Args...)> cb); | |
| unsigned int on(unsigned int event_id, std::function<void ()> cb); | |
| void remove_listener(unsigned int listener_id); | |
| template <typename... Args> | |
| void emit(unsigned int event_id, Args... args); | |
| private: | |
| struct ListenerBase | |
| { | |
| ListenerBase() {} | |
| ListenerBase(unsigned int i) | |
| : id(i) {} | |
| virtual ~ListenerBase() {} | |
| unsigned int id; | |
| }; | |
| template <typename... Args> | |
| struct Listener : public ListenerBase | |
| { | |
| Listener() {} | |
| Listener(unsigned int i, std::function<void (Args...)> c) | |
| : ListenerBase(i), cb(c) {} | |
| std::function<void (Args...)> cb; | |
| }; | |
| std::mutex mutex; | |
| unsigned int last_listener; | |
| std::multimap<unsigned int, std::shared_ptr<ListenerBase>> listeners; | |
| EventEmitter(const EventEmitter&) = delete; | |
| const EventEmitter& operator = (const EventEmitter&) = delete; | |
| }; | |
| template <typename... Args> | |
| unsigned int EventEmitter::add_listener(unsigned int event_id, std::function<void (Args...)> cb) | |
| { | |
| if (!cb) | |
| { | |
| throw std::invalid_argument("EventEmitter::add_listener: No callbak provided."); | |
| } | |
| std::lock_guard<std::mutex> lock(mutex); | |
| unsigned int listener_id = ++last_listener; | |
| listeners.insert(std::make_pair(event_id, std::make_shared<Listener<Args...>>(listener_id, cb))); | |
| return listener_id; | |
| } | |
| template <typename... Args> | |
| unsigned int EventEmitter::on(unsigned int event_id, std::function<void (Args...)> cb) | |
| { | |
| return add_listener(event_id, cb); | |
| } | |
| template <typename... Args> | |
| void EventEmitter::emit(unsigned int event_id, Args... args) | |
| { | |
| std::list<std::shared_ptr<Listener<Args...>>> handlers; | |
| { | |
| std::lock_guard<std::mutex> lock(mutex); | |
| auto range = listeners.equal_range(event_id); | |
| handlers.resize(std::distance(range.first, range.second)); | |
| std::transform(range.first, range.second, handlers.begin(), [] (std::pair<const unsigned int, std::shared_ptr<ListenerBase>> p) { | |
| auto l = std::dynamic_pointer_cast<Listener<Args...>>(p.second); | |
| if (l) | |
| { | |
| return l; | |
| } | |
| else | |
| { | |
| throw std::logic_error("EventEmitter::emit: Invalid event signature."); | |
| } | |
| }); | |
| } | |
| for (auto& h : handlers) | |
| { | |
| h->cb(args...); | |
| } | |
| } | |
| #endif |
Yes, you can find the details on the implementation and use under http://www.rioki.org/2014/12/29/eventemitter-in-c.html
But in your case, you need to specify the types:
emitter.on<int>(0, [](int err){
cout << "on error" <<1<< endl;
});
This is sort of dumb thing by C++. You either must specify the type on the template function or wrap it into a std::function object: C++'s template type inference does not work well when working with lambdas.
The solution (with on<int>) doesn't work for me (Xcode 6.2, LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn)). I copy & pasted the example from above and replaced the call with your suggestion.
The error is:
No matching member function for call to 'on'
Do you know how to solve this?
[EDIT] Nevermind, got it to work with a nice solution from Stack Overflow. Here if you're interested.
How do I return other values, besides int?
for example -
emitter.emit(0, "Test String");
Edit
In case anyone else runs into the problem the solution is :
EventEmitter emitter;
emitter.on<std::string>(0, [](std::string err) {
std::cout << err << std::endl;
});
std::string str = "pass string";
emitter.emit(0, str);
Or, you force the template type:
emitter.emit<std::string>(0, "pass string");
Hey @rioki, how can I register a non-static event handler? The code below can't be compiled
class Data: public EventEmitter{
public:
void activate(){
this->on(1, this->show); // -> error: invalid use of non-static member function
}
void show(const char* message){
cout << message << endl;
}
};Update
I've managed to invoke it correctly (with std::bind)
class Data: public EventEmitter{
public:
void activate(){
std::function<void(const char*)> handler = std::bind(&Data::show, this, std::placeholders::_1);
this->on(1, handler);
}
void show(const char* message){
cout << message << endl;
}
};@anhldbk Alternatively:
class Data: public EventEmitter
{
public:
void activate()
{
this->on(1, [this] (const char* message) {show(msg);});
}
void show(const char* message)
{
cout << message << endl;
}
};@anhldbk Alternatively:
class Data: public EventEmitter { public: void activate() { this->on(1, [this] (const char* message) {show(msg);}); } void show(const char* message) { cout << message << endl; } };
Mmm, that compiles for you? When I try that it says cannot convert from lambda to std::function.
i am build it on visual studio 2013,but can not work.