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