js calls so through ffi

1, Install ffi (depending on gcc environment)

Project open source address: GitHub - node-ffi/node-ffi: Node.js Foreign Function Interface

1. Installation: NPM install node gyp

2. Installation: npm install node FFI. If the installation fails with this method, add the following configuration in package.json, and then execute npm install in the project root path, as shown in the following figure:

3. Check whether ffi is successfully installed, and create a new test file, ffitest.js   

const ffi = require('ffi-napi');
console.log("ffi >>> " , ffi);

4. Execute the command: node ffitest.js. If no error is reported, the installation is successful

5. Prepare a so file, write a program in c language, create a file ffitest.c, and write the code as follows

#include<stdio.h>
//Multiply two numbers
int add(int a , int b)
{
        printf("add call \n");
        return a + b;
}

Then compile it into so file, compile the command: gcc -shared ffitest.c -o ffitest.so -fPIC -ldl, and then you will get the file, ffitest.so

2, js calls so

1. Call parameterless function

(1) Write a parameterless function in ffitest.c, and then recompile it. The code is as follows:

char* getMsg()
{
        printf("getMsg call \n");
        return "hello world";
}

(2) Write JS code and file ffitest.js. The code is as follows:

const ffi = require('ffi-napi');
const libs = ffi.Library("./ffitest.so" , {
        "getMsg":["string" , []]
});
const msg = libs.getMsg();
console.log("getMsg return = " , msg);

(3) Execute the command: node ffitest.js, and output the following information, indicating that the call is successful:

 

2. Call the parameter transfer function with parameter value, taking the transfer of int as an example,

(1) Compile a function with parameter type int in ffitest.c and recompile it. The code is as follows:

int add(int a , int b)
{
        printf("add call \n");
        return a + b;
}

(2) In ffitest.js, write the following code:

const ffi = require('ffi-napi');
const libs = ffi.Library("./ffitest.so" , {
        "getMsg":["string" , []],
        "add":["int" , ["int" , "int"]]
});
const msg = libs.getMsg();
console.log("getMsg return = " , msg);
const result = libs.add(1 , 1);
console.log("add = " , result);

(3) node ffitest.js outputs the following information, indicating that the call is successful

3. Call the function that passes the address parameter

(1) In ffitest.c, add an add2, which has no return value. The return value is output by passing the address value. The specific code is as follows:

void add2(int a , int b , int* out)
{
        printf("add2 call \n");
        int c = a + b;
        *out = c;
}

(2) Modify ffitest.js with the following code:

const ffi = require('ffi-napi');
const libs = ffi.Library("./ffitest.so" , {
        "getMsg":["string" , []],
        "add":["int" , ["int" , "int"]],
        "add2":["void" , ["int" , "int" , "string"]]
});
const msg = libs.getMsg();
console.log("getMsg return = " , msg);
const result = libs.add(1 , 1);
console.log("add = " , result);
//Request 4 bytes because an int takes up 4 bytes
//At this time, the returned is the hexadecimal small end mode byte type, which needs to be converted to decimal
let out = new Buffer.alloc(4);
libs.add2(100 , 2000 , out);
const res = byte2int(out);
console.log("add2 = " , res);
process.on('exit' , function(){
  callback;
});
function byte2int(param){
        let result = param[3] << 24;
        result = result + (param[2] << 16);
        result = result + ( param[1] << 8);
        result = result +  param[0];
        return result;
}

At this time, the returned type is byte, which is the small end mode. At this time, you need to manually convert int. for specific conversion, please refer to the above byte2int() function. For value delivery, please refer to the above methods.

4. Correspondence of common parameters

char* ---------------- string

int -------------------- int

It is troublesome to process value transfer. At present, there are also relevant parameter processing methods. For details, please refer to GitHub - TooTallNate/ref: Turn Buffer instances into "pointers" , not introduced here

But the above parameters are basically enough

3, Processing of callback function

In actual projects, callback functions are sometimes needed. c language uses function pointers to realize callback, but js can directly take functions as parameters. Compared with js, it is much more convenient to realize callback. The following describes how to call the callback function:

(1) In ffitest.c, compile a callback function. The code is as follows,

#include<stdio.h>
 void (*MY_CALL_BACK)(char* msg);
//Multiply two numbers
int add(int a , int b)
{
        printf("add call \n");
        return a + b;
}
void add2(int a , int b , int* out)
{
        printf("add2 call \n");
        int c = a + b;
        *out = c;
}
char* getMsg()
{
        printf("getMsg call \n");
        return "hello world";
}
//Set callback function
void SetCallBack(void* callback)
{
        MY_CALL_BACK = callback;
}
//Execute callback function
void testCallBacl()
{
        MY_CALL_BACK("this is a callback");
        printf("*****************\n");
}

(2) Modify the ffitest.js file. When setting back to the function, we need to pass in a pointer. We can use pointer for parameters. The specific code is as follows:

const ffi = require('ffi-napi');
const libs = ffi.Library("./ffitest.so" , {
        "getMsg":["string" , []],
        "add":["int" , ["int" , "int"]],
        "add2":["void" , ["int" , "int" , "string"]],
        "SetCallBack":["void" , ["pointer"]],
        "testCallBacl":["void" , []]
});
const msg = libs.getMsg();
console.log("getMsg return = " , msg);
const result = libs.add(1 , 1);
console.log("add = " , result);
//Request 4 bytes because an int takes up 4 bytes
//At this time, the returned is the hexadecimal small end mode byte type, which needs to be converted to decimal
let out = new Buffer.alloc(4);
libs.add2(100 , 2000 , out);
const res = byte2int(out);
console.log("add2 = " , res);
//Callback function
const callback = ffi.Callback("void" , ['string'] , function(data){
        console.log("call back data : " , data);
});
//Set callback function
libs.SetCallBack(callback);
//Execute this function, and then call the callback function
libs.testCallBacl();
//byte to int
function byte2int(param){
        let result = param[3] << 24;
        result = result + (param[2] << 16);
        result = result + ( param[1] << 8);
        result = result +  param[0];
        return result;
}

4, In conclusion, the program can be extended by c language or c + + through ffi library. Here is just an example of calling so. The calling dll code is the same, but I was discouraged by myself when installing the windows environment. If there is a great God, what solutions can I share.

 

Tags: C++ Javascript

Posted on Sun, 24 Oct 2021 00:41:32 -0400 by jlhaslip