C開發lua模組(二) --- 操作table和呼叫lua函式
C語言沒有類似 lua_pushtable 和 lua_totable 的方法,這也很容易理解,因為C語言中沒有一種資料型別可以和lua中的table相對應的,那C函式怎麼操作table型別的引數或者返回table型別的結果呢,lua API中提供了一系列的操作table的函式。
void lua_createtable (lua_State *L, int narr, int nrec); //建立一個空的table並壓入棧中,並預分配narr個array元素的空間和預分配nrec個非array元素的空間 void lua_newtable (lua_State *L); // lua_createtable的特例版,相當於呼叫 lua_createtable(L, 0, 0)
以上兩個方法用於建立一個lua table並壓入棧中,所以,想要返回一個table,就可以如下操作:
static int returntable(lua_State *L)
{
lua_newtable(L);
return 1;
}
上面程式碼返回的是一個空的table,如果需要向這個table中新增元素,API提供了幾個用於設定table元素的方法:
void lua_settable (lua_State *L, int index); void lua_rawset (lua_State *L, int index); void lua_setfield (lua_State *L, int index, const char *k); void lua_rawseti (lua_State *L, int index, int n);
lua_settable在呼叫之前需要先壓入你要設定的 key 和 value,例如你要設定table["foo"] = "bar",先把 "foo" 壓入棧中,再把 "bar" 壓入棧中,然後再呼叫lua_settable,index取你要設定的table在棧中的索引,呼叫完該方法後,壓入的key和value會自動被彈出棧。lua_rawset除了設定時不會觸發元表操作外和lua_settable基本相同,修改示例程式碼為table新增元素:
static int returntable(lua_State *L) { lua_newtable(L); lua_pushstring(L, "foo"); lua_pushstring(L, "bar"); lua_settable(L, -3); //由於壓入了key和value,所以此時的table索引為-3 //lua_rawset(L, -3); 也可以使用lua_rawset return 1; //設定後,會彈出 "foo"和 "bar", 所以此時table仍舊處在棧頂 }
lua_setfield簡化了lua_settable的呼叫,key不再需要壓入棧中,而是直接當作函式的第三個引數傳入,所以只需要壓入value然後呼叫方法即可,呼叫後同樣會把value從棧中彈出,lua_rawseti 和 lua_setfield類似,但是隻能設定整數型別的key,可以對於純數字索引的陣列使用。
static int returntable(lua_State *L) { lua_newtable(L); lua_pushstring(L, "foo"); lua_pushstring(L, "bar"); lua_settable(L, -3); //由於壓入了key和value,所以此時的table索引為-3 //lua_rawset(L, -3); 也可以使用lua_rawset lua_pushstring(L, "Tom"); lua_setfield(L, -2, "username"); //由於只壓入了value,所以table此時的索引為-2 return 1; }
接下來實現一個用指定字元切割字串返回陣列的函式:
static int split(lua_State *L)
{
int len;
const char *str = lua_tolstring(L, 1, &len);
const char *sep = lua_tostring(L, 2);
int lastpos = -1, i = 0, key = 1;
lua_newtable(L); //建立一個table作為返回值
for(;i < len; i++)
{
if (str[i] == *sep)
{
lua_pushlstring(L, str + lastpos + 1, i - lastpos - 1); //壓入子串
lua_rawseti(L, -2, key); //把子串新增進table
lastpos = i;
key++;
}
}
//處理最後一個子串
lua_pushlstring(L, str + lastpos + 1, len - lastpos - 1);
lua_rawseti(L, -2, key);
return 1;
}
上面程式碼使用了lua_tolstring 和 lua_pushlstring 兩個方法,可以使用第三個引數,來獲取或設定字串的長度,這個方法可以在lua中像 split("aaa,bbb,ccc", ",") 這樣呼叫,並返回一個字串陣列。
同樣,C API也提供了獲取table值得方法:
void lua_gettable (lua_State *L, int index); //獲取索引為index的table中指定key的value,key要預先壓入棧,函式呼叫結束key會被彈出棧,並把value壓入棧中
void lua_rawget (lua_State *L, int index); // lua_gettable類似,但不涉及元表
void lua_getfield (lua_State *L, int index, const char *k); //key不需要壓入棧,直接當作第三個引數傳入,得到的value同樣會被壓入棧中
void lua_rawgeti (lua_State *L, int index, int n); // 除了不涉及元表,並且key只能為整數
下面實現一個求陣列所有元素和的方法:
static int array_sum(lua_State *L)
{
//檢查引數是否為一個table, 如果不是返回nil
if (!lua_istable(L, 1))
{
lua_pushnil(L);
return 1;
}
int sum = 0;
int len = lua_objlen(L, 1); //返回陣列的長度
int i = 1; //lua table 索引從1開始
for(; i <= len; i++)
{
lua_rawgeti(L, 1, i);
sum += lua_tointeger(L, -1);
lua_pop(L, 1); //將剛剛獲取的元素值從棧中彈出,其實也可以不用彈棧 因為table的索引始終是1,新讀取的值始終在棧頂,也就是索引是-1,不過這是一個好的習慣
}
lua_pushinteger(L, sum);
return 1;
}
lua_istable 可以檢查一個值是否為table,是返回1,否則返回0, lua_objlen 可以獲取字串或陣列的長度
以上就是C API中關於table操作的一些方法,接下來介紹一下在C程式碼中呼叫lua函式。
假設現在lua指令碼中有這樣一種需求,要求有一個array_map方法,該方法接受兩個引數,第一個引數為一個數組,第二個引數為一個函式f,用數組裡的所有元素當作引數呼叫函式f,用返回值組成一個新的陣列並返回,大致的lua指令碼像這樣:
local function double(num)
return num * 2
end
local arr = {1, 2, 3, 4, 5}
local res = array_map(arr, double) --期望得到 {2, 4, 6, 8, 10}
那該如何用C實現這個array_map方法呢,這就需要在C程式碼中呼叫lua函式,為此,C API提供了兩個用來呼叫lua函式的方法:
void lua_call (lua_State *L, int nargs, int nresults);
int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);
這兩個方法都相對比較複雜,lua_call要求首先壓入要呼叫的lua函式,然後依次按順序壓入nargs個引數, 這時呼叫lua_call,之後這個函式和所有的引數都會從棧中彈出,最後把函式返回的nresults個結果依次壓入棧中。lua_pcall在保護模式下執行lua函式,當執行出錯時這個錯誤會被捕獲,而不會直接終止程式執行,lua_pcall的返回值是一個錯誤碼,當返回0時表示沒有錯誤,此時效果和lua_call一樣,當返回不為0時表示有錯誤,並將錯誤訊息壓入棧中,第四個引數還可以指定一個錯誤處理函式的索引,如果不指定可以傳入0值。
下面實現前面提到的array_map方法:
static int array_map(lua_State *L)
{
//校驗引數型別
if (!lua_istable(L, 1) || !lua_isfunction(L, 2))
{
lua_pushnil(L);
return 1;
}
int len = lua_objlen(L, 1); //陣列的大小
int i = 1;
for(; i <= len; i++)
{
//由於方法呼叫之後會被彈出,所以呼叫之前先複製一份,pushvalue會將指定索引的值複製一份到棧頂
lua_pushvalue(L, 2);
lua_rawgeti(L, 1, i); //獲取陣列元素值並會壓入棧中
lua_call(L, 1, 1); //呼叫函式,一個引數 一個返回值,此時複製的函式和壓入的引數都會彈出,然後壓入結果
lua_rawseti(L, 1, i); //再把結果替換掉陣列中的原值。
}
lua_pushvalue(L, 1); //把陣列複製一份放到棧頂 當作返回值
return 1;
}
現在把本文中的所有方法放到上節中的clib.c中,重新編譯成clib.so
#include <lauxlib.h>
//編寫C函式 static使外部無法直接訪問這個函式
static int sum(lua_State *L)
{
int a = lua_tointeger(L, 1); //第一個加數,函式的第一個引數總是索引1
int b = lua_tointeger(L, 2); //第二個加數
lua_pushinteger(L, a + b); //壓入結果
return 1; //返回1表示該方法只有一個返回值
}
static int split(lua_State *L)
{
size_t len;
const char *str = lua_tolstring(L, 1, &len);
const char *sep = lua_tostring(L, 2);
int lastpos = -1, i = 0, key = 1;
lua_newtable(L); //建立一個table作為返回值
for(;i < len; i++)
{
if (str[i] == *sep)
{
lua_pushlstring(L, str + lastpos + 1, i - lastpos - 1); //壓入子串
lua_rawseti(L, -2, key); //把子串新增進table
lastpos = i;
key++;
//處理最後一個子串
lua_pushlstring(L, str + lastpos + 1, len - lastpos - 1);
lua_rawseti(L, -2, key);
return 1; //返回table
}
static int array_sum(lua_State *L)
{
//檢查引數是否為一個table, 如果不是返回nil
if (!lua_istable(L, 1))
{
lua_pushnil(L);
return 1;
}
int sum = 0;
int len = lua_objlen(L, 1); //返回陣列的長度
int i = 1; //lua table 索引從1開始
for(; i <= len; i++)
{
lua_rawgeti(L, 1, i);
sum += lua_tointeger(L, -1);
}
lua_pushinteger(L, sum);
float aver = (float)sum / len; //平均值
lua_pushnumber(L, aver);
return 2;
}
static int array_map(lua_State *L)
{
//校驗引數型別
if (!lua_istable(L, 1) || !lua_isfunction(L, 2))
{
lua_pushnil(L);
return 1;
}
int len = lua_objlen(L, 1); //陣列的大小
int i = 1;
for(; i <= len; i++)
{
//由於方法呼叫之後會被彈出,所以呼叫之前先複製一份,pushvalue會將指定索引的值複製一份到棧頂
lua_pushvalue(L, 2);
lua_rawgeti(L, 1, i); //獲取陣列元素值並會壓入棧中
lua_call(L, 1, 1); //呼叫函式,一個引數 一個返回值,此時複製的函式和壓入的引數都會彈出,然後壓入結果
lua_rawseti(L, 1, i); //再把結果替換掉陣列中的原值。
}
lua_pushvalue(L, 1); //把陣列複製一份放到棧頂 當作返回值
return 1;
}
//宣告一個luaL_Reg結構體陣列
static const struct luaL_Reg funcs[] = {
{"sum", sum},
{"split", split},
{"array_sum", array_sum},
{"array_map", array_map},
{NULL, NULL} // 該陣列最後一個元素始終是 {NULL, NULL}
};
int luaopen_clib(lua_State *L)
{
luaL_register(L, "clib", funcs); //第二個引數要和你的模組名一致
return 1;
}
編寫lua測試指令碼:
local clib = require "clib"
local s = clib.sum(3, 99)
print(s)
print("---------------------")
local str = "aaa,bbbb,cccc,dddd,eeee"
local arr = clib.split(str, ",")
for i = 1, #arr do
print(arr[i])
end
print("---------------------")
local arr1 = {1, 2, 3, 10}
local sum = clib.array_sum(arr1)
print("sum=", sum)
print("---------------------")
local function double(num)
return num * 2
end
local arr2 = {1, 2, 3, 4, 5}
local res = clib.array_map(arr2, double)
for i = 1, #res do
print(res[i])
end
執行結果:
102
---------------------
aaa
bbbb
cccc
dddd
eeee
---------------------
sum= 16
---------------------
2
4
6
8
10
篇幅所限,就寫到這裡,如果想更深入瞭解lua擴充套件的開發技術,歡迎關注後續文章。
相關推薦
C開發lua模組(二) --- 操作table和呼叫lua函式
C語言沒有類似 lua_pushtable 和 lua_totable 的方法,這也很容易理解,因為C語言中沒有一種資料型別可以和lua中的table相對應的,那C函式怎麼操作table型別的引數或者返回table型別的結果呢,lua API中提供了一系列的操作table
嵌入式C++開發詳解(二)
面向物件程式設計(一) 一、面向物件程式設計介紹 (一)什麼是面向物件? 面向將系統看成通過互動作用來完成特定功能的物件的集合。每個物件用自己的方法來管理資料。也就是說只有物件內部的程式碼
Java開發學習心得(二):Mybatis和Url路由
rac 處理 2.4 報錯 localhost insert 實體 tips control Java開發學習心得(二):Mybatis和Url路由 序號接上一篇Java開發學習心得(一):SSM環境搭建 1.3 Mybatis MyBatis 本是apache的一個開源項
C語言陣列篇(二)指標陣列和陣列指標
陣列指標 和 指標陣列 這兩個名詞可以說是經常搞混了 陣列指標--> 陣列的
C++動態記憶體:(二)過載new和delete
一、過載的原因 用new建立動態物件時會發生兩件事:(1)使用operatoe
我的C#跨平臺之旅(二):開發一組標準的Restful API
ref 運行 mar margin bruce ora soft left 啟用 添加NuGet引用:Microsoft.AspNet.WebApi.Owin 在啟動類啟用WebApi; 添加一個Controller類,代碼如下: 運行程序
Smobiler實現美觀登入介面——C# 或.NET Smobiler例項開發手機app(二)
目錄 一、 本文目標 二、 準備工作 1、 資料庫 2、 材料 三、 介面佈局 1、設定控制元件的屬性值 (1) 輸入框 (2) 圖片屬性 (3) HandElectricity的標題的label屬性 (4)登入按鈕 (5)版權申明 (
C#框架程式設計動態載入模組(二)
本文系原創,轉載請註明出處: 在上一篇部落格中,我完成了介面的設計部分,下面我接著來講具體的程式碼實現。先來看模組配置頁面的實現,看程式碼: private void LoadItem() { string sq
C#.架構設計 資料(二)c# 專案中包含了多個模組或多個功能,如何靈活開啟/關閉、新增/刪除某個模組或功能
一、簡介 不知不覺,短短几個月的時間,我已經寫了大大小小100篇部落格。短短几個月的時間,見證了我的努力、我的收穫、我的學習效率。從一開始的零基礎,到現在我需要了解整個專案的設計架構,才能來滿足我的設計需求。
c++實戰開發詳解(二)類與物件
一、面向物件程式設計介紹 (一)什麼是面向物件? 面向將系統看成通過互動作用來完成特定功能的物件的集合。每個物件用自己的方法來管理資料。也就是說只有物件內部的程式碼能夠操作物件內部的資料。
用C語言擴充套件lua模組(入門)
#include <lua.h> #include <lauxlib.h> #include <lualib.h> static void encode_sha(const char* src, char* des) { /* *sha-hash /
配置 vim C/C++集成開發環境實踐(二)
可能 默認 sorl tgui ace ast gvim evm arch 主要參考GitHub項目:所需即所獲:像 IDE 一樣使用 vim 其教程講解已相當詳細,故此處文章僅簡要記錄個人按照上述項目學習配置vim的過程。 可能是個人理解不到位或者項目久未更新的原因,實踐
【C語言天天練(二)】statickeyword
修飾 weight () main函數 class code keyword spa tail 引言: statickeyword不僅能夠修飾變量。並且能夠修飾函數。了解它的使用方法,不僅對閱讀別人的代碼有幫助,也有助於自己寫出更加健壯的
C++構造函數(二)
frame 筆記 自動轉換 數據類型 public clas 並不是 調用 這樣的 本篇是介紹C++的構造函數的第二篇(共二篇),屬於讀書筆記,對C++進行一個系統的復習。 復制構造函數 復制構造函數是構造函數的一種,也被稱為拷貝構造函數,他只有一個參數,參數類型是本類的引
Python和C|C++的混編(二):利用Cython進行混編
cde uil 有時 當前 class def 將在 python 混編 還能夠使用Cython來實現混編 1 下載Cython。用python setup.py install進行安裝 2 一個實例 ① 創建helloworld文件夾創建hellowor
C++ 模板詳解(二)(轉)
創建 規則 error ++ 例如 public err iostream () 四、類模板的默認模板類型形參 1、可以為類模板的類型形參提供默認值,但不能為函數模板的類型形參提供默認值。函數模板和類模板都可以為模板的非類型形參提供默認值。 2、類模板的類型形
Python開發簡單爬蟲(二)---爬取百度百科頁面數據
class 實例 實例代碼 編碼 mat 分享 aik logs title 一、開發爬蟲的步驟 1.確定目標抓取策略: 打開目標頁面,通過右鍵審查元素確定網頁的url格式、數據格式、和網頁編碼形式。 ①先看url的格式, F12觀察一下鏈接的形式;② 再看目標文本信息的
Spring Data 開發環境搭建(二)
是不是 lns utf-8 void ext for 實體類 connect domain 首先咱們先創建一個maven工程 在pom.xml加入以下 依賴 <!--Mysql 驅動包--> <dependency> <
學習Opencv 2.4.9(二) ---操作像素
椒鹽噪聲 window align 是個 簡單 ++ ima opencv2 mar 作者:咕唧咕唧liukun321來自:http://blog.csdn.net/liukun321本質上說一張圖像就是由數值組成的矩陣。Opencv 2.x由 cv::Mat 這個數據
讀書筆記--C陷阱與缺陷(二)
ase 結果 erro bit 使用 功能 錯誤 多層 gnu 第二章 1. 理解函數聲明 書中分析了復雜的類型聲明方式,也說明了使用typedef聲明會更好理解,推薦大家使用typedef進行函數聲明。 書中類型分析一層一層挖掘,讓讀者可以理解多層嵌套的類型含義,有