Lua FFI 實戰

來源:互聯網
上載者:User

轉自:http://blog.csdn.net/weiwangchao_/article/details/16880401

由來

FFI庫,是LuaJIT中最重要的一個擴充庫。它允許從純Lua代碼調用外部C函數,使用C資料結構。有了它,就不用再像Lua標準math庫一樣,編寫Lua擴充庫。把開發人員從開發Lua擴充C庫(語言/功能綁定庫)的繁重工作中釋放出來。 FFI簡介

FFI庫,允許從純Lua代碼調用外部C函數,使用C資料結構。

FFI庫最大限度的省去了使用C手工編寫繁重的Lua/C綁定的需要。不需要學習一門獨立/額外的綁定語言——它解析普通C聲明。這樣可以從C標頭檔或參考手冊中,直接剪下,粘貼。它的任務就是綁定很大的庫,但不需要搗鼓脆弱的綁定產生器。

FFI緊緊的整合進了LuaJIT(幾乎不可能作為一個獨立的模組)。JIT編譯器為Lua代碼直接存取C資料結構而產生的代碼,等同於一個C編譯器應該生產的代碼。在JIT編譯過的代碼中,調用C函數,可以被內連處理,不同於基於Lua/C API函數調用。

這一頁將簡要介紹FFI庫的使用方法。 激勵範例:調用外部C函數

真的很用容易去調用一個外部C庫函數:

① local ffi = require("ffi") ② ffi.cdef[[  int printf(const char* fmt, ...);    ]]③ ffi.C.printf("Hello %s!", "world")

以上操作步驟,如下:

① 載入FFI庫② 為函數增加一個函式宣告。這個包含在`中括弧`對之間的部分,是標準C文法。.③ 調用命名的C函數——非常簡單

事實上,背後的實現遠非如此簡單:③ 使用標準C庫的命名空間ffi.C。通過符號名("printf")索引這個命名空間,自動綁定標準C庫。索引結果是一個特殊類型的對象,當被調用時,執行printf函數。傳遞給這個函數的參數,從Lua對象自動轉換為相應的C類型。

Ok,使用printf()不是一個壯觀的樣本。你也可能使用了io.write()和string.format()。但你有這個想法…… 以下是一個Windows平台彈出訊息框的樣本:

 local ffi = require("ffi") ffi.cdef[[ int MessageBoxA(void *w, const char *txt, const char *cap, int type); ]] ffi.C.MessageBoxA(nil, "Hello world!", "Test", 0) 

Bing! 再一次, 遠非如此簡單,不?

和要求使用Lua/C API去綁定函數的努力相比: 建立一個外部C檔案, 增加一個C函數,遍曆和檢查Lua傳遞的參數,並調用這個真實的函數,

傳統的處理方式 增加一個模組函數列表和對應的名字, 增加一個luaopen_*函數,並註冊所有模組函數, 編譯並連結為一個動態庫(DLL), 並將庫檔案遷移到正確的路徑, 編寫Lua代碼,載入模組 等等…… 最後調用綁定函數。

唷。(很不爽呀。) 激勵樣本: 使用C資料結構

FFI庫允許你建立,並訪問C資料結構。當然,其主要應用是C函數介面。但,也可以獨立使用。

Lua構建在進階資料類型之上。它們很靈活、可擴充,而且是動態。這就是我們大家都喜歡Lua的原因所在。唉,針對特殊任務,你需要一個低級的資料結構時,這可能會低效。例如,一個超大的不同結構的數組,需要通過一張超大的表,儲存非常多的小表來實現。這需要大量的記憶體開銷以及效能開銷。

這裡是一個庫的草圖,操作一個彩圖,以及一個基準。首先,樸素的Lua版本,如下:

 local floor = math.floor   local function image_ramp_green(n) local img = {} local f = 255/(n-1) for i=1,n do img[i] = { red = 0, green = floor((i-1)*f), blue = 0, alpha = 255 } end return img end   local function image_to_grey(img, n) for i=1,n do local y = floor(0.3*img[i].red + 0.59*img[i].green + 0.11*img[i].blue) img[i].red = y; img[i].green = y; img[i].blue = y end end   local N = 400*400 local img = image_ramp_green(N) for i=1,1000 do image_to_grey(img, N) end 

以上代碼,建立一個160.000像素的一張表,其中每個元素是一張持有4個範圍0至255的數字值的表。首先,建立了一張綠色斜坡的圖(1D,為了簡單化),然後進行1000次灰階轉換操作。實在很蠢蛋,可是我需要一個簡單樣本……

以下是FFI版本代碼。其中,被修改的部分加粗標註:

① local ffi = require("ffi")ffi.cdef[[typedef struct { uint8_t red, green, blue, alpha; } rgba_pixel;]]② local function image_ramp_green(n)  local img = ffi.new("rgba_pixel[?]", n)  local f = 255/(n-1)③  for i=0,n-1 do④  img[i].green = i*f    img[i].alpha = 255  end  return imgendlocal function image_to_grey(img, n)③ for i=0,n-1 do⑤   local y = 0.3*img[i].red + 0.59*img[i].green + 0.11*img[i].blue    img[i].red = y; img[i].green = y; img[i].blue = y  endendlocal N = 400*400local img = image_ramp_green(N)for i=1,1000 do  image_to_grey(img, N)end

Ok, 這是不是太困難:

① 首先,載入FFI庫,聲明底層資料類型。這裡我們選擇一個資料結構,持有4位元組欄位,每一個由4x8 RGBA像素組成。

② 通過ffi.new()直接建立這個資料結構——其中'?'是一個預留位置,變長數組元素個數。

③ C資料是基於0的(zero-based),所以索引必須是0 到 n-1。你可能需要分配更多的元素,而不僅簡化轉換一流代碼。

④ 由於ffi.new()預設0填充(zero-fills)數組, 我們僅需要設定綠色和alpha欄位。

⑤ 調用math.floor()的過程可以省略,因為轉換為整數時,浮點數已經被向0截斷。這個過程隱式的發生在資料被儲存在每一個像素的欄位時。

現在讓我們看一下主要影響的變更:

首先,記憶體消耗從22M降到640K(4004004位元組)。少了35x。所以,表確實有一個顯著的開銷。BTW(By the Way: 順便說一句): 原始Lua

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.