Lua与C++的交互

参考:

https://blog.csdn.net/v_xchen_v/article/details/77249332
https://blog.csdn.net/xiaoluoshan/article/details/53155758

Lua堆栈

Lua和C++ 的交互机制的基础在于Lua提供了一个虚拟栈,C++ 和Lua之间的所有类型的数据交换都通过这个栈完成。无论何时C想从Lua中调用一个值,被请求的值将会被压入栈,无论何时C想要传递一个值给Lua,首先将整个值压栈,然后就可以在Lua中调用。栈的特点是先进后出:

image.png

堆栈索引的方式可是是正数也可以是负数,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶。

Lua栈可以存储数字、字符串、表、闭包等,它们最终都用TValue这种数据结构来保存:

image.png

TValue结构对应于lua中的所有数据类型,是一个{值, 类型} 结构,它把值和类型绑在一起,用tt记录value的类型,value是一个联合结构,由Value定义,可以看到这个联合有四个成员:

  • p:可以存一个指针, 实际上是lua中的light userdata结构
  • n:所有的数值存在这里
  • b:Boolean值存在这里
  • gc:gc是一个指针,它可以指向的类型由联合体GCObject定义,诸如table、thread、closure、string等需要内存管理垃圾回收的类型都存在这里
    可以得出如下结论:
  1. lua中,number、boolean、nil、light userdata四种类型的值是直接存在栈上元素里的,和垃圾回收无关。
  2. lua中,string、table、closure、userdata、thread存在栈上元素里的只是指针,,他们都会在生命周期结束后被垃圾回收。

Lua API

/*
** access functions (stack -> C)
*/

LUA_API int             (lua_isnumber) (lua_State *L, int idx);
LUA_API int             (lua_isstring) (lua_State *L, int idx);
LUA_API int             (lua_iscfunction) (lua_State *L, int idx);
LUA_API int             (lua_isuserdata) (lua_State *L, int idx);
LUA_API int             (lua_type) (lua_State *L, int idx);
LUA_API const char     *(lua_typename) (lua_State *L, int tp);

LUA_API int            (lua_equal) (lua_State *L, int idx1, int idx2);
LUA_API int            (lua_rawequal) (lua_State *L, int idx1, int idx2);
LUA_API int            (lua_lessthan) (lua_State *L, int idx1, int idx2);

LUA_API lua_Number      (lua_tonumber) (lua_State *L, int idx);
LUA_API lua_Integer     (lua_tointeger) (lua_State *L, int idx);
LUA_API int             (lua_toboolean) (lua_State *L, int idx);
LUA_API const char     *(lua_tolstring) (lua_State *L, int idx, size_t *len);
LUA_API size_t          (lua_objlen) (lua_State *L, int idx);
LUA_API lua_CFunction   (lua_tocfunction) (lua_State *L, int idx);
LUA_API void           *(lua_touserdata) (lua_State *L, int idx);
LUA_API lua_State      *(lua_tothread) (lua_State *L, int idx);
LUA_API const void     *(lua_topointer) (lua_State *L, int idx);


/*
** push functions (C -> stack)
*/
LUA_API void  (lua_pushnil) (lua_State *L);
LUA_API void  (lua_pushnumber) (lua_State *L, lua_Number n);
LUA_API void  (lua_pushinteger) (lua_State *L, lua_Integer n);
LUA_API void  (lua_pushlstring) (lua_State *L, const char *s, size_t l);
LUA_API void  (lua_pushstring) (lua_State *L, const char *s);
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
                                                      va_list argp);
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
LUA_API void  (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
LUA_API void  (lua_pushboolean) (lua_State *L, int b);
LUA_API void  (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API int   (lua_pushthread) (lua_State *L);


/*
** get functions (Lua -> stack)
*/
LUA_API void  (lua_gettable) (lua_State *L, int idx);
LUA_API void  (lua_getfield) (lua_State *L, int idx, const char *k);
LUA_API void  (lua_rawget) (lua_State *L, int idx);
LUA_API void  (lua_rawgeti) (lua_State *L, int idx, int n);
LUA_API void  (lua_createtable) (lua_State *L, int narr, int nrec);
LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
LUA_API int   (lua_getmetatable) (lua_State *L, int objindex);
LUA_API void  (lua_getfenv) (lua_State *L, int idx);


/*
** set functions (stack -> Lua)
*/
LUA_API void  (lua_settable) (lua_State *L, int idx);
LUA_API void  (lua_setfield) (lua_State *L, int idx, const char *k);
LUA_API void  (lua_rawset) (lua_State *L, int idx);
LUA_API void  (lua_rawseti) (lua_State *L, int idx, int n);
LUA_API int   (lua_setmetatable) (lua_State *L, int objindex);
LUA_API int   (lua_setfenv) (lua_State *L, int idx);

此外还提供了API函数来人工控制堆栈:

/*
** basic stack manipulation
*/
LUA_API int   (lua_gettop) (lua_State *L);
LUA_API void  (lua_settop) (lua_State *L, int idx);
LUA_API void  (lua_pushvalue) (lua_State *L, int idx);
LUA_API void  (lua_remove) (lua_State *L, int idx);
LUA_API void  (lua_insert) (lua_State *L, int idx);
LUA_API void  (lua_replace) (lua_State *L, int idx);
LUA_API int   (lua_checkstack) (lua_State *L, int sz);

示例:

#include "lua.hpp"  
#include <iostream>
int main()
{
    //创建一个state
    lua_State *L = luaL_newstate();
    //入栈
    lua_pushstring(L,"i am testing lua & c++");
    lua_pushnumber(L,123);

    //读栈取值
    if(lua_isstring(L,-2))//或if(lua_isstring(L,1))
    {
        std::cout<<lua_tostring(L,-2)<<std::endl;
    }
    if(lua_isnumber(L,-1))
    {
        std::cout<<lua_tonumber(L,-1)<<std::endl;
    }

    //关闭state
    lua_close(L);
    return 0;
}

C++ 调用 Lua

C++ 可以获取Lua中的值,可以调用Lua函数,还可以修改Lua文件。

1. 获取Lua值

  1. 使用lua_getglocal来获取值并将其压栈(table还需使用lua_getfield)。
  2. 使用C API lua_toXXX将栈中元素取出转成相应类型的值。

2. C调用Lua函数

  1. 使用lua_getglocal来获取函数并将其压栈。
  2. 如果这个函数有参数的话,就需要依次将函数的参数也压入栈。
  3. 调用lua_pcall开始调用函数,调用完成以后,会将返回值压入栈中。
  4. 取返回值,调用完毕。

3. 示例:

#luac.lua
name = "xchen"
version = 1
me = { name = "xchen", gender = "female"}
function add (a,b)
    return a+b
end
#include "lua.hpp"
#include <iostream>
using namespace std;

//显示栈内情况
static void stackDump(lua_State* L);

int main()
{
    //创建一个state
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);//打开lua给我们提供的标准库

    //读lua文件
    int fret = luaL_dofile(L,"luac.lua");
    if(fret)
    {
        std::cout<<"read lua file error!"<<std::endl;
    }

    //读取变量
    lua_getglobal(L,"name");   //string to be indexed
    std::cout<<"name = "<<lua_tostring(L,-1)<<std::endl;

    //读取数字
    lua_getglobal(L,"version"); //number to be indexed
    std::cout<<"version = "<<lua_tonumber(L,-1)<<std::endl;

    //读取表
    lua_getglobal(L, "me");  //table to be indexed
    if(!lua_istable(L,-1))
    {
        std::cout<<"error:it is not a table"<<std::endl;
    }
    //取表中元素
    lua_getfield(L, -1 ,"name");
    std::cout<<"student name = "<<lua_tostring(L,-1)<<std::endl;
    lua_getfield(L,-2,"gender");
    std::cout<<"student gender = "<<lua_tostring(L,-1)<<std::endl;

    //修改表中元素
    lua_pushstring(L, "007");
    lua_setfield(L,-4, "name");
    lua_getfield(L, -3 ,"name");
    std::cout<<"student newName = "<<lua_tostring(L,-1)<<std::endl;

    //取函数
    lua_getglobal(L,"add");
    lua_pushnumber(L,15);
    lua_pushnumber(L,5);
    lua_pcall(L,2,1,0);//2-参数格式,1-返回值个数,调用函数,函数执行完,会将返回值压入栈中
    std::cout<<"5 + 15 = "<<lua_tonumber(L,-1)<<std::endl;

    //查看栈
    stackDump(L);

    //关闭state
    lua_close(L);
    return 0;
}

static void stackDump(lua_State* L) {
    cout << "\nbegin dump lua stack" << endl;
    int i = 0;
    int top = lua_gettop(L);
    for (i = 1; i <= top; ++i) {
        int t = lua_type(L, i);
        switch (t) {
            case LUA_TSTRING: {
                printf("'%s' ", lua_tostring(L, i));
            }
                break;
            case LUA_TBOOLEAN: {
                printf(lua_toboolean(L, i) ? "true " : "false ");
            }
                break;
            case LUA_TNUMBER: {
                printf("%g ", lua_tonumber(L, i));
            }
                break;
            default: {
                printf("%s ", lua_typename(L, t));
            }
                break;
        }
    }
    cout << "\nend dump lua stack" << endl;
}
image.png

Lua 调用 C++

Lua可以调用C++的函数,步骤为:

  1. 将C的函数包装成Lua环境认可的函数
  2. 将包装好的函数注册到Lua环境中
  3. 像使用普通Lua函数那样使用注册函数

1. 包装C函数

将被调用的C函数从普通的C函数包装成Lua_CFunction格式,并需要在函数中将返回值压入栈中,并返回返回值个数:

int (Lua_CFunction*)(lua_state*)
{
    // c code        // 实现逻辑功能
    // lua_push code // 需要将返回值压入堆栈
    return n;        // n为具体的返回值个数,告诉解释器,函数向堆栈压入几个返回值
}

示例:

int add(int a,int b)
{
    return a+b;
}
int add(lua_state*L)
{
    int a = lua_tonumber(-1);
    int b = lua_tonumber(-2);
    int sum = a+b;
    lua_pushnumber(L,sum);
    return 1;
}

2. 注册C函数到Lua环境

lua_register(L,"Add2Number",add);//将c函数add注册到全局table[Add2Number]中

lua_register是一个宏,对应两个函数:lua_pushfunction(L,f)和lua_setglobal(L,n),将函数存放在一个全局table中。

3. 示例(使用就不写了)

#test.lua
print("Hi! " .. sayHi("xchen"))
#include "lua.hpp"
#include <iostream>
using namespace std;
//C++中定义、实现函数sayHi
int sayHi(lua_State *L)
{
    //获取lua函数中的第一个参数
    string name = luaL_checkstring(L,1);
    //压栈
    lua_pushstring(L,name.data());
    return 1;
}
int main()
{
    //创建一个state
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    //为Lua注册名为第一个参数的函数,实际上是调用c++中第三个参数名的函数
    lua_register(L, "sayHi" ,sayHi);

    //读lua文件并运行Lua code
    int fret = luaL_dofile(L,"test.lua");
    if(fret)
    {
        std::cout<<"read lua file error!"<<std::endl;
    }

    //关闭state
    lua_close(L);
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容