23

A quick google search yields at least one tutorial for writing a C++ "Hello World" for node.js, but it's unclear if it's possible to write such an extension using only C. Assuming it is possible, what challenges / limitations would I face?

hippietrail
  • 15,848
  • 18
  • 99
  • 158
noahlz
  • 10,202
  • 7
  • 56
  • 75

6 Answers6

18

You can write parts of your extension in C if you want, but you'll need at least a small bit of C++ code to glue together your C code with Node.

As you will have seen in your HelloWorld, extensions rely on the v8.h and node.h headers, which have all of the classes that Node expects. Without those, you won't be able to properly create the JS object to export back to Node.

That said, you can pretty easily just write a small set of C++ functions that just call C functions, and wrap some kind of C structure.

loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
8

Found this on Hacker News:

https://github.com/wesolows/v8plus

v8+: Node addon C++ to C boundary

This layer offers a way to write at least simple Node addons in C without all the horrible C++ goop you'd otherwise be expected to use. That goop still exists, but you don't have to write it. More importantly, you can write your module in a sane programming environment, avoiding the confusing and error-prone C++ semantics.

noahlz
  • 10,202
  • 7
  • 56
  • 75
6

Now we have at least 3 good options:

node-ffi: Node.js Foreign Function Interface
addon for loading and calling dynamic libraries using pure JavaScript. It can be used to create bindings to native libraries without writing any C code
https://github.com/node-ffi/node-ffi

SWIG: Simplified Wrapper and Interface Generator
(it generates wrappers for many languages, what solves many problems at once)
http://www.swig.org/

emscripten
Compiles C and C++ into highly-optimizable JavaScript that runs even on the web at near-native speed, without plugins.
http://kripken.github.io/emscripten-site/

Bernardo Ramos
  • 4,048
  • 30
  • 28
5

Need to declare the individual C function in your C++ code using the extern "C" syntax

Example:

#define BUILDING_NODE_EXTENSION
#include <node.h>

extern "C" void f(int i, char c, float x);

using namespace v8;

if you have multiple C functions, then it can be grouped via braces:

extern "C" {
  void   f(int i, char c, float x);
  int    g(char* s, char const* s2);
  double sqrtOfSumOfSquares(double a, double b);
}

then call the function from the C++ function:

Handle<Value> MyFunction(const Arguments& args) {
  HandleScope scope;
   f(7, 'x', 3.14);    // <--- 
  return scope.Close(String::New("Hello"));
}

Handle<Value> CreateFunction(const Arguments& args) {
  HandleScope scope;

  Local<FunctionTemplate> tpl = FunctionTemplate::New(MyFunction);
  Local<Function> fn = tpl->GetFunction();
  fn->SetName(String::NewSymbol("theFunction")); // omit this to make it anonymous

  return scope.Close(fn);
}

void Init(Handle<Object> target) {
  target->Set(String::NewSymbol("createFunction"),
      FunctionTemplate::New(CreateFunction)->GetFunction());
}


NODE_MODULE(addon, Init)

Note: Using sample code from Nodejs Addons

AzizSM
  • 6,199
  • 4
  • 42
  • 53
2

If your module uses libuv you can link it to the node executable. It exports the libuv functions as a shared library does.

Then you can use node-ffi to interface with it (no C++ knowledge needed here).

Here is how I made it on Windows, using MSVS:

  • Create a new DLL solution in MSVS
  • Download the libuv and copy the include and the lib files to the MSVS
  • Download the node.lib file and put it in the lib folder of MSVS
  • Compile the example source below that adds a timer to the main event loop

testlib.c:

#include <stdio.h>
#include <stdlib.h>
#include "uv.h"

void (*p_callback)(int number, char *text);

void timer_cb1 (uv_timer_t* timer, int status) {
  printf("libuv timer here\n", status);
  p_callback(123, "it worked!");
}

void set_timer (int interval, void *pfunction) {
  uv_loop_t *loop;
  uv_timer_t *timer1;

  printf("set_timer called. interval=%d callback=%p\n", interval, pfunction);

  p_callback = pfunction;

  printf("uv_version_string = %s\n", uv_version_string());

  loop = uv_default_loop();
  if (loop == 0) {
    puts("could not get the reference to the default loop");
    return;
  }

  puts("got the default loop. now allocating the timer struct");

  timer1 = (uv_timer_t *) malloc(sizeof(uv_timer_t));
  if (timer1 == 0) {
    puts("malloc failed");
    return;
  }

  puts("initializing timer");
  uv_timer_init(loop, timer1);

  puts("starting timer");
  uv_timer_start(timer1, (uv_timer_cb) &timer_cb1, interval, interval);

  puts("timer created. returning");

}

use the testlib.def:

EXPORTS set_timer

And remember to link to the node.lib

  • Move the created dll to the test folder and run these commands there:

npm install ffi (currently the build tools are required. check the instructions)

node test-lib.js

test-lib.js is here:

var ffi = require('ffi');

var testlib = ffi.Library('testlib', {
  'set_timer': [ 'void', [ 'int', 'pointer' ] ]
});

var callback = ffi.Callback('void', ['int', 'string'],
  function(number, text) {
    console.log("javascript callback here!!!  number=" + number + "  text=" + text);
  }
);

console.log('registering the callback...');
testlib.set_timer(500, callback);
console.log('done')

Use your imagination. We have networking, worker threads, and other options inside libuv...

Bernardo Ramos
  • 4,048
  • 30
  • 28
1

The code that directly interacts with node.js needs to written in C++.

You could write extern "C" wrappers using opaque types for everything you need from node.h and v8.h, but it's probably easier to just use C++ instead (which, of course, can call out to C code).

Christoph
  • 164,997
  • 36
  • 182
  • 240
  • hmm, how has been `stream` module realized? I've seen its source has written on js, but I didn't see any special things that might be connected with C or C++. Do you know something about it? – MaximPro Oct 09 '22 at 08:04