Lua源碼下載
本篇主要介紹Lua與C的互調(diào)方式梁沧。
首先下載Lua源碼鸵钝,這里使用Lua5.15。
lua.c和luac.c都有main方法柔逼。lua.c的main是編譯運(yùn)行時(shí)用的犯犁,luac.c的main是編譯一個(gè)將lua代碼編譯成二進(jìn)制代碼的軟件用的,使用exe打包時(shí)需要移除其中一個(gè),這里移除luac.c籽慢。
項(xiàng)目地址
Lua提供了下方三個(gè)偽索引浸遗,所謂偽索引就是數(shù)據(jù)不在棧上,卻可以像操作棧內(nèi)元素一樣使用箱亿。這里我們主要查看LUA_REGISTRYINDEX為注冊(cè)表索引跛锌,可通過(guò)該索引訪問(wèn)到注冊(cè)表。該表內(nèi)只有兩個(gè)子表:_LOADED和_LOADLIB届惋。_LOADED表用于存儲(chǔ)模塊髓帽,默認(rèn)有string、table脑豹、coroutine郑藏、package、_G等Lua基礎(chǔ)模塊瘩欺,_LOADLIB表內(nèi)只有一個(gè)__gc函數(shù)译秦。
LUA_GLOBALSINDEX為全局表索引,即可通過(guò)該索引訪問(wèn)到上面的_G模塊击碗。
#define LUA_REGISTRYINDEX (-10000)
#define LUA_ENVIRONINDEX (-10001)
#define LUA_GLOBALSINDEX (-10002)
//iapi.c
static TValue *index2adr (lua_State *L, int idx) {
if (idx > 0) {
TValue *o = L->base + (idx - 1);
api_check(L, idx <= L->ci->top - L->base);
if (o >= L->top) return cast(TValue *, luaO_nilobject);
else return o;
}
else if (idx > LUA_REGISTRYINDEX) {
api_check(L, idx != 0 && -idx <= L->top - L->base);
return L->top + idx;
}
else switch (idx) { /* pseudo-indices */
case LUA_REGISTRYINDEX: return registry(L); //獲取注冊(cè)表
case LUA_ENVIRONINDEX: { //獲取當(dāng)前函數(shù)環(huán)境
Closure *func = curr_func(L);
sethvalue(L, &L->env, func->c.env);
return &L->env;
}
case LUA_GLOBALSINDEX: return gt(L); //獲取全局表
default: {
Closure *func = curr_func(L);
idx = LUA_GLOBALSINDEX - idx;
return (idx <= func->c.nupvalues)
? &func->c.upvalue[idx-1]
: cast(TValue *, luaO_nilobject);
}
}
}
Lua通過(guò)require加載模塊筑悴,首先會(huì)在package.loaded表中查找(loaded表也就是使用LUA_REGISTRYINDEX訪問(wèn)到的表),不存在再加載稍途。
當(dāng)我們需要為L(zhǎng)ua制作插件時(shí)阁吝,可以使用CPP項(xiàng)目生成dll文件供lua調(diào)用。
下方為加載模塊模板械拍,函數(shù)名格式為luaopen_DLL文件名_自定義模塊名突勇。
__declspec(dllexport) int luaopen_LuaAPI_core(lua_State* L);
#include <iostream>
extern "C"
{
#include "lua.h"
#include "LuaAPI.h"
#include "lauxlib.h"
}
static int Hello(lua_State* L)
{
std::cout << "hello" << std::endl;
return 0;
}
static luaL_reg Functions[] = //注冊(cè)函數(shù)表,函數(shù)名對(duì)應(yīng)函數(shù)地址
{
{"hello",Hello},
};
int luaopen_LuaAPI_core(lua_State * L)
{
luaL_openlib(L, "luo",Functions,0); //將luo模塊加入_LOADED表內(nèi)坷虑,可以改為加入_G表內(nèi)
std::cout << "LuaAPI.core注冊(cè)完成" << std::endl; //require "LuaAPI.core"
return 1; //表示返回一個(gè)參數(shù)
}
lua調(diào)用
local core=require "LuaAPI.core"
core.hello(); --或者luo.hello
上方Lua代碼只是調(diào)用了一個(gè)C函數(shù)甲馋,并沒(méi)有形參和返回值接口。Lua與宿主語(yǔ)言的交互通過(guò)一個(gè)虛擬棧調(diào)用迄损,也就是說(shuō)所有信息都包含在LuaState這個(gè)結(jié)構(gòu)體內(nèi)定躏。
Lua可以將參數(shù)壓入棧內(nèi)傳到C函數(shù)內(nèi),C函數(shù)從棧中獲取參數(shù)并處理后可以將返回值壓入棧內(nèi)芹敌,傳到Lua中痊远。這里實(shí)現(xiàn)Lua調(diào)用一個(gè)C函數(shù)完成加法操作
static int add(lua_State* L)
{
if (lua_type(L, -2) == LUA_TNUMBER && lua_type(L, -1) == LUA_TNUMBER) //參數(shù)1和2都為number類型
{
//參數(shù)從左到右入棧,則從右到左出棧
int a=lua_tointeger(L, -2); //獲取參數(shù)1
int b = lua_tointeger(L, -1); //獲取參數(shù)2
lua_pop(L,2); //彈出兩個(gè)參數(shù)
int sum = a + b;
lua_pushinteger(L, sum); //將返回值壓入棧
}
else
{
lua_error(L);
}
return 1; //表示返回一個(gè)參數(shù)
}
//見(jiàn)上氏捞,注冊(cè)函數(shù)中加入add
static luaL_reg Functions[] =
{
{"hello",Hello},
{"add",add},
};
lua調(diào)用
local core=require "LuaAPI.core"
core.hello(); --或者luo.hello
local result=core.add(1,2);
print(result)
附錄
一個(gè)簡(jiǎn)單的打印棧函數(shù)碧聪,不打印嵌套表
static std::string typeNames[]=
{
"nil",
"boolean",
"light userdata" ,
"number",
"string",
"table",
"function",
"userdata",
"thread",
};
static void print_table(lua_State* L, int index, int nIndex)
{
printf("%d\t table\t", index);
lua_pushvalue(L, index); //將需打印的table復(fù)制至棧頂
lua_pushnil(L); //壓入初始key
while (lua_next(L, -2)) //遍歷表
{
switch (lua_type(L,-2))//key值
{
case LUA_TBOOLEAN:
std::cout << "key:" << (lua_toboolean(L, -2) == 1) << "\t";
break;
case LUA_TNUMBER:
std::cout << "key:" << lua_tonumber(L, -2) << "\t";
break;
case LUA_TSTRING:
std::cout << "key:" << lua_tostring(L, -2) << "\t";
break;
default:
std::cout << "key type:" << typeNames[lua_type(L, -2)] << "\t";
break;
}
std::cout << ",";
switch (lua_type(L, -1))//value值
{
case LUA_TBOOLEAN:
std::cout << "value:" << (lua_toboolean(L, -1) == 1) << "\t";
break;
case LUA_TNUMBER:
std::cout << "value:" << lua_tonumber(L, -1) << "\t";
break;
case LUA_TSTRING:
std::cout << "value:" << lua_tostring(L, -1) << "\t";
break;
default: //表嵌套未實(shí)現(xiàn)
std::cout << "value type:" << typeNames[lua_type(L, -1)] << "\t";
break;
}
lua_pop(L, 1); //彈出一個(gè)value液茎,才能獲取下一個(gè)value
}
lua_pop(L, 1); //彈出之前復(fù)制的表
printf("%10d\n", nIndex);
}
static int print_stack(lua_State* L)
{
int stackCount = lua_gettop(L);
if (stackCount==0)
{
return 0;
}
std::cout << "====================棧頂===================" << std::endl;
for (int i = stackCount; i > 0; i--) //存在table類型的話需要從右往左取參逞姿,所以一般從棧底開(kāi)始
{
int nIndex = i - stackCount - 1;
switch (lua_type(L, i))
{
case LUA_TNIL:
printf("%d\t nil \t%d\n", i, nIndex);
break;
case LUA_TBOOLEAN:
printf("%d\t boolean:%d \t%d\n", i, lua_toboolean(L, i) == 1, nIndex);
break;
case LUA_TLIGHTUSERDATA:
printf("%d\t light userdata \t%d\n", i, nIndex);
break;
case LUA_TNUMBER:
printf("%d\t number:%lf \t%d\n", i, lua_tonumber(L, nIndex), nIndex);
break;
case LUA_TSTRING:
printf("%d\t string:%s \t%d\n", i, lua_tostring(L, i), nIndex);
break;
case LUA_TTABLE:
print_table(L, i, nIndex);
break;
case LUA_TFUNCTION:
printf("%d\t function \t%d\n", i, nIndex);
break;
case LUA_TUSERDATA:
printf("%d\t userdata \t%d\n", i, nIndex);
break;
case LUA_TTHREAD:
printf("%d\t thread \t%d\n", i, nIndex);
break;
}
}
std::cout << "====================棧尾===================" << std::endl;
return 0;
}
Lua文件的加載路徑為package.path辞嗡,可自行修改,默認(rèn)使用exe文件路徑為相對(duì)路徑的基準(zhǔn)滞造。
Lua默認(rèn)不支持中文變量欲间,可以修改llex.c的llex方法使其支持,有三處需要修改断部,在下方標(biāo)出猎贴。
llex.c的422行到431行
else if (isalpha(ls->current) || ls->current == '_' || ls->current > 0X80) //第一處ls->current >0x80
{
/* identifier or reserved word */
TString *ts;
do {
if (ls->current > 0X80) //第二處
{
save_and_next(ls);
}
save_and_next(ls);
} while (isalnum(ls->current) || ls->current == '_' || ls->current > 0X80); //第三處