Storing module and exports in C++ module

41 views
Skip to first unread message

gabriel....@intel.com

unread,
Apr 26, 2015, 8:48:34 PM4/26/15
to nod...@googlegroups.com
Hi!

I am writing node.js bindings for a C library. Some of the C API takes callback function pointers, so my corresponding JS API will accept JS callbacks. I intend to use a hybrid JS/C++ solution to achieve running the JS callback whenever the C callback gets called. Let's say the C API has the following signature:

void c_api_function( int flags, void (*callback)() );

Let's further say that the name of my module is cApiBindings (i.e., you will load the module by doing require( "bindings" )( "cApiBindings" ) ), and that I intend to expose the above function as cApiBindings.c_api_function(). I was thinking about achieving this as follows:

------------------------------------------------------------------------------------------------------
1. The JS side:
var cApiBindings = require( "bindings" )( "cApiBindings" );

cApiBindings.callbacks = {};

cApiBindings.callbackUuid = 0;

cApiBindings.c_api_function( flags, callback ) {
  var uuid = cApiBindings.callbackUuid++;

  cApiBindings.callbacks[ uuid ] = callback;

  return cApiBindings._partial_c_api_function( flags, uuid );
};

IOW, store the callback in an array of currently active callbacks, and pass the location of the JS callback to the C++ side.

2. The C++ side, containing the crux of the problem:

void default_c_api_callback( int uuid ) {
  Local<Function> callback = Local<Function>::Cast(
    ?????->Get( String::NewFromUtf8( "callbacks" ) )
      ->Get( uuid ) );

  /* Execute the JS callback thus retrieved */
}

void bind__partial_c_api_function(  const FunctionCallbackInfo<Value>& args ) {
  int flags = args[ 0 ]->ToUint32(),
    uuid = args[ 1 ]->ToUint32();

  /* Create a closure using libffi so that uuid ends up getting passed to default_c_api_callback() as defined above */
  void (*closure)() = create_closure( default_c_api_callback, uuid );

  c_api_function( flags, closure );
}

void Init( Handle<Object> exports, Handle<Object> module ) {
  NODE_SET_METHOD( exports, "_partial_c_api_function", bind__partial_c_api_function );
}

NODE_MODULE( cApiBindings, Init )
------------------------------------------------------------------------------------------------------

As you can see, in the C callback I need a way of retrieving the module onto which I've defined all the bindings, and onto which the JS side has defined the callbacks hash. Where does node keep this reference? For JS modules, it's in a parameter passed into the outermost IIFE, so it's always within the scope of the module.

I've tried to simply define a global variable

Handle<Object> module_exports;

and set it inside Init() as

module_exports = exports;

but that is useless, because attempting to access members of module_exports from the C callback via module_exports->Get(...) results in a segault. So, is there a correct way of storing a reference to module and/or exports that gets passed into Init() in a global variable for later use by the C callback?

Can I somehow work my way from Isolate::GetCurrent()?

I have successfully assigned the module object to a key on the global object inside Init():

Isolate::GetCurrent()->GetCurrentContext()->Global()
  ->ForceSet( String::NewFromUtf8( Isolate::GetCurrent(), "cApiBindings" ), module,
    ( PropertyAttribute )( ReadOnly | DontEnum | DontDelete ) );

I can then retrieve the module off the global object later on. Great, but I'd rather not pollute the global object. I'd much rather store module in a global variable in the source file instead. Is there a correct way of doing that?

TIA for your help,



Gabriel

gabriel....@intel.com

unread,
Apr 27, 2015, 11:03:27 AM4/27/15
to nod...@googlegroups.com
Hey!

Hurray for rubber duck problem solving!

...
Persistent<Object> module_exports;
...
void default_c_api_callback( int uuid ) {
    Local<Function> callback = Local<Function>::Cast(
      Local<Object>::New( module_exports )
        ->Get( String::NewFromUtf8( Isolate::GetCurrent(), "callbacks" ) )
          -> Get( uuid ) );
}
...
void Init( Handle<Object> exports, Handle<Object> module ) {
module_exports.Reset( Isolate::GetCurrent(), exports );
...
}

NODE_MODULE( cApiBindings, Init )

HTH,



Gabriel

Kevin Ingwersen (Ingwie Phoenix)

unread,
Apr 27, 2015, 11:03:52 AM4/27/15
to nod...@googlegroups.com
Hey.

Have you considered getting „this“ from the „args“ parameter? This would allow you to store callbacks in the local module. That is how I handled the issue of storing a void* in the current instance.

It is ages ago that I coded with v8, but I think what you want is this:

Local<Object> _this = args.getHolder();

Please forgive me, but I pretty much forgot all about the API ^^“

Using the method I suggest is equivalent of this JS:

cApiBinding.prototype.call_func = function( uuid ) {
var cbs = this.callbacks; // <-
_c_lib_function( uuid, cbs[uuid] );
}

Hope it helped!

Kind regards, Ingwie
Reply all
Reply to author
Forward
0 new messages