如何正確的在CC++裡呼叫Lua
序言
涉及關鍵詞
:WSL 、Lua、GCC 、C/C++
宣告
:本文不對lua,wsl,gcc的安裝配置進行教學。
先來看看百度百科是怎麼定義lua的
Lua 是一個小巧的
指令碼語言
。 其設計目的是為了透過靈活嵌入應用程式中從而為應用程式提供靈活的擴充套件和定製功能。Lua由標準C編寫而成,Lua指令碼可以很容易的被
C/C++
程式碼呼叫,也可以反過來呼叫C/C++的函式,這使得Lua在應用程式中可以被廣泛應用。
單純的學習lua並不能完全的發揮lua的作用,lua的一大優點就是能和c/c++無縫連線,而且可以在不需要重複編譯c/c++的情況下可以修改lua檔案並且起作用,當我們的專案檔案很大的時候,使用lua進行專案修改極大的減少了等待時間。
Lua中的棧
lua中的棧是一個很奇特的資料結構,普通的棧只有一排索引,但是在lua中有兩排索引,正數1索引的位置在棧底,負數-1索引的位置在棧頂。如下圖所示。
根據結構圖,我們不需要知道棧的大小,我們就可以確定的棧頂和棧底的位置
當索引是1的時候對應的是棧底
當索引是-1的時候對於的是棧頂。
常用的lua api解釋
lua_State* L=luaL_newstate(); luaL_newstate()函式返回一個指向堆疊的指標
lua_createtable(L,0,0);新建並壓入一張表
lua_pushstring(L,0,0);壓入一個字串
lua_pushnumber(L,0,0);壓入一個數字
lua_tostring(L,1);取出一個字串
lua_tointeger(L,1);取出數字
double b=lua_tonumber();取出一個double型別的數字
lua_load()函式 當這個函式返回0時表示載入
luaL_loadfile(filename) 這個函式也是隻允許載入lua程式檔案,不執行lua檔案。它是在內部去用lua_load()去載入指定名為filename的lua程式檔案。當返回0表示沒有錯誤。
luaL_dofile 這個函式不僅僅載入了lua程式檔案,還執行lua檔案。返回0表示沒有錯誤。
lua_push*(L,data)壓棧,
lua_to*(L,index)取值,
lua_pop(L,count)出棧。
lua_close(L);釋放lua資源
lua_getglobal(L, “val”);//獲取全域性變數的val的值,並將其放入棧頂
檔案目錄結構
makefile檔案
需要注意的是,在用gcc編譯的時候需要加上 -llua -lm -ldl
CC
=
gcc
target
=
main
obj
=
main。o
# gcc all。obj -o main
$(target)
:
$(
obj
)
time
$(
CC
)
$(
obj
)
-o
$(
target
)
-llua -lm -ldl
%。o
:
%。
c
$(
CC
)
-c $< -o
$@
。PHONY
:
clean
clean
:
rm -rf
$(
obj
)
$(
target
)
lua測試程式碼
name
=
“bob”
age
=
20
mystr
=
“hello lua”
mytable
=
{
name
=
“tom”
,
id
=
123456
}
function
add
(
x
,
y
)
return
2
*
x
+
y
end
示例一:透過c語言讀取lua中的變數
#include
#include
“。。/inc/lua。h”
#include
“。。/inc/lualib。h”
#include
“。。/inc/lauxlib。h”
int
main
()
{
lua_State
*
L
=
luaL_newstate
();
luaL_openlibs
(
L
);
int
retLoad
=
luaL_loadfile
(
L
,
“test。lua”
);
if
(
retLoad
==
0
)
{
printf
(
“load file success retLoad:%d
\n
”
,
retLoad
);
}
if
(
retLoad
||
lua_pcall
(
L
,
0
,
0
,
0
))
{
printf
(
“error %s
\n
”
,
lua_tostring
(
L
,
-
1
));
return
-
1
;
}
lua_getglobal
(
L
,
“name”
);
//lua獲取全域性變數name的值並且返回到棧頂
lua_getglobal
(
L
,
“age”
);
//lua獲取全域性變數age的值並且返回到棧頂,這個時候length對應的值將代替width的值成為新棧頂
//注意讀取順序
int
age
=
lua_tointeger
(
L
,
-
1
);
//棧頂
const
char
*
name
=
lua_tostring
(
L
,
-
2
);
//次棧頂
printf
(
“name = %s
\n
”
,
name
);
printf
(
“age = %d
\n
”
,
age
);
lua_close
(
L
)
return
0
;
}
測試結果:
測試是在wsl中進行的
這裡面需要注意的是,當出現多個lua_getglobal()函式的時候由上到下,依次壓入棧,在使用lua_to*(L,index)函式讀取棧頂元素的時候,如果每次壓入棧的資料型別都不一樣,那麼在讀取的是就要注意讀取順序。
當使用lua_tointegers()的是,返回值型別的long long
#define LUA_INTEGER long long
typedef
LUA_INTEGER
lua_Integer
;
LUA_API
lua_Integer
(
lua_tointegerx
)
(
lua_State
*
L
,
int
idx
,
int
*
isnum
);
當使用lua_tolstring()的時候,返回值型別是const char*
函式原型:
LUA_API
const
char
*
(
lua_tolstring
)
(
lua_State
*
L
,
int
idx
,
size_t
*
len
);
當使用lua_tonumber()的是,返回值型別是double
函式原型
#define LUA_NUMBER double
typedef
LUA_NUMBER
lua_Number
;
LUA_API
lua_Number
(
lua_tonumberx
)
(
lua_State
*
L
,
int
idx
,
int
*
isnum
);
示例二:透過c語言呼叫lua中的函式
#include
#include
“。。/inc/lua。h”
#include
“。。/inc/lualib。h”
#include
“。。/inc/lauxlib。h”
int
main
()
{
lua_State
*
L
=
luaL_newstate
();
luaL_openlibs
(
L
);
int
retLoad
=
luaL_loadfile
(
L
,
“test。lua”
);
if
(
retLoad
==
0
)
{
printf
(
“load file success retLoad:%d
\n
”
,
retLoad
);
}
if
(
retLoad
||
lua_pcall
(
L
,
0
,
0
,
0
))
{
printf
(
“error %s
\n
”
,
lua_tostring
(
L
,
-
1
));
return
-
1
;
}
//呼叫函式,依次壓入引數
lua_getglobal
(
L
,
“add”
);
lua_pushnumber
(
L
,
10
);
lua_pushnumber
(
L
,
20
);
//檢視壓入棧的元素
for
(
int
i
=
1
;
i
<
3
;
i
++
)
{
printf
(
“number:%f
\n
”
,
lua_tonumber
(
L
,
-
i
));
}
//lua_pcall(L,2,1,0):傳入兩個引數 期望得到一個返回值,0表示錯誤處理函式在棧中的索引值,壓入結果前會彈出函式和引數
int
pcallRet
=
lua_pcall
(
L
,
2
,
1
,
0
);
//lua_pcall將計算好的值壓入棧頂,並返回狀態值
if
(
pcallRet
!=
0
)
{
printf
(
“error %s
\n
”
,
lua_tostring
(
L
,
-
1
));
return
-
1
;
}
printf
(
“pcallRet:%d
\n
”
,
pcallRet
);
int
val
=
lua_tonumber
(
L
,
-
1
);
//在棧頂取出資料
printf
(
“val:%d
\n
”
,
val
);
lua_pop
(
L
,
-
1
);
//彈出棧頂
//再次檢視棧內元素,發現什麼都沒有,因為lua在返回函式計算值後會清空棧,只保留返回值
for
(
int
i
=
1
;
i
<
3
;
i
++
)
{
printf
(
“number:%f
\n
”
,
lua_tonumber
(
L
,
-
i
));
}
lua_close
(
L
)
return
0
;
}
測試結果:
由結果可以看出來,當呼叫完lua中的函式以後,會自動清空棧,只保留結果在棧頂。
注意:這個時候我們修改一下lua中的add函式:把2改為4
function
add
(
x
,
y
)
return
4
*
x
+
y
end
這時不進行編譯,直接再執行一下./main,可以看到這個結果改變了從40變成了60,這是在我們沒有進行重複編譯的情況下直接產生的變化。
漂亮的證明了lua在c語言裡的嵌入特性,lua中的函式就像是文字一樣被讀取,但是又確實是作為程式被執行。當我們的專案很大的時候,每次編譯都需要幾十分鐘,這個時候如果合理的利用lua特性,僅僅是需要修改lua檔案就可以避免這十幾分鐘的空白時間。
示例三:在c語言裡面呼叫lua中的table
#include
#include
“。。/inc/lua。h”
#include
“。。/inc/lualib。h”
#include
“。。/inc/lauxlib。h”
int
main
()
{
lua_State
*
L
=
luaL_newstate
();
luaL_openlibs
(
L
);
int
retLoad
=
luaL_loadfile
(
L
,
“test。lua”
);
if
(
retLoad
==
0
)
{
printf
(
“load file success retLoad:%d
\n
”
,
retLoad
);
}
if
(
retLoad
||
lua_pcall
(
L
,
0
,
0
,
0
))
{
printf
(
“error %s
\n
”
,
lua_tostring
(
L
,
-
1
));
return
-
1
;
}
printf
(
“讀取lua table中對應的值
\n
”
);
//將全域性變數mytable壓入棧
lua_getglobal
(
L
,
“mytable”
);
//壓入表中的key
lua_pushstring
(
L
,
“name”
);
//lua_gettable會在棧頂取出一個元素並且返回把查詢到的值壓入棧頂
lua_gettable
(
L
,
1
);
const
char
*
name
=
lua_tostring
(
L
,
-
1
);
//在棧頂取出資料
printf
(
“name:%s
\n
”
,
name
);
lua_pushstring
(
L
,
“id”
);
//壓入id
lua_gettable
(
L
,
1
);
//在lua mytable表中取值返回到棧頂
int
id
=
lua_tonumber
(
L
,
-
1
);
//在棧頂取出資料
printf
(
“id:%d
\n
”
,
id
)
lua_close
(
L
)
return
0
;
}
測試結果:
在註釋中可以看得很清楚,lua_gettable(L,1)獲取table物件,同時獲取棧頂元素返回到table中查詢對應的value,再壓入棧中。
所有c語言測試程式:
#include
#include
“。。/inc/lua。h”
#include
“。。/inc/lualib。h”
#include
“。。/inc/lauxlib。h”
#define CODE1
#define _CODE2
#define _CODE3
int
main
()
{
lua_State
*
L
=
luaL_newstate
();
luaL_openlibs
(
L
);
int
retLoad
=
luaL_loadfile
(
L
,
“test。lua”
);
if
(
retLoad
==
0
)
{
printf
(
“load file success retLoad:%d
\n
”
,
retLoad
);
}
if
(
retLoad
||
lua_pcall
(
L
,
0
,
0
,
0
))
{
printf
(
“error %s
\n
”
,
lua_tostring
(
L
,
-
1
));
return
-
1
;
}
#ifdef CODE1
//printf(“呼叫lua中的變數\n”);
lua_getglobal
(
L
,
“name”
);
//lua獲取全域性變數name的值並且返回到棧頂
lua_getglobal
(
L
,
“age”
);
//lua獲取全域性變數age的值並且返回到棧頂,這個時候length對應的值將代替width的值成為新棧頂
//注意讀取順序
int
age
=
lua_tointeger
(
L
,
-
1
);
const
char
*
name
=
lua_tostring
(
L
,
-
2
);
printf
(
“name = %s
\n
”
,
name
);
//棧頂
printf
(
“age = %d
\n
”
,
age
);
//次棧頂
lua_close
(
L
);
#endif
#ifdef CODE2
// printf(“呼叫lua中的函式\n”);
//呼叫函式,依次壓入引數
lua_getglobal
(
L
,
“add”
);
lua_pushnumber
(
L
,
10
);
lua_pushnumber
(
L
,
20
);
//檢視壓入棧的元素
for
(
int
i
=
1
;
i
<
3
;
i
++
)
{
printf
(
“number:%f
\n
”
,
lua_tonumber
(
L
,
-
i
));
}
//lua_pcall(L,2,1,0):傳入兩個引數 期望得到一個返回值,0表示錯誤處理函式在棧中的索引值,壓入結果前會彈出函式和引數
int
pcallRet
=
lua_pcall
(
L
,
2
,
1
,
0
);
//lua_pcall將計算好的值壓入棧頂,並返回狀態值
if
(
pcallRet
!=
0
)
{
printf
(
“error %s
\n
”
,
lua_tostring
(
L
,
-
1
));
return
-
1
;
}
printf
(
“pcallRet:%d
\n
”
,
pcallRet
);
int
val
=
lua_tonumber
(
L
,
-
1
);
//在棧頂取出資料
printf
(
“val:%d
\n
”
,
val
);
lua_pop
(
L
,
-
1
);
//彈出棧頂
//再次檢視棧內元素,發現什麼都沒有,因為lua在返回函式計算值後會清空棧,只保留返回值
for
(
int
i
=
1
;
i
<
3
;
i
++
)
{
printf
(
“number:%f
\n
”
,
lua_tonumber
(
L
,
-
i
));
}
lua_close
(
L
);
#endif
#ifdef CODE3
// printf(“讀取lua table中對應的值\n”);
//將全域性變數mytable壓入棧
lua_getglobal
(
L
,
“mytable”
);
//壓入表中的key
lua_pushstring
(
L
,
“name”
);
//lua_gettable會在棧頂取出一個元素並且返回把查詢到的值壓入棧頂
lua_gettable
(
L
,
1
);
const
char
*
name
=
lua_tostring
(
L
,
-
1
);
//在棧頂取出資料
printf
(
“name:%s
\n
”
,
name
);
lua_pushstring
(
L
,
“id”
);
//壓入id
lua_gettable
(
L
,
1
);
//在lua mytable表中取值返回到棧頂
int
id
=
lua_tonumber
(
L
,
-
1
);
//在棧頂取出資料
printf
(
“id:%d
\n
”
,
id
);
lua_close
(
L
);
#endif
return
0
;
}
希望這篇文章能夠幫到正在學習lua的同學。