Skip to content

Instantly share code, notes, and snippets.

@fridgerator
Created September 27, 2019 13:47
Show Gist options
  • Save fridgerator/789283ae01f55e9ae9b771357450e471 to your computer and use it in GitHub Desktop.
Save fridgerator/789283ae01f55e9ae9b771357450e471 to your computer and use it in GitHub Desktop.
Native node module event emitter
#include <nan.h>
#include "Foo.h"
void InitAll(v8::Local<v8::Object> exports) {
Foo::Init(exports);
}
NODE_MODULE(bindings, InitAll)
{
"targets": [
{
"target_name": "hello",
"sources": [
"src/addon.cc", "src/Foo.cc"
],
"include_dirs": [
"<!(node -e \"require('nan')\")"
]
}
]
}
// https://github.com/gerhardberger/addon-emitter
#ifndef EMITTER_H
#define EMITTER_H
#include <nan.h>
#include <map>
#include <vector>
#include <iostream>
namespace internal {
using CP = Nan::CopyablePersistentTraits<v8::Function>::CopyablePersistent;
} // namespace internal
class Emitter : public Nan::ObjectWrap {
public:
Emitter () {}
~Emitter () {}
template<typename... Args>
void Emit (v8::Local<v8::Value> info, const Args&... args) {
v8::String::Utf8Value name(info->ToString());
if (m[std::string(*name)].empty()) return;
std::vector<v8::Local<v8::Value>> converted_args = { args... };
for (std::vector<internal::CP>::iterator it = m[std::string(*name)].begin();
it != m[std::string(*name)].end(); ++it) {
Nan::MakeCallback(Nan::GetCurrentContext()->Global()
, Nan::New(*it)
, converted_args.size(), &converted_args.front());
}
}
static void On (const Nan::FunctionCallbackInfo<v8::Value>& info) {
if (!info[0]->IsString() || !info[1]->IsFunction()) return;
Emitter *emitter = ObjectWrap::Unwrap<Emitter>(info.Holder());
v8::String::Utf8Value name(info[0]->ToString());
v8::Local<v8::Function> cb = info[1].As<v8::Function>();
Nan::Persistent<v8::Function> persistent_cb;
persistent_cb.Reset(cb);
emitter->m[std::string(*name)].push_back(persistent_cb);
}
private:
std::map<std::string, std::vector<internal::CP> > m;
};
#endif
#include "Foo.h"
Nan::Persistent<v8::Function> Foo::constructor;
Foo::Foo () { }
Foo::~Foo () { }
void Foo::Init(v8::Local<v8::Object> exports) {
Nan::HandleScope scope;
// Prepare constructor template
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
tpl->SetClassName(Nan::New("Foo").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
// Prototype
Nan::SetPrototypeMethod(tpl, "bar", Bar);
Nan::SetPrototypeMethod(tpl, "on", Emitter::On); // Emitter's prototype method
constructor.Reset(tpl->GetFunction());
exports->Set(Nan::New("Foo").ToLocalChecked(), tpl->GetFunction());
}
void Foo::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
if (info.IsConstructCall()) {
Foo *foo = new Foo();
foo->Wrap(info.This());
info.GetReturnValue().Set(info.This());
} else {
Nan::ThrowError("nope");
}
}
void Foo::Bar(const Nan::FunctionCallbackInfo<v8::Value>& info) {
Foo *foo = ObjectWrap::Unwrap<Foo>(info.Holder());
foo->Emit(Nan::New("sus").ToLocalChecked(), Nan::New("hi").ToLocalChecked(), Nan::New(6));
}
#ifndef FOO_H
#define FOO_H
#include <nan.h>
#include "emitter.h"
class Foo : public Emitter {
public:
static void Init(v8::Local<v8::Object> exports);
private:
explicit Foo ();
~Foo ();
static void New (const Nan::FunctionCallbackInfo<v8::Value>& info);
static void Bar (const Nan::FunctionCallbackInfo<v8::Value>& info);
static Nan::Persistent<v8::Function> constructor;
};
#endif
var Foo = require('./build/Release/hello').Foo
var foo = new Foo()
// subscribing to an event
foo.on('sus', function () {
console.log('Received:', arguments);
})
setInterval(function () {
// this method fires the `sus` event on the native side
foo.bar()
}, 1000)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment