23

Is there a way to call JS functions from C++ through node.js (as callbacks or something like that)? If yes, how? I'm searching for it on the web, but haven't found any helpful resource.

Thanks in advance

3

2 Answers 2

6

One way to do it form a native addon can be using the provided function as a callback, for example let's gonna assume that you have a function named setPrintFunction() declared in your native environment (A native addon):

(Call this for example main.cc)

#include <node.h>
#include <string>

v8::Persistent<v8::Function> fn;

// Call this at any time, but after the capture!
void printToNode(std::string msg) {
  auto isolate = fn->GetIsolate();
  // This part is the one that transforms your std::string to a javascript
  // string, and passes it as the first argument:
  const unsigned argc = 1;
  auto argv[argc] = {
      v8::String::NewFromUtf8(isolate,
                          msg.c_str(),
                          v8::NewStringType::kNormal).ToLocalChecked()
  };
  cb->Call(context, Null(isolate), argc, argv).ToLocalChecked();
}

// This is your native function that captures the reference
void setPrintFunction(const v8::FunctionCallbackInfo<Value>& args) {
  auto isolate = args.GetIsolate();
  auto context = isolate->GetCurrentContext();
  auto cb = v8::Local<v8::Function>::Cast(args[0]);
  fn = v8::Persistent<v8::Function>::New(cb);
}

// This part exports the function
void Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {
  NODE_SET_METHOD(module, "exports", setPrintFunction);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Init)

Then, just importing your addon and using it like:

(Call this for example index.js)

const { setPrintFunction } = require('<your path to .node file>');

function printNodeMsg(msg) {
  console.log('<msg>: ' + msg);
}

setPrintFunction(printNodeMsg);

So what you're basically doing is capturing the reference to the v8::Function (Which is the javascript function, but in the native environment) and then invoking it and passing "Hello World!" as the first (and unique) parameter.

More on the subject: https://nodejs.org/api/addons.html

Sign up to request clarification or add additional context in comments.

5 Comments

Can you add an example how to use the solution in C++? In this case passing an std::string to the C++ function, which will be passed to its Javascript counterpart.
I think I get what you want: For example could be a function that captures a reference to another function and then, at your will, make a function in the native part that invokes it. That could be a possible solution?
Well, when I was asking this question, I wanted to know the following: Let's say I have a JS function printNodeMsg(msg) and I want to call it and pass the msg parameter to it from C++. The addon is not a problem, but I wanted to be able to pass parameters, not just invoking a function. So basically calling C++ function void printNodeMsg(const std::string& msg) would call prontNodeMsg(msg) on the JS site, passing the specified string data.
Isn't this solution opposite to what OP's questions is?
@barath the OP asked how to call a JavaScript-created function from a C++ context, but using node. So as far as I can understand, the OP wants a C/C++ procedure that can be supplied with a JS function so it can be crafted from a JS context, but used later during the course of the C++ program.
-3

Of course you can. For example, if you want to write a simple factorial function in C++, you could do something like

#include <node.h>

using namespace v8;

int factorial(int n) {
    if (n == 0) return 1;
    else return n * factorial(n - 1);
}

void Factorial(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope(isolate);

    if (args.Length() != 2) {
        isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong number of arguments")));
    } else {
        if (!(args[0]->IsNumber() && args[1]->IsFunction())) {
            isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments type")));
        } else {
            int result = factorial(args[0]->Int32Value());

            Local<Function> callbackFunction = Local<Function>::Cast(args[1]);
            const unsigned argc = 1;
            Local<Value> argv[argc] = { Number::New(isolate, result) };

            callbackFunction->Call(isolate->GetCurrentContext()->Global(), argc, argv);
        }
    }
}

void Init(Handle<Object> exports) {
    NODE_SET_METHOD(exports, "factorial", Factorial);
}

NODE_MODULE(Factorial, Init)

And in your JavaScript file, call it like this

var factorialAddon = require('./addons/Factorial');
factorialAddon.factorial(5, function (result) {
    console.log(result);
});

1 Comment

he meant the other way around ... from cpp call js

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.