lua調(diào)用C/C++的方法詳解
1 lua vs C/C++
lua是腳本語言,優(yōu)點是門檻低,可以熱更新,缺點當(dāng)然就是性能。C/C++是編譯型語言,有點是性能高,但是相對的,門檻高,技術(shù)不好的人寫的代碼可能還沒有l(wèi)ua的性能高,容易出現(xiàn)core,不能熱更新。
不過,lua語言本身就是用C實現(xiàn)的,而且,可以將很多能力封裝成lua的接口供lua調(diào)用。
2 C/C++如何給lua提供接口
查看一個lua模塊的源代碼會發(fā)現(xiàn),lua模塊的實現(xiàn)中既包含lua代碼,也包含C代碼,其中,C代碼的主要邏輯就是獲取參數(shù),調(diào)用系統(tǒng)調(diào)用,返回值,C代碼會編譯為so供lua調(diào)用,而lua代碼就是將C代碼提供的一些接口進行再封裝,以便在lua中更好用,更簡單,然后再通過lua代碼對外提供接口。因此,這么看起來,通過lua調(diào)用C函數(shù),重要的就是在C中如何獲取參數(shù)以及如何返回值。
下面的說明以linotify項目進行說明。
2.1 lua模塊的查找
當(dāng)在lua里面通過require(“inotify”)時,lua怎么知道去哪里查找inotify模塊呢?此時,inotify模塊是個lua腳本還是個so呢?
跟其他腳本語言類似,lua中也是通過變量來控制模塊的查找的,其中package.path是搜索lua模塊的路徑,package.cpath是搜索so模塊的路徑,先查找lua模塊,再查找so模塊。
通過上面這種方式,在當(dāng)前目錄找到了inotify.so。
2.2 so的入口
要調(diào)用inotify.so中的函數(shù),肯定還是要用動態(tài)庫的函數(shù):dlopen、dlsym。例如,當(dāng)調(diào)用require(“inotify”)時,如果沒有導(dǎo)入inotify.so,則調(diào)用dlopen加載inotify.so庫,
當(dāng)在lua中調(diào)用local wd = handle:addwatch('/home/rob/', inotify.IN_CREATE, inotify.IN_MOVE)
時,會調(diào)用handle_add_watch()函數(shù)。
2.3 參數(shù)獲取
lua和C之間是通過棧進行交互的,當(dāng)調(diào)用C函數(shù)時,C函數(shù)的第一個參數(shù)是lua_State的指針,可以將它理解為lua的一個狀態(tài)機。
如果要調(diào)用函數(shù),第一步就是參數(shù)的獲取,lua會將參數(shù)放到棧中,因此,inotify.so中的函數(shù)可以獲取棧中的數(shù)據(jù)得到參數(shù):
fd = get_inotify_handle(L, 1); path = luaL_checkstring(L, 2); top = lua_gettop(L); for (i = 3; i <= top; i++) { mask |= (uint32_t)luaL_checkinteger(L, i); }
get_inotify_handle()獲取棧中的第1個參數(shù),luaL_checkstring(L, 2)獲取棧中的第2個參數(shù),且第2個參數(shù)是個字符串,然后通過lua_gettop(L)獲取所有的參數(shù)的個數(shù),再用for循環(huán)將剩余的參數(shù)通過位或放到mask變量。通過這種方式就分別得到了addwatch()的三個參數(shù)。
然后再調(diào)用inotify的inotify_add_watch()完成實際的邏輯。
2.4 返回值
當(dāng)具體的業(yè)務(wù)邏輯完成后,就需要將返回值傳給lua,依舊是通過入棧的方式。
在這里,調(diào)用完inotify_add_watch()就得到某個監(jiān)聽操作的描述符,也需要將這個描述符返回,如果操作成功,調(diào)用lua_pushinteger(L, wd)
將wd返回,如果操作失敗,則返回3個值:
static int handle_error(lua_State *L) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); lua_pushinteger(L, errno); return 3; }
第1個是nil,第2個是錯誤信息,第3個是錯誤碼。
因此,在lua中可以這樣來調(diào)用:
local wd, err_info, errno = handle:addwatch('/home/rob/', inotify.IN_CREATE, inotify.IN_MOVE) if wd == nil then print("ERROR=", err_info) end
同時還需要注意handle_add_watch()函數(shù)的返回值,返回值表明了lua中函數(shù)返回值的個數(shù)。例如這里,成功時,只返回描述符,因此,函數(shù)返回值是1,失敗時,多了額外的錯誤信息,因此,函數(shù)返回值是3。
2.5 一個小的demo
有了上面的了解,可以實現(xiàn)我們的一個小小的demo。
假設(shè)我們要實現(xiàn)一個加法操作,實際的加法操作在C中完成,然后在lua中調(diào)用。
#include <lua.h> #include <lauxlib.h> static int handle_add(lua_State *L) { int a, b, c; a = luaL_checkinteger(L, 1); b = luaL_checkinteger(L, 2); c = a + b; lua_pushinteger(L, c); return 1; } static luaL_Reg funcs[] = { {"add", handle_add}, {NULL, NULL} }; int luaopen_demo(lua_State *L) { lua_createtable(L, 0, sizeof(funcs)/sizeof(luaL_Reg) - 1); luaL_setfuncs(L, funcs, 0); return 1; }
那這里的入口函數(shù)就是luaopen_demo(),里面就調(diào)用了兩個函數(shù),先調(diào)用lua_createtable創(chuàng)建
將上面的代碼編譯為so:
gcc demo.c -fPIC -shared -o demo.so
lua中調(diào)用:
local demo = require("demo") print(demo.add(2, 3))
3 lua FFI
lua C API實現(xiàn)lua的模塊使用的是虛擬棧的方式,實現(xiàn)起來太過麻煩,用戶需要使用一種新的接口(C API)和模式(虛擬棧)實現(xiàn),而使用FFI機制,就可以在lua中直接調(diào)用C函數(shù)。
3.1 一個小例子
local ffi = require("ffi") ffi.cdef[[ int printf(const char*fmt, ...); ]] ffi.C.printf("hello %s", "world");
首先加載ffi模塊,然后使用cdef添加C函數(shù)的聲明,有點類似于C語言中的頭文件,然后就可以調(diào)用ffi.C中的printf函數(shù)。然后就可以使用luajit編譯:luajit hell.lua
。
3.2 調(diào)用so
上面的例子是調(diào)用C標準庫中的函數(shù),如果需要調(diào)用其他的so文件呢?
// libtest.c #include <stdio.h> int show(char *str) { int ret = 0; if(str == NULL) { ret = -1; } else { printf("input: %s\n", str); } return ret; }
# 將上述代碼編譯為so gcc -shared -fPIC libtest.c -o libtest.so
然后就可以在lua中調(diào)用:
local ffi = require("ffi") -- 加載libtest.so local myffi = ffi.load("test") -- 聲明函數(shù)原型 ffi.cdef[[ int show(char *str); ]] local str1 = "hello" -- 將字符串類型轉(zhuǎn)換為char* local str2 = ffi.cast("char *",str1) -- 調(diào)用libtest.so中的show函數(shù) print(myffi.show(str2))
4 C API vs FFI
FFI相比C API最大的優(yōu)勢就是比較好理解,性能高,但是使用FFI也存在一些兼容性的問題;而C API由于是官方提供的接口,在穩(wěn)定性方面還是很好的。
5 參考文檔
以上就是lua調(diào)用C/C++的方法詳解的詳細內(nèi)容,更多關(guān)于lua調(diào)用C/C++的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Lua中的閉合函數(shù)、非全局函數(shù)與函數(shù)的尾調(diào)用詳解
這篇文章主要介紹了Lua中的閉合函數(shù)、非全局函數(shù)與函數(shù)的尾調(diào)用詳解,本文對這2種函數(shù)和尾調(diào)用做了深入研究,需要的朋友可以參考下2014-09-09linux系統(tǒng)安裝Nginx Lua環(huán)境
因項目需求,需要在Linux系統(tǒng)下搭建一套nginx+lua的開發(fā)環(huán)境,經(jīng)過一番摸索,現(xiàn)總結(jié)如下,希望大家能夠喜歡。2016-12-12

lua開發(fā)中實現(xiàn)MVC框架的簡單應(yīng)用