eventemitter seems to work

This commit is contained in:
raub 2018-03-06 17:53:43 +03:00
parent f4ca876cc2
commit 0f9ada3680
5 changed files with 100 additions and 84 deletions

View File

@ -30,6 +30,7 @@ dependency packages.
[Cross-platform commands](#cross-platform-commands)
[Class EventEmitter](#class-eventemitter)
---
@ -559,3 +560,22 @@ For Windows the `/y` flag was embedded.
...
'action' : ['<(cp)', 'a', 'b'],
```
---
## class EventEmitter
A C++ implementation of [Events API](https://nodejs.org/api/events.html).
An example of it's usage can be found in **examples/node-addon** directory.
There is `Example` class, implemented in **cpp/example.cpp**, that inherits
EventEmitter behavior and is exported to JS.
For C++ side `EventEmitter` has following public methods:
* `void emit(const std::string &name, int argc = 0, v8::Local<v8::Value> *argv = NULL)`
emits an event with the given `name` and, optionally, some additional arguments where
`argc` is the number of arguments and `argv` is a pointer to the arguments array.
* `void on(const std::string &name, v8::Local<v8::Value> that, const std::string &method)`
subscribes `that[method]` to receive `name` events from this emitter, basically
`emitter.on(name, that[method])`.

View File

@ -1,3 +1,13 @@
'use strict';
module.exports = require('./binary/addon');
const util = require('util');
const { Example } = require('./binary/addon');
Example.prototype[util.inspect.custom] = function() {
return `Example { listeners: [${this.eventNames()}] }`;
};
module.exports = Example;

View File

@ -1,12 +1,13 @@
'use strict';
const { Example } = require('./core');
const Example = require('./core');
console.log('Example', Example);
const example = new Example();
console.log('example 0', example);
console.log('static listenerCount', Example.listenerCount);
@ -26,16 +27,15 @@ console.log('setMaxListeners', example.setMaxListeners);
console.log('rawListeners', example.rawListeners);
console.log('destroy', example.destroy);
// console.log('example.eventNames -2', example.eventNames());
example.on('evt1', (arg1, arg2) => {
console.log('EVT1', arg1, arg2, example.eventNames());
});
// console.log('example.eventNames -1', example.eventNames());
example.once('evt2', (arg1, arg2) => {
console.log('EVT2', arg1, arg2, example.eventNames());
});
// console.log('example.eventNames 0', example.eventNames());
example.emit('evt1', 111, '221');
example.emit('evt1', 112, '222');
@ -48,6 +48,8 @@ console.log('example.eventNames 2', example.eventNames());
example.emit('evt2', 112, '222');
// console.log('example.eventNames 3', example.eventNames());
console.log('example 1', example);
module.exports = Example;

View File

@ -6,15 +6,14 @@
#include <map>
#include <deque>
#include <iostream>
// #include <iostream> -> std::cout << "..." << std::endl;
#define THIS_EMITTER \
EventEmitter *emitter = ObjectWrap::Unwrap<EventEmitter>(info.This());
// Use the dummy template for static-int initialization
// Use the dummy template class for static-int initialization
template< class Dummy >
class EventEmitterStatics {
protected:
@ -36,48 +35,6 @@ class EventEmitter : public Nan::ObjectWrap, public EventEmitterStatics<void> {
typedef FNMAP_TYPE::iterator FNMAP_IT_TYPE;
public:
// C++ side emit() method
void emit(const std::string &name, int argc = 0, v8::Local<v8::Value> *argv = NULL) {
VEC_TYPE list = _listeners[name];
if (list.empty()) {
return;
}
for (IT_TYPE it = list.begin(); it != list.end(); ++it) {
Nan::Callback callback(Nan::New(*it));
if ( ! callback.IsEmpty() ) {
callback.Call(argc, argv);
}
}
}
// C++ side on() method
void on(const std::string &name, v8::Local<v8::Value> that, const std::string &method) {
v8::Local<v8::String> code = JS_STR(
"((emitter, name, that, method) => emitter.on(name, that[method]))"
);
v8::Local<v8::Function> connector = v8::Local<v8::Function>::Cast(v8::Script::Compile(code)->Run());
Nan::Callback connectorCb(connector);
v8::Local<v8::Object> emitter = Nan::New<v8::Object>();
this->Wrap(emitter);
v8::Local<v8::Value> argv[] = { emitter, JS_STR(name.c_str()), that, JS_STR(method.c_str()) };
connectorCb.Call(4, argv);
}
protected:
EventEmitter () {
@ -115,11 +72,58 @@ protected:
Nan::SetMethod(ctorFunc, "listenerCount", jsStaticListenerCount);
ACCESSOR_RW(ctorObj, defaultMaxListeners);
//Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
// ACCESSOR_RW(proto, type);
}
public:
// C++ side emit() method
void emit(const std::string &name, int argc = 0, v8::Local<v8::Value> *argv = NULL) {
// Important! As actual get map[key] produces a new (empty) map entry
if ( _listeners.find(name) == _listeners.end() ) {
return;
}
// A copy is intended, because handlers can call removeListener (and they DO)
VEC_TYPE list = _listeners[name];
if (list.empty()) {
return;
}
for (IT_TYPE it = list.begin(); it != list.end(); ++it) {
Nan::Callback callback(Nan::New(*it));
if ( ! callback.IsEmpty() ) {
callback.Call(argc, argv);
}
}
}
// C++ side on() method
void on(const std::string &name, v8::Local<v8::Value> that, const std::string &method) {
v8::Local<v8::String> code = JS_STR(
"((emitter, name, that, method) => emitter.on(name, that[method]))"
);
v8::Local<v8::Function> connector = v8::Local<v8::Function>::Cast(v8::Script::Compile(code)->Run());
Nan::Callback connectorCb(connector);
v8::Local<v8::Object> emitter = Nan::New<v8::Object>();
this->Wrap(emitter);
v8::Local<v8::Value> argv[] = { emitter, JS_STR(name.c_str()), that, JS_STR(method.c_str()) };
connectorCb.Call(4, argv);
}
protected:
// Deprecated static method
static NAN_METHOD(jsStaticListenerCount) {
@ -137,73 +141,53 @@ protected:
static NAN_SETTER(defaultMaxListenersSetter) { THIS_EMITTER; SETTER_INT32_ARG;
EventEmitter::_defaultMaxListeners = v;
_defaultMaxListeners = v;
}
static NAN_GETTER(defaultMaxListenersGetter) { THIS_EMITTER;
RET_VALUE(JS_INT(EventEmitter::_defaultMaxListeners));
RET_VALUE(JS_INT(_defaultMaxListeners));
}
static NAN_METHOD(jsAddListener) { THIS_EMITTER;
REQ_UTF8_ARG(0, name);
REQ_FUN_ARG(1, cb);
Nan::Persistent<v8::Function> persistentCb;
persistentCb.Reset(cb);
emitter->_listeners[std::string(*name)].push_back(persistentCb);
}
static NAN_METHOD(jsAddListener) { _wrapListener(info); }
static NAN_METHOD(jsEmit) { THIS_EMITTER;
std::cout << "emit 0 " << emitter->_listeners.size() << std::endl;
for (MAP_IT_TYPE it = emitter->_listeners.begin(); it != emitter->_listeners.end(); ++it) {
std::cout << "emit 0 _listeners " << it->first << std::endl;
}
for (MAP_IT_TYPE it = emitter->_raw.begin(); it != emitter->_raw.end(); ++it) {
std::cout << "emit 0 _raw " << it->first << std::endl;
}
REQ_UTF8_ARG(0, name);
int length = info.Length();
std::vector< v8::Local<v8::Value> > args;
std::cout << "emit 1 " << emitter->_listeners.size() << std::endl;
for (int i = 1; i < length; i++) {
args.push_back(info[i]);
}
std::cout << "emit 2 " << emitter->_listeners.size() << std::endl;
emitter->emit(*name, length - 1, &args[0]);
std::cout << "emit 3 " << emitter->_listeners.size() << std::endl;
}
static NAN_METHOD(jsEventNames) { THIS_EMITTER;
std::cout << "jsEventNames 0 " << emitter->_raw.size() << std::endl;
v8::Local<v8::Array> jsNames = Nan::New<v8::Array>(emitter->_raw.size());
if (emitter->_raw.empty()) {
RET_VALUE(jsNames);
return;
}
std::cout << "jsEventNames 1 " << emitter->_raw.size() << std::endl;
int i = 0;
for (MAP_IT_TYPE it = emitter->_raw.begin(); it != emitter->_raw.end(); ++it, i++) {
jsNames->Set(JS_INT(i), JS_STR(it->first));
}
std::cout << "jsEventNames 2 " << emitter->_raw.size() << std::endl;
RET_VALUE(jsNames);
}

View File

@ -2,7 +2,7 @@
"name": "addon-tools-raub",
"author": "Luis Blanco <raubtierxxx@gmail.com>",
"description": "A set of extra tools for Node.js addons",
"version": "0.0.8",
"version": "0.0.9",
"main": "index.js",
"keywords": [
"node",