(原)lua使用ffi調用c程序的函數


轉載請注明出處:

http://www.cnblogs.com/darkknightzh/p/5812763.html

參考網址:

http://luajit.freelists.narkive.com/Yhm9jicx/unexpected-type-conversion-for-arithmetic-with-cdata-double

http://luajit.org/ext_ffi_semantics.html#convert_fromlua

https://github.com/szagoruyko/loadcaffe

1. 新建calcmath.cpp,輸入:

 1 #include <iostream>
 2 #include <cmath>
 3 #include <stdio.h>
 4 //using namespace std;
 5 
 6 
 7 extern "C" {
 8 float isquare(float val);
 9 double isqrt(double val);
10 void ivecadd(double* a, double* b, int len);
11 }
12 
13 float isquare(float val)
14 {
15     return val*val;
16 }
17 
18 double isqrt(double val)
19 {
20     return sqrt(val);
21 }
22 
23 void ivecadd(double* a, double* b, int len)
24 {
25     for (int i = 0; i < len; ++i)
26     {
27         a[i] = a[i] + b[i];
28     }
29 }

說明:包含三個函數:isquare計算平方,isqrt計算開方,ivecadd計算兩個數組對應元素之和。

2. 在終端中輸入:

g++ -shared -fPIC -o libcalcmath.so calcmath.cpp

此時當前文件夾內會生成libcalcmath.so。

說明:不能使用gcc,會提示如下錯誤:

undefined symbol: _ZNSt8ios_base4InitD1Ev

截圖如下:

3. 新建調用該庫的lua文件:calcmath.lua(名字隨便),並輸入:

 1 calcmath = {}
 2 
 3 local ffi = require("ffi")
 4 
 5 ffi.cdef[[
 6 float isquare(float val);
 7 double isqrt(double val);
 8 void ivecadd(double* a, double* b, int len);
 9 ]]
10 
11 calcmath.C = ffi.load('calcmath')
12 
13 --[[local squareVal = calcmath.C.isquare(5)    -- call functions in this file
14 print(squareVal)
15 local sqrtVal = calcmath.C.isqrt(5)
16 print(sqrtVal)
17 
18 local a = ffi.new('double[2]', {5.2, 6.7})
19 local b = ffi.new("double[2]", {3, 7})
20 
21 local x = ffi.cast('double&',a)
22 local y = ffi.cast('double&',a+1)
23 
24 calcmath.C.ivecadd(a, b, ffi.sizeof(a)/8)
25 print(tonumber(x), tonumber(y))
26 print(ffi.sizeof(a))
27 ]]

說明:a上面注釋的代碼實際可以直接使用,此時在當前文件內直接調用c函數。如果只在當前文件使用calcmath,可以加上local,如local calcmath = {}。如果要在其他文件內使用calcmath,則不能加上local,否則會提示:

b 該文件名字如果為ffi.lua的話,可能不能在其他文件內成功調用(應該是與系統文件ffi.lua沖突):

也可能能調用成功(在另一台電腦上試了一下,如果用include則失敗,如果用dofile則成功。。。)。

160830更新:剛才又試了一下,即便該文件名字為‘ffi.lua’,無論是include還是dofile,都能成功:test.lua的前6行如下:

local ffi = require("ffi")

--include 'ffi.lua'       -- this line and the following 2 line are both ok

require 'paths'
paths.dofile('ffi.lua')

如果不加第一句require就不行。估計程序能區分開哪個是系統的庫文件,哪個是當前的文件。。。先將就着這樣理解吧。

c libcalcmath.so文件可以放在當前文件夾下,也可以放在/usr/lib下;不論放在當前文件夾還是/usr/lib下,代碼中均不用加上lib及.so,ffi.load 時會自動添加。當然在代碼中也可以加上,如libcalcmath或libcalcmath.so或calcmath.so。我這邊試了,如果放在其他文件夾下,需要使用絕對路徑,並且需要使用庫的完整名libcalcmath.so,如:/home/XXX/libcalcmath.so,否則會提示:

4. 新建測試程序test.lua,並輸入:

 1 local ffi = require("ffi")
 2 
 3 --include 'calcmath.lua'       -- this line and the following 2 line are both ok
 4 
 5 require 'paths'
 6 paths.dofile('calcmath.lua')
 7 
 8 local C = calcmath.C
 9 
10 local squareVal = C.isquare(5)
11 print(squareVal)
12 local sqrtVal = C.isqrt(5)
13 print(sqrtVal)
14 
15 local a = ffi.new('double[2]', {5.2, 6.7})
16 local b = ffi.new("double[2]", {3, 7})
17 
18 local x = ffi.cast('double&',a)
19 local y = ffi.cast('double&',a+1)
20 
21 C.ivecadd(a, b, ffi.sizeof(a)/8)
22 print(tonumber(x), tonumber(y))
23 print(ffi.sizeof(a))

說明:a第一句local ffi = require("ffi")必須要,否則下面的ffi.new無法使用。

b 不確定是否需要釋放new出來的內存。。。(以后碰到了再說吧)。

5. 結果:

使用th test.lua結果如下:

無法使用lua test.lua,會提示如下的錯誤:

使用luajit test.lua,結果如下:

說明

如果isqrt中參數類型使用float的話,精度會有損失,結果為:

2.2360680103302

使用double后,結果為:

2.2360679774998

使用計算器的結果:

2.2360679774997896964091736687313

可見,使用double后,精度范圍內結果一致。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM