C與Lua的互調(diào)

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)
Lua虛擬棧

附錄

一個(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); //第三處

tolua之C#與Lua的交互方式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蝴光,隨后出現(xiàn)的幾起案子她渴,更是在濱河造成了極大的恐慌,老刑警劉巖蔑祟,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趁耗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡疆虚,警方通過(guò)查閱死者的電腦和手機(jī)苛败,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)径簿,“玉大人罢屈,你說(shuō)我怎么就攤上這事∑ぃ” “怎么了缠捌?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)译蒂。 經(jīng)常有香客問(wèn)我曼月,道長(zhǎng),這世上最難降的妖魔是什么柔昼? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任哑芹,我火速辦了婚禮,結(jié)果婚禮上捕透,老公的妹妹穿的比我還像新娘聪姿。我一直安慰自己,他們只是感情好激率,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布咳燕。 她就那樣靜靜地躺著勿决,像睡著了一般乒躺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上低缩,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天嘉冒,我揣著相機(jī)與錄音曹货,去河邊找鬼。 笑死讳推,一個(gè)胖子當(dāng)著我的面吹牛顶籽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播银觅,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼礼饱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了究驴?” 一聲冷哼從身側(cè)響起镊绪,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎洒忧,沒(méi)想到半個(gè)月后蝴韭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡熙侍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年榄鉴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛉抓。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡庆尘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出巷送,到底是詐尸還是另有隱情减余,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布惩系,位于F島的核電站位岔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏堡牡。R本人自食惡果不足惜抒抬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晤柄。 院中可真熱鬧擦剑,春花似錦、人聲如沸芥颈。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)爬坑。三九已至纠屋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盾计,已是汗流浹背售担。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工赁遗, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人族铆。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓岩四,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親哥攘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子剖煌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容