简体   繁体   中英

Calling JavaScript from C++ with node.js

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

the answer is yes, and here is a way to do it :

I have created a js file called index.js and here is the path to the file

D:\NodeJS\call_js_from_cpp 

it contains the following code

console.log("hello");

Now I have a C++ code that executes the following shell command and stores the output in the variable "output" (which is a string):

node D:\NodeJS\call_js_from_cpp\index.js

Here is the C++ code : (note that in the code, there are double \\ in the path so it becomes like this node D:\\NodeJS\\call_js_from_cpp\\index.js)

#include<iostream>
#include<fstream>
#include<string>
#include<cstdlib>
#include<sstream>

std::string ssystem (const char *command) {
  char tmpname [L_tmpnam];
  std::tmpnam ( tmpname );
  std::string scommand = command;
  std::string cmd = scommand + " >> " + tmpname;
  std::system(cmd.c_str());
  std::ifstream file(tmpname, std::ios::in );
  std::string result;
  if (file) {
   while (!file.eof()) result.push_back(file.get());
   file.close();
  }
  remove(tmpname);
  return result;
}

//for cygwin

int main(int argc, char *argv[])
{
   std::string bash = "node D:\\NodeJS\\call_js_from_cpp\\index.js";
   std::string in;
   std::string s = ssystem(bash.c_str());
   std::istringstream iss(s);
   std::string output;
   while ( std::getline(iss, output) )
    {
      std::cout << output;
    }

   return 0;
 }

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

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);
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM