├── .gitignore ├── .travis.yml ├── test ├── index.dSYM │ └── Contents │ │ ├── Resources │ │ └── DWARF │ │ │ └── index │ │ └── Info.plist └── index.cxx ├── package.json ├── LICENSE ├── README.md └── index.hxx /.gitignore: -------------------------------------------------------------------------------- 1 | /test/index 2 | 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | script: build run test 4 | 5 | compiler: clang 6 | 7 | os: 8 | - linux 9 | - osx 10 | 11 | -------------------------------------------------------------------------------- /test/index.dSYM/Contents/Resources/DWARF/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypercore-cxx/cxx-eventemitter/HEAD/test/index.dSYM/Contents/Resources/DWARF/index -------------------------------------------------------------------------------- /test/index.dSYM/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleIdentifier 8 | com.apple.xcode.dsym.index 9 | CFBundleInfoDictionaryVersion 10 | 6.0 11 | CFBundlePackageType 12 | dSYM 13 | CFBundleSignature 14 | ???? 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleVersion 18 | 1 19 | 20 | 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eventemitter", 3 | "description": "EventEmitter for C++", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/datcxx/cxx-eventemitter.git" 7 | }, 8 | "homepage": "https://github.com/datcxx/cxx-eventemitter", 9 | "dependencies": { 10 | }, 11 | "scripts": { 12 | "test": [ 13 | "clang++ -std=c++2a", 14 | "-O1 -g -fsanitize=address -fno-omit-frame-pointer", 15 | "test/index.cxx -o test/index &&", 16 | "ASAN_OPTIONS=detect_leaks=1", 17 | "ASAN_OPTIONS=fast_unwind_on_malloc=0", 18 | "./test/index" 19 | ] 20 | }, 21 | "files": [ 22 | "index.hxx" 23 | ], 24 | "keywords": [ 25 | "event", 26 | "listener", 27 | "eventemitter", 28 | "emitter", 29 | "observe", 30 | "subscribe" 31 | ], 32 | "author": { 33 | "name": "Paolo Fragomeni", 34 | "email": "paolo@async.ly", 35 | "url": "http://async.ly" 36 | }, 37 | "license": "MIT" 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Paolo Fragomeni 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SYNOPSIS 2 | A minimalist event emitter for `C++`. 3 | 4 | # DESCRIPTION 5 | This emitter allows you to emit and receive arbitrary/variadic paramaters of 6 | equal type. Captures are also allowed. 7 | 8 | # EXAMPLE 9 | 10 | ```c++ 11 | #include "deps/datcxx/cxx-eventemitter/index.h" 12 | 13 | int main() { 14 | 15 | EventEmitter ee; 16 | 17 | ee.on("hello", [&](string name, int num) { 18 | // ...do something with the values. 19 | }); 20 | 21 | ee.emit("hello", "beautiful", 100); 22 | } 23 | ``` 24 | 25 | # USAGE 26 | This module is designed to work with the [`datcxx`][0] build tool. To add this 27 | module to your project us the following command... 28 | 29 | ```bash 30 | build add datcxx/cxx-eventemitter 31 | ``` 32 | 33 | # TEST 34 | 35 | ```bash 36 | build test 37 | ``` 38 | 39 | # API 40 | 41 | ## INSTANCE METHODS 42 | 43 | ### `void` on(string eventName, lambda callback); 44 | Listen for an event multiple times. 45 | 46 | ### `void` once(string eventName, lambda callback); 47 | Listen for an event once. 48 | 49 | ### `void` emit(string eventName [, arg1 [, arg2...]]); 50 | Emit data for a particular event. 51 | 52 | ### `void` off(string eventName); 53 | Remove listener for a particular event. 54 | 55 | ### `void` off(); 56 | Remove all listeners on the emitter. 57 | 58 | ### `int` listeners(); 59 | Number of listeners an emitter has. 60 | 61 | ## INSTANCE MEMBERS 62 | 63 | ### maxListeners 64 | Number of listeners an event emitter should have 65 | before considering that there is a memory leak. 66 | 67 | [0]:https://github.com/datcxx/build 68 | -------------------------------------------------------------------------------- /test/index.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../index.hxx" 3 | 4 | #define ASSERT(message, ...) do { \ 5 | if(!(__VA_ARGS__)) { \ 6 | std::cerr << "FAIL: " << message << std::endl; \ 7 | exit(0);\ 8 | } \ 9 | else { \ 10 | std::cout << "OK: " << message << std::endl; \ 11 | testcount++; \ 12 | } \ 13 | } while(0); 14 | 15 | int main () { 16 | int testcount = 0; 17 | int testplan = 9; 18 | 19 | // 20 | // sanity test. 21 | // 22 | ASSERT("sanity: true is true", true == true); 23 | 24 | EventEmitter ee; 25 | 26 | ASSERT("maxListeners should be 10", ee.maxListeners == 10); 27 | 28 | ee.on("event1", [&](int a, std::string& b) { 29 | ASSERT("first arg should be equal to 10", a == 10); 30 | ASSERT("second arg should be foo", b == "foo"); 31 | }); 32 | 33 | ee.emit("event1", 10, (std::string) "foo"); 34 | 35 | try { 36 | ee.on("event1", []() {}); 37 | } 38 | catch(...) { 39 | ASSERT("duplicate listener", true); 40 | } 41 | 42 | ee.on("event2", [&]() { 43 | ASSERT("no params", true); 44 | }); 45 | 46 | ee.emit("event2"); 47 | 48 | int count1 = 0; 49 | ee.on("event3", [&]() { 50 | if (++count1 == 10) { 51 | ASSERT("event was emitted correct number of times", true); 52 | } 53 | }); 54 | 55 | for (int i = 0; i < 10; i++) { 56 | ee.emit("event3"); 57 | } 58 | 59 | int count2 = 0; 60 | ee.on("event4", [&](){ 61 | count2++; 62 | if (count2 > 1) { 63 | ASSERT("event was fired after it was removed", false); 64 | } 65 | }); 66 | 67 | ee.emit("event4"); 68 | ee.off("event4"); 69 | ee.emit("event4"); 70 | ee.emit("event4"); 71 | 72 | int count3 = 0; 73 | ee.on("event5", [&]() { 74 | count3++; 75 | ASSERT("events were fired even though all listeners were removed", false); 76 | }); 77 | 78 | ASSERT("the correct number of listeners has been reported", ee.listeners() == 4); 79 | 80 | ee.off(); 81 | 82 | ASSERT("no additional events fired after all listeners removed", 83 | count1 == 10 && 84 | count2 == 1 && 85 | count3 == 0 86 | ); 87 | 88 | ASSERT("testcount == plan", testcount == testplan) 89 | } 90 | -------------------------------------------------------------------------------- /index.hxx: -------------------------------------------------------------------------------- 1 | #ifndef __EVENTS_H_ 2 | #define __EVENTS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class EventEmitter { 12 | std::map events; 13 | std::map events_once; 14 | 15 | template 16 | struct traits : public traits { 17 | }; 18 | 19 | template 20 | struct traits { 21 | 22 | typedef std::function fn; 23 | }; 24 | 25 | template 26 | typename traits::fn 27 | to_function (Callback& cb) { 28 | 29 | return static_cast::fn>(cb); 30 | } 31 | 32 | int _listeners = 0; 33 | 34 | public: 35 | int maxListeners = 10; 36 | 37 | int listeners() { 38 | return this->_listeners; 39 | } 40 | 41 | template 42 | void on(const std::string& name, Callback cb) { 43 | 44 | auto it = events.find(name); 45 | if (it != events.end()) { 46 | throw new std::runtime_error("duplicate listener"); 47 | } 48 | 49 | if (++this->_listeners >= this->maxListeners) { 50 | std::cout 51 | << "warning: possible EventEmitter memory leak detected. " 52 | << this->_listeners 53 | << " listeners added. " 54 | << std::endl; 55 | }; 56 | 57 | auto f = to_function(cb); 58 | auto fn = new decltype(f)(to_function(cb)); 59 | events[name] = static_cast(fn); 60 | } 61 | 62 | template 63 | void once(const std::string& name, Callback cb) { 64 | this->on(name, cb); 65 | events_once[name] = true; 66 | } 67 | 68 | void off() { 69 | events.clear(); 70 | events_once.clear(); 71 | this->_listeners = 0; 72 | } 73 | 74 | void off(const std::string& name) { 75 | 76 | auto it = events.find(name); 77 | 78 | if (it != events.end()) { 79 | events.erase(it); 80 | this->_listeners--; 81 | 82 | auto once = events_once.find(name); 83 | if (once != events_once.end()) { 84 | events_once.erase(once); 85 | } 86 | } 87 | } 88 | 89 | template 90 | void emit(std::string name, Args... args) { 91 | 92 | auto it = events.find(name); 93 | if (it != events.end()) { 94 | 95 | auto cb = events.at(name); 96 | auto fp = static_cast*>(cb); 97 | (*fp)(args...); 98 | } 99 | 100 | auto once = events_once.find(name); 101 | if (once != events_once.end()) { 102 | this->off(name); 103 | } 104 | } 105 | 106 | EventEmitter(void) {} 107 | 108 | ~EventEmitter (void) { 109 | events.clear(); 110 | } 111 | }; 112 | 113 | #endif 114 | 115 | --------------------------------------------------------------------------------