Как вернуть класс C ++ в NODE.JS - PullRequest
3 голосов
/ 18 января 2020

Настройка:
У меня есть приложение NODE.JS, которое должно выполнять некоторые вычисления с низкой задержкой
Я решил использовать N-API и node-gyp, чтобы включить собственный модуль C ++ для NODE.JS приложение

Текущее состояние:
Я дошел до того, что работает набор инструментов, я могу скомпилировать исходный код C ++ в двоичные файлы, включая двоичный модуль в приложении NODE.JS приложение NODE.JS выполняет
Я могу вызывать методы C ++ со стандартными типами NODE.JS из NODE.JS, и методы могут возвращать стандартные типы NODE.JS обратно в NODE.JS после их завершения с выполнением

Проблема:
Я не могу понять, как вернуть пользовательский тип / объект из C ++ в NODE.JS
В настоящее время я хочу вернуть в основном структура с несколькими типами, чтобы получить результат сложного синтаксического анализа обратно к NODE.JS за один NODE.JS вызов

вывод минимального кода:
Я сделал минимальную реализацию, чтобы продемонстрировать, куда я хочу делать. Если вы прокомментируете #define ENABLE_RETURN_CLASS, код использует только стандартные типы NODE.JS, и все работает. Под изображением вывода, показывающего цепочку инструментов и выполнение работ, как и предполагалось:
float works

Если вы оставите #define ENABLE_RETURN_CLASS, код не скомпилируется. Насколько я понимаю, он не понимает, как преобразовать объект C ++ в объект NODE.JS. Это ошибка: enter image description here

Минимальный код:
Инициализация NODE.JS приложения

npm init
npm install node-gyp --save-dev
npm install node-addon-api

Компиляция двоичных файлов C ++ в NODE.JS модуль

npm run build

Запустите приложение NODE.JS

node main.js

Код можно найти в этом хранилище:
https://github.com/OrsoEric/2020-01-18-Test-NODEJS-Return-Class
Я планирую обновить его, как только будет найдено решение.

Код класса, который я хочу вернуть в приложение NODE.JS:

my_class.h

namespace User
{
    //Class I want to return to NODE.JS
    class My_class
    {
        public:
            //Constructor
            My_class( void );
            //Public references
            float &my_float( void );
            int &my_int( void );
        private:
            //Private class vars
            float g_my_float;
            int g_my_int;
    };
}   //End namestace: User

my_class. cpp

#include <iostream>
//Class header
#include "my_class.h"

namespace User
{
    //Constructor
    My_class::My_class( void )
    {
        this -> g_my_float = (float)1.001;
        this -> g_my_int = (int)42;
    }
    //Public Reference
    float &My_class::my_float( void )
    {
        return this -> g_my_float;
    }
    //Public Reference
    int &My_class::my_int( void )
    {
        return this -> g_my_int;
    }
}   //End namestace: User

Код для привязки между C ++ и NODE.JS. #define ENABLE_RETURN_CLASS включает код, который возвращает класс. Экземпляр в этом примере является глобальной переменной.

node_bindings. cpp

//NODE bindings
#include <napi.h>
//C++ Class I want to return to NODE.JS
#include "my_class.h"

//Comment to disable the code that return the class instance
//#define ENABLE_RETURN_CLASS

//Instance of My_class I want to return to NODE.JS
User::My_class g_instance;

//Prototype of function called by NODE.JS that initializes this module
extern Napi::Object init(Napi::Env env, Napi::Object exports);
//Prototype of function that returns a standard type: WORKS
extern Napi::Number get_my_float(const Napi::CallbackInfo& info);

#ifdef ENABLE_RETURN_CLASS
//Prototype of function that returns My_class to NODE.JS: DOES NOT WORK!!!
extern Napi::Object get_my_class(const Napi::CallbackInfo& info);
#endif // ENABLE_RETURN_CLASS

//Initialize instance
Napi::Object init(Napi::Env env, Napi::Object exports)
{
    //Construct the instance of My_class I want to return to NODE.JS
    g_instance = User::My_class();
        //Register methods accessible from the outside in the NODE.JS environment
    //Return a standard type
    exports.Set( "get_my_float", Napi::Function::New(env, get_my_float) );
    #ifdef ENABLE_RETURN_CLASS
    //Return the whole class
    exports.Set( "get_my_class", Napi::Function::New(env, get_my_class) );
    #endif

    return exports;
}   //End function: init | Napi::Env | Napi::Object

//Interface between function and NODE.JS
Napi::Number get_my_float(const Napi::CallbackInfo& info)
{
    Napi::Env env = info.Env();
    //Check arguments
    if (info.Length() != 0)
    {
        Napi::TypeError::New(env, "ERR: Expecting no arguments").ThrowAsJavaScriptException();
    }
    //Get the return value
    float tmp = g_instance.my_float();
    //Return a NODE.JS number
    return Napi::Number::New(env, (float)tmp);
} //End Function: get_my_float | Napi::CallbackInfo&

#ifdef ENABLE_RETURN_CLASS
//Interface between function and NODE.JS
Napi::Object get_my_class(const Napi::CallbackInfo& info)
{
    Napi::Env env = info.Env();
    //Check arguments
    if (info.Length() != 0)
    {
        Napi::TypeError::New(env, "ERR: Expecting no arguments").ThrowAsJavaScriptException();
    }
    //Get the return value
    User::My_class tmp = g_instance;
    //Return a NODE.JS number
    return Napi::Object::New(env, (User::My_class)tmp);
} //End Function: get_my_float | Napi::CallbackInfo&
#endif // ENABLE_RETURN_CLASS

NODE_API_MODULE( My_cpp_module, init )

The NODE.JS application main. js включает и выполняет модуль C ++:

//Include native C++ module
const my_custom_cpp_module = require('./build/Release/MyCustomCppModule.node');
console.log('My custom c++ module',my_custom_cpp_module);

//TEST:
tmp = my_custom_cpp_module.get_my_float();
console.log( tmp );

module.exports = my_custom_cpp_module;

Привязки описаны в файле binding.gyp :

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

Пакет . json - это то, что NODE.JS необходимо решить зависимости, скомпилируйте и запустите

{
  "name": "2020-01-18-test-return-object",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "build": "node-gyp rebuild",
    "clean": "node-gyp clean"
  },
  "author": "",
  "license": "ISC",
  "gypfile": true,
  "devDependencies": {
    "node-gyp": "^6.1.0"
  },
  "dependencies": {
    "node-addon-api": "^2.0.0"
  }
}

SOLUTION

Я не могу подогнать собственный класс внутри Napi :: Object, но я могу создать пустой Napi :: Объект и создавать поля по одному. https://github.com/OrsoEric/2020-01-18-Test-NODEJS-Return-Class

Реализовать правильную конструкцию Napi :: Object внутри node_bindings. cpp

//NODE bindings
#include <napi.h>
//C++ Class I want to return to NODE.JS
#include "my_class.h"

//Comment to disable the code that return the class instance
#define ENABLE_RETURN_CLASS

//Instance of My_class I want to return to NODE.JS
User::My_class g_instance;

//Prototype of function called by NODE.JS that initializes this module
extern Napi::Object init(Napi::Env env, Napi::Object exports);
//Prototype of function that returns a standard type: WORKS
extern Napi::Number get_my_float(const Napi::CallbackInfo& info);

#ifdef ENABLE_RETURN_CLASS
//Prototype of function that returns My_class to NODE.JS: DOES NOT WORK!!!
extern Napi::Object get_my_class(const Napi::CallbackInfo& info);
#endif // ENABLE_RETURN_CLASS

//Initialize instance
Napi::Object init(Napi::Env env, Napi::Object exports)
{
    //Construct the instance of My_class I want to return to NODE.JS
    g_instance = User::My_class();
        //Register methods accessible from the outside in the NODE.JS environment
    //Return a standard type
    exports.Set( "get_my_float", Napi::Function::New(env, get_my_float) );
    #ifdef ENABLE_RETURN_CLASS
    //Return the whole class
    exports.Set( "get_my_class", Napi::Function::New(env, get_my_class) );
    #endif

    return exports;
}   //End function: init | Napi::Env | Napi::Object

//Interface between function and NODE.JS
Napi::Number get_my_float(const Napi::CallbackInfo& info)
{
    Napi::Env env = info.Env();
    //Check arguments
    if (info.Length() != 0)
    {
        Napi::TypeError::New(env, "ERR: Expecting no arguments").ThrowAsJavaScriptException();
    }
    //Get the return value
    float tmp = g_instance.my_float();
    //Return a NODE.JS number
    return Napi::Number::New(env, (float)tmp);
} //End Function: get_my_float | Napi::CallbackInfo&

#ifdef ENABLE_RETURN_CLASS
//Interface between function and NODE.JS
Napi::Object get_my_class(const Napi::CallbackInfo& info)
{
    Napi::Env env = info.Env();
    //Check arguments
    if (info.Length() != 0)
    {
        Napi::TypeError::New(env, "ERR: Expecting no arguments").ThrowAsJavaScriptException();
    }
    //Get a copy of the instance of the class I want to return
    User::My_class tmp = g_instance;
    //Construct empty return object in the NODE.JS environment
    Napi::Object ret_tmp = Napi::Object::New( env );
    //Manually create and fill the fields of the return object
    ret_tmp.Set("my_float", Napi::Number::New( env, (float)tmp.my_float() ));
    ret_tmp.Set("my_int", Napi::Number::New( env, (int)tmp.my_int() ));
    //Return a NODE.JS Object
    return (Napi::Object)ret_tmp;
} //End Function: get_my_class | Napi::CallbackInfo&
#endif // ENABLE_RETURN_CLASS

NODE_API_MODULE( My_cpp_module, init )

Добавить инструкцию по тестированию в main. js:

//Include native C++ module
const my_custom_cpp_module = require('./build/Release/MyCustomCppModule.node');
console.log('My custom c++ module',my_custom_cpp_module);

//TEST: Standard NODE.JS type
tmp = my_custom_cpp_module.get_my_float();
console.log( tmp );
//Custom NODE.JS type
class_tmp = my_custom_cpp_module.get_my_class();
console.log( class_tmp );

module.exports = my_custom_cpp_module;

Выход:
enter image description here

1 Ответ

3 голосов
/ 18 января 2020

Я думаю, как описано в Napi :: Object docs , вы не можете создать экземпляр объекта с пользовательским классом. Только примитивные значения. Поэтому я бы предложил создать пустой Napi::Object и использовать его Set для сопоставления значений.

Napi::Object ret = Napi::Object::New(env);

ret.Set("my_float", Napi::Number::New(env, (float)tmp.my_float()));

Заполните все поля и верните объект. Так же, как вы сделали с exports

...