Node-ffi:讓Node.js與外部C/C++庫交互的神器

Node-ffi (Foreign Function Interface)是一個Node.js的輕量級模塊,可以讓Node.js輕鬆地調用動態鏈接庫中的函數。該模塊為Node.js與外部C/C++庫交互提供了簡便、快捷的方式,提供了一種翻譯C語言API到JavaScript的機制。下面從多個方面對Node-ffi進行詳細介紹。

一、安裝Node-ffi

Node-ffi是一個npm模塊,安裝非常簡單,只需在終端中輸入以下命令即可:


npm install ffi

安裝完畢後,只需在項目中引入即可:


const ffi = require("ffi");

二、使用Node-ffi

1. Node-ffi的簡單示例

下面是一個簡單的示例,用於展示如何使用Node-ffi調用C庫函數:


const ffi = require("ffi");

let libm = ffi.Library("libm.so.6", {
    "ceil": ["double", ["double"]] //ceil為C庫中的函數名,["double", ["double"]]為C語言函數的返回值類型和參數列表
});

libm.ceil(1.5); // Node.js可以調用 C 庫函數

由於ceil是math.h工具庫中的一個函數,因此我們需要在調用前將其添加到libm對象中。Library函數的第一個參數是所需C庫文件的名稱或完整路徑。由於math.h是標準C庫的一部分,因此該庫被鏈接到所有C程序中,包括我們所編寫的JavaScript文件。該示例使用了libm.so.6文件。

2. 將C結構映射到Node.js結構體

在C應用程序中,結構體是一種用於組合數據類型的機制。使用Node-ffi時,我們必須將C結構映射到Node.js結構體。

這是一個由C結構體定義的示例:


struct Network {
    double bias;
    double weights[10];
};

要將其映射到Node.js結構體,我們可以使用ref模塊來聲明類型:


let ref = require("ref");
let StructType = require("ref-struct");

let Network = StructType({
    bias: "double", // 結構體中的欄位名稱和類型
    weights: ref.refType("double"), //指針類型的欄位
});

注意,weights欄位被定義為指針類型, 因此我們需要通過ref模塊的refType方法來聲明。

創建一個C語言結構體的示例:


let net = new Network();
net.bias = 0.1;
net.weights[0] = 0.2;
net.weights[1] = 0.3;

3. 將C函數返回的指針轉換為JavaScript Buffer

有時,C函數可能會返回指針類型的數據。如果我們需要在JavaScript中對此數據進行操作,我們需要將返回的指針轉換為JavaScript Buffer對象。

請看以下示例:

在C應用程序中,我們有一個函數返回指向double數組的指針:


double *create_array(){
    double *arr = (double *)malloc(3 * sizeof(double));
    arr[0] = 1.1;
    arr[1] = 2.2;
    arr[2] = 3.3;
    return arr;
}

我們可以使用Node-ffi調用該函數:


let lib = ffi.Library("mylib", {
    "create_array": ["pointer", []]
});

let arr = lib.create_array(); //arr是一個指針類型
let size = 3 * ref.sizeof.double; // size為數組的大小
let doubleArray = Buffer.alloc(size); //創建一個新的Buffer,該Buffer可以理解為是一個double指針數組
doubleArray.writeDoubleLE(arr[0], 0); // 將返回的指針中的值寫入Buffer
doubleArray.writeDoubleLE(arr[1], 8); // 參數2是寫入Buffer的偏移量,8表示下標為1
doubleArray.writeDoubleLE(arr[2], 16);

通過將返回的指針轉換為JavaScript Buffer對象,我們就可以在JavaScript中處理C返回的所有數據了。

4. 傳遞複合類型的參數

如果一個C函數的參數是複合類型,則需要在JavaScript中定義一個結構體,以便在調用該函數時傳遞正確的參數。

請看下面的示例:

在C應用程序中,我們有一個名為print_person()的函數,它需要Person結構作為參數:


struct Person {
    char *name;
    int age;
    double salary;
};

void print_person(struct Person p){
    printf("Name: %s, Age: %d, Salary: %f", p.name, p.age, p.salary);
}

在Node.js中,我們必須先定義一個相對應的Person結構體,然後使用該結構體作為函數的參數:


let Person = StructType({
    name: "string",
    age: "int",
    salary: "double"
});

let person = new Person();
person.name = "Tom";
person.age = 30;
person.salary = 50000;

let printPerson = ffi.Library("mylib", {
    "print_person": ["void", [Person]]
});

printPerson.print_person(person);

5. 應將C字元串返迴轉換為JavaScript字元串

在C中,字元串是由字元數組表示的,結尾會附有一個空字元(‘\0’)。當C程序返回一個字元串時,它實際上是返回了一個指向字元數組的指針。

這是一個簡單的返回字元串的C函數示例:


char *get_string(int id){
    if (id == 0) {
        return "Hello World";
    } else {
        return "Node.js is awesome";
    }
}

我們可以使用Node-ffi調用該函數:


let lib = ffi.Library("mylib", {
    "get_string": ["string", ["int"]]
});

let str = lib.get_string(1); // str是c字元串
let offset = str.indexOf("\0");
if (offset != -1) {
    str = str.slice(0,offset); //截取字元串直到"\0"
}

需要注意的是,在JavaScript中,我們需要手動去除C字元數組末尾的空字元.

三、總結

Node-ffi可以為Node.js提供一種與外部C/C++庫交互的簡便方式。該模塊適用於需要帶有高性能C/C++代碼的應用程序,但同時不想犧牲Node.js中所帶來的其他便利功能的情況。我們可以使用Node-ffi的API輕鬆地從Node.js中調用現有的C代碼,這使得我們能夠利用已經存在的模塊,而無需使用Node.js重新實現這些模塊。Node-ffi擴展了Node.js,並提供了C和JavaScript之間無縫互操作所需的翻譯功能。

原創文章,作者:SSLRS,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/332388.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
SSLRS的頭像SSLRS
上一篇 2025-01-21 17:30
下一篇 2025-01-24 18:46

相關推薦

發表回復

登錄後才能評論