Upgrading C++ addons that inherit from EventEmitter

976 views
Skip to first unread message

mscdex

unread,
Aug 1, 2011, 2:35:48 AM8/1/11
to nodejs
As you may or may not have known, in node v0.5.2, EventEmitter on the C
++ side went bye bye.

This change affected one of my bindings, node-ncurses, and FWIW I
thought I'd share the commit that shows how I went about solving the
issue (take a look mainly at ncurses.js and ncurses.cc):
https://github.com/mscdex/node-ncurses/commit/d5c8fc1651c37483bd27288b67a37dcd7eab26c4

Astro

unread,
Aug 15, 2011, 8:19:46 PM8/15/11
to nod...@googlegroups.com

Ouch! That is a lot of code performing magic.

node-expat is faced with the same problem. Instead of building a JS
object extending library like you did here, I'm thinking about
require("events") and subclassing that... oh wait, C++ needs the
superclass at compile time.

Is there really no simple solution to this silly API breakage?


Astro

mscdex

unread,
Aug 15, 2011, 8:47:41 PM8/15/11
to nodejs
On Aug 15, 8:19 pm, Astro <as...@spaceboyz.net> wrote:
> Ouch! That is a lot of code performing magic.

Here's a summary of my changes:

1. Change "#include <node_events.h>" to "#include
<node_object_wrap.h>"
2. Change C++ class inheritance lines, such as in the declaration from
"class Foo : public EventEmitter" to "class Foo : public ObjectWrap"
and in any constructors from "Foo() : EventEmitter()" to "Foo() :
ObjectWrap()"
3. Remove "t->Inherit(EventEmitter::constructor_template);" from
inside your Initialize function where t is your C++ class's function
template
4. Remove any persistent symbols used for event names and create a
persistent symbol for emitting events by including "static
Persistent<String> emit_symbol;" outside the class declaration and
doing "emit_symbol = NODE_PSYMBOL("emit");" inside your Initialize
function.
5. Wherever you were previously emitting via "instance-
>Emit(event_symbol, 2, data)", change it to something like:
Local<Value> emit_v = handle_->Get(emit_symbol);
if (!emit_v->IsFunction()) return;
Local<Function> emit = Local<Function>::Cast(emit_v);
TryCatch tc;
emit->Call(handle_, 3, vChr);
if (tc.HasCaught()) {
// propagate error
}
6. Lastly, create a little javascript module that modifies your
existing C++ class, that looks like:
var EventEmitter = require('events').EventEmitter,
addon = require('./foo_addon');

// extend the original prototype with that of EventEmitter's
// the extend() I use here is simply the jquery extend()
extend(true, addon.Window.prototype, EventEmitter.prototype);
module.exports = addon;


That's about it. You could also probably wrap the new C++ emitting
code into your own private Emit function to make it more DRY
obviously.

Also, if you change your wscript to use a different basename for
your .node filename, you can then make the basename of the new
javascript module to be that of the previous .node basename (i.e.
foo.node -> foo_addon.node, foo.js). This should allow no breakage of
existing code and make the upgrade seamless, since most people will
should already just be doing require('foo').

mscdex

unread,
Aug 15, 2011, 9:04:06 PM8/15/11
to nodejs
On Aug 15, 8:47 pm, mscdex <msc...@gmail.com> wrote:
>      // extend the original prototype with that of EventEmitter's
>      // the extend() I use here is simply the jquery extend()
>      extend(true, addon.Window.prototype, EventEmitter.prototype);

First, this should've been addon.Foo.prototype not
addon.Window.prototype (that's what I get for so much copying and
pasting)
Secondly, as suggested by TooTallNate, you could probably also simply
instead do: addon.Foo.prototype.__proto__ = EventEmitter.prototype;

Bradley Meck

unread,
Aug 15, 2011, 9:21:09 PM8/15/11
to nod...@googlegroups.com
WTB require(v8::String) inside addon init.

mscdex

unread,
Aug 15, 2011, 9:34:54 PM8/15/11
to nodejs
On Aug 15, 9:21 pm, Bradley Meck <bradley.m...@gmail.com> wrote:
> WTB require(v8::String) inside addon init.

Huh?

Bradley Meck

unread,
Aug 15, 2011, 9:38:30 PM8/15/11
to nod...@googlegroups.com
Having access to require other modules via the require function when loading would be nice. Same as having the require function in a JS module. Having simple access to some server niceties as well as utilities such as EventEmitter / url.parse . Though the issue would be making requiring of other modules in C++ would need to handle not found errors manually.

Ben Noordhuis

unread,
Aug 15, 2011, 9:45:44 PM8/15/11
to nod...@googlegroups.com

It's the same technique: look up the require function in the global
context and call it.

Isaac Schlueter

unread,
Aug 15, 2011, 9:52:02 PM8/15/11
to nod...@googlegroups.com
The require function doesn't exist in the global context. It's a free
variable passed to the JS closure. I'm not sure how we'd go about
making that work the same way with the process.dlopen call.

C++ modules aren't really for doing complex things that need to be
strewn across multiple modules. Just get your binding done as quick
as possible, get out of there, and then wrap it in JS for all the
fancy stuff.

> --
> Job Board: http://jobs.nodejs.org/
> Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
> You received this message because you are subscribed to the Google
> Groups "nodejs" group.
> To post to this group, send email to nod...@googlegroups.com
> To unsubscribe from this group, send email to
> nodejs+un...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/nodejs?hl=en?hl=en
>

Oleg Efimov (Sannis)

unread,
Sep 20, 2011, 5:47:22 PM9/20/11
to nod...@googlegroups.com
Thank you, Brian.
Reply all
Reply to author
Forward
0 new messages