简体   繁体   English

Node.js 在 C/C++ 中添加模块

[英]Node.js add on module in C/C++

I have been exploring writing add-on modules for node.js in C/C++.我一直在探索用 C/C++ 为 node.js 编写附加模块。 So far I have a simple add on working where I can call an add on function form JavaScript, it's very simple.到目前为止,我有一个简单的添加工作,我可以从 JavaScript 调用 function 的添加,这非常简单。 The routine is called hello , its passed a string and then returns the same string prefixed with Hello with a space between the two words.该例程称为hello ,它传递一个字符串,然后返回以Hello为前缀的相同字符串,两个单词之间有一个空格。

package.json : package.json

{
    "name":         "test",
    "version":      "2.0.0",
    "description":  "test description",
    "main":         "index.js",
    "gypfile":      true,
    "scripts": {
        "build":    "node-gyp rebuild",
        "clean":    "node-gyp clean"
    },
    "author": "Simon Platten",
    "license": "ISC",
    "devDependencies": {
        "node-gyp": "^7.1.2"
      },
    "dependencies": {
        "node-addon-api": "^3.1.0"
    }
}

binding.gyp :绑定.gyp

{
    "targets":[{
        "target_name":  "test",
        "cflags!":      ["-fno-exceptions"],
        "cflags_cc!":   ["-fno-exceptions"],
        "sources":      ["cpp/main.cpp"],
        "include_dirs": ["<!@(node -p \"require('node-addon-api').include\")"],
        "libraries":    [],
        "dependencies": ["<!(node -p \"require('node-addon-api').gyp\")"],
        "defines":      ["NAPI_DISABLE_CPP_EXCEPTIONS"],
    }]
}

main.cpp主文件

/**
 * File:    main.cpp
 * Notes:
 *  Work in progress for add on module for node.js 'test'
 * History:
 *  2021/03/11 Written by Simon Platten
 */
 #include <napi.h>
 #include <iostream>
 #include <string>
/**
 * Return "Hello " + user name passed back
 */
 Napi::String hello(const Napi::CallbackInfo& info) {
     Napi::Env env = info.Env();
     std::string strResult = "Hello ";
     strResult += info[0].ToString();
     return Napi::String::New(env, strResult);
 }
/**
 * Callback method when module is registered with Node.js
 */
 Napi::Object Init(Napi::Env env, Napi::Object exports) {
     exports.Set(
        Napi::String::New(env, "hello"),
        Napi::Function::New(env, hello)
     );
     return exports;
 }
 NODE_API_MODULE(test, Init);

index.js : index.js

const test = require("./build/Release/test");

console.log(test.hello("Simon"));

This works great, now I want to get a bit more adventurous.这很好用,现在我想更冒险一点。 I'm not sure of the correct terminology for this, but for example, when a socket is used in node there are all kinds of on callbacks or events that can be managed with a callback function.我不确定正确的术语,但是例如,当在节点中使用套接字时,可以使用回调 function 管理各种on回调或事件。 This is exactly what I want to achieve from my module, the module will be passed a large amount of data to process, I will return immediately whilst the data is processed by the module and when its complete and ready for the client I want to issue a notification to node.js in the form of an on style handler.这正是我想从我的模块中实现的,模块将传递大量数据进行处理,当模块处理数据时,我将立即返回,并且当它完成并准备好用于我要发布的客户端时以on样式处理程序的形式通知 node.js。

How?如何?

In JS:在 JS 中:

Create a class that inherits from EventEmitter .创建一个继承自 EventEmitter 的class Doing this you'll be able to listen to events from this class.这样做您将能够收听来自此 class 的事件。

If you call your addon's function with a callback from this class and in the callback you emit your event you can achieve what you want.如果您使用来自此 class 的回调调用插件的 function 并在回调中发出您的事件,您可以实现您想要的。

Something like this:像这样的东西:

const test = require("./build/Release/test");
const EventEmitter = require('events');

class Test extends EventEmitter {
    constructor() {
        super();
        this.addon = test;
    }

    test() {
        test.hello("Simon", (data) => {
            this.emit("testEvent", data);
        });
    }
}

const myTest = new Test();

myTest.on("testEvent", (data) => {
    console.log("testEvent says: ", data);
})

myTest.test();

In C++:在 C++ 中:

Based on the example found here , you can add an AsyncWorker that will process your data in the background and call a callback once finished.根据此处找到的示例,您可以添加一个 AsyncWorker,它将在后台处理您的数据并在完成后调用回调。

Like this:像这样:

#include <napi.h>
#include <iostream>
#include <string>
#include <chrono>
#include <thread>

using namespace Napi;

class TestWorker : public AsyncWorker {
public:
    TestWorker(Function& callback) : AsyncWorker(callback) {}

    void Execute() override {
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    void OnOK() override {
        HandleScope scope(Env());
        Callback().Call({String::New(Env(), "message from the addon")});
    }
};

You can provide this callback as an argument to your hello function along with your current argument "Simon".您可以将此回调作为参数提供给您的 hello function 以及您当前的参数“Simon”。

Your code extended with the callback argument and the starting of the AsyncWorker:您的代码使用回调参数和 AsyncWorker 的开头进行了扩展:

 Napi::String hello(const Napi::CallbackInfo& info) {
     Napi::Env env = info.Env();
     std::string strResult = "Hello ";
     strResult += info[0].ToString();
     Function cb = info[1].As<Function>();
     TestWorker* wk = new TestWorker(cb);
     wk->Queue();
     return Napi::String::New(env, strResult);
 }

If you are plan to create streaming while using event emitter concept then you may find this post interesting Streaming data into a Node.js C++ Addon with N-API如果您计划在使用事件发射器概念的同时创建流式传输,那么您可能会发现这篇文章很有趣Streaming data into a Node.js C++ Addon with N-API

If you need to do this as no blocking asynchronous operation then you may do this on a separate thread, for that you have to use napi_threadsafe_function .如果您需要将其作为无阻塞异步操作来执行,那么您可以在单独的线程上执行此操作,因为您必须使用napi_threadsafe_function Usage is bit complex, it is explained in detail in the StackOverflow post How to use napi_threadsafe_function for NodeJS Native Addon用法有点复杂,在 StackOverflow 帖子How to use napi_threadsafe_function for NodeJS Native Addon中有详细说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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