lua与c接口编程

简单描述下lua 虚拟栈的索引

假如存在十个元素  st[10]=st[-1]=st.top()  st[1]=st[-10]=栈底部。

下面代码中用到的lua cAPI

  1. set_newlib()
    void luaL_newlib (lua_State *L, const luaL_Reg l[]);

    创建一个新表并在那里注册 列表中的函数。

    它通过以下宏实现:

         (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
    

    数组必须是实际数组, 不是指向它的指针。

    void luaL_newlibtable (lua_State *L, const luaL_Reg l[]);

    创建一个大小优化的新表 将所有条目存储在数组中(但实际上并不存储它们)。 它旨在与luaL_setfuncs结合使用(见luaL_newlib)。

    它是作为宏实现的。 数组必须是实际数组, 不是指向它的指针。

  2. lua_newuserdata()

    void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue);

    此函数在堆栈上创建并推送一个新的完整用户数据, 具有关联的 Lua 值,称为 , 加上一个与字节相关的原始内存块。 (可以使用函数 lua_setiuservalue 和 lua_getiuservalue 设置和读取用户值。nuvalueuser valuessize

    该函数返回内存块的地址。 Lua 确保此地址在 相应的 UserData 是活动的(参见 §2.5)。 此外,如果用户数据被标记为最终确定(参见第 2.5.3 节), 其地址至少在调用其终结器之前有效。

  3. luaL_checkudata()

    void *luaL_checkudata (lua_State *L, int arg, const char *tname);

    检查函数参数是否为 userdata 的类型(见luaL_newmetatable)和 返回 UserData 的内存块地址(参见 lua_touserdata)。

  4. lua_createtable()

    void lua_createtable (lua_State *L, int narr, int nrec);

    创建一个新的空表并将其推送到堆栈上。 参数是表中元素数的提示 将作为一个序列; 参数是指示有多少其他元素 该表将有。 Lua 可能会使用这些提示为新表预分配内存。 当您提前知道时,这种预分配可能有助于性能 表格将包含多少个元素。 否则,您可以使用该函数lua_newtable

  5. luaL_newmetatable()

    int luaL_newmetatable (lua_State *L, const char *tname);

    如果注册表已具有项 , 返回 0。 否则 创建一个新表,用作 UserData 的元表, 将 pair 添加到这个新表中, 将 pair 添加到注册表中 , 并返回 1。tname__name = tname[tname] = new table

    在这两种情况下, 该函数将关联的最终值推送到堆栈上 在注册表中。

  6. lua_setfield() 

    int lua_setmetatable (lua_State *L, int index);

    从堆栈中弹出一个表或 nil,然后 将该值设置为给定索引处值的新元表。 现在始终为 1。

  7. lua_pushvalue()

    void lua_pushvalue (lua_State *L, int index);

    在给定索引处推送元素的副本 到堆栈上。

  8. lua_setuservalue()

    int lua_setiuservalue (lua_State *L, int index, int n);

    从堆栈中弹出一个值并将其设置为 与 给定索引处的完整用户数据。 如果 userdata 没有该值,则返回 0。

  9. lua_getuservalue()

    int lua_getiuservalue (lua_State *L, int index, int n);

    将与 给定索引处的完整用户数据和 返回推送值的类型。n

    如果 userdata 没有该值, 按 nil 并返回 LUA_TNONE

  10. lua_seti()

    void lua_seti (lua_State *L, int index, lua_Integer n);

    是否等同于 , 其中 是给定索引处的值 and 是堆栈顶部的值。t[n] = vtv

    此函数从堆栈中弹出值。 与 Lua 一样,此函数可能会触发元方法 对于“NewIndex”事件(参见 §2.4)。

  11. lua_geti()

    int lua_geti (lua_State *L, int index, lua_Integer i);

    将值 , 其中 是给定索引处的值。 与 Lua 一样,此函数可能会触发元方法 对于“index”事件(参见 §2.4)。t[i]t

    返回推送值的类型。

  12. lua_pop()

    void lua_pop (lua_State *L, int n);

    从堆栈中弹出元素。 它是在lua_settop上作为宏实现的

一 c 如何调用lua 代码

lua代码如下

-- 文件名 c.lua
function Init(... )
    print("Init call")
    -- body
end
function Start( ...)
    print("start",...)
    -- body
end

c代码如下

//文件名lua.c
#include<stdio.h>
#include<stdlib.h>
#include<lua.h>
#include<lauxlib.h>
#include<lualib.h>

static void
callFuncFromLua(lua_State*L,const char*funcName){
    lua_getglobal(L,funcName);
    lua_call(L,0,0);
}
static void
callFuncFromLuaWithParm(lua_State*L,const char*funcName,int parm){
    lua_getglobal(L,funcName);
    lua_pushnumber(L,parm);
    lua_call(L,1,0);
}

int main(void){
    const char* path="./c.lua";
    //创建lua虚拟机
    lua_State*L=luaL_newstate();
    luaL_openlibs(L);
    //编译并加载
    if(luaL_dofile(L,path)!=LUA_OK){
        const char* err=lua_tostring(L,-1);
        fprintf(stderr,"err number:%s\n ",err);
    }
    int parm=666;
    callFuncFromLua(L,"Init");
    callFuncFromLuaWithParm(L,"Start",parm);
    
    lua_close(L);
    return 0;
}

二   lua 如何调用c代码

思路 :首先将c代码编译成动态库 ,动态库返回 lua表供lua使用

so.c

#include<stdio.h>
#include<stdlib.h>
#include<lua.h>
#include<lualib.h>
#include<lauxlib.h>
/**
 * upvalue 上值
 * c 闭包 多个上值 c函数
 *  gcc cso.c  -o uv.so -fPIC --shared
*/
static int echo(lua_State*L){
 
    lua_Integer cnt=lua_tointeger(L,lua_upvalueindex(1));//获取第一个上值
    const char* str1=lua_tostring(L,lua_upvalueindex(2));
    printf("上值元素为%s\n",str1);
    cnt++;
    const char*str=lua_tostring(L,-1);
    printf("used [times=%lld] str:%s\n",cnt,str);
    lua_pushinteger(L,cnt);
    lua_replace(L,lua_upvalueindex(1));
    return 0;
}

int luaopen_uv(lua_State*L){
    
    lua_createtable(L,0,0);    //创建一个新的空表压入虚拟栈
    lua_pushinteger(L,0);         //代表函数echo的上值(1)
    lua_pushstring(L,"aron yang");//代表函数echo的上值(2)
    lua_pushcclosure(L,echo,2);   //2代表函数echo的上值个数
    lua_setfield(L,-2,"echo"); //该函数用于将栈顶的值赋给指定表中的字段。如果表不存在,则会自动创建。
    return 1;
}

test.lua

-- 加载c动态库 找到uv.so
package.cpath = package.cpath .. ";./?.so;"
local uv=require "uv" -- 从c代码中得到一个k,v表

print("hello world")

三、c代码申请一块内存userdata供lua使用

实现功能 能够创建一个log对象,可以记录打印信息的次数并且可以打印10次最近的历史记录

lua文件不过多描述

c文件

首先创建一个tab ={"newlog",newlog}表, tab["newlog"]= newlog(函数指针)

 然后把这个表返回到lua层 ,因此lua代码即可调用 tab中函数。

newlog() 函数实现 :调用lua_newuserdata() 相当于malloc 一块内存,将这块内存转化为用户定义的对象类型,初始化该对象的成员变量。

lua_createtable(L,10,0)即创建了一个类似于stirng[10]的循环数组来存放打印记录

luaL_newmetatable()创建一个元表并且设置好元方法,并绑定到userdata。(类似于类中成员函数绑定到这个类中)

因此这块内存中既有了成员变量也有了成员函数。

lua文件

package.cpath = package.cpath .. ";./?.so;"
local ud=require "ud"

local log=ud.new()
log:printLog("yangdonghai")


local log1=ud.new()

log1:printLog("民主")
log1:printLog("富强")
log1:printLog("爱国")
log1:printLog("敬业")
log1:printLog("文明")
log1:printLog("友善")
log1:printLog("法制")
log1:printLog("和谐")
log1:printLog("自由")
log1:printLog("平等")
log1:printLogHistory()

c文件

#include<stdio.h>
#include<stdlib.h>
#include<lua.h>
#include<lualib.h>
#include<lauxlib.h>

struct log{
    int cnt;
};
static int printLog(lua_State*L){
    struct log*ptr=(struct log*)luaL_checkudata(L,1,"aron.log.newLog");
    ptr->cnt++;
    const char*str=lua_tostring(L,-1);
    printf("print[%s]cnt[%3d] \n",str,ptr->cnt);
    lua_getuservalue(L,1);//从userdata获取到用于存放记录的表 1为userdata索引
    lua_pushvalue(L,2);//把索引为二的值(str)复制到栈顶
    lua_seti(L,3,(ptr->cnt-1)%10+1);//把栈顶元素设置到索引为3的数组当中
    return 0;
}
static int printLogHistory(lua_State*L){
    struct log*ptr=(struct log*)luaL_checkudata(L,1,"aron.log.newLog");
    lua_getuservalue(L,1);
    if(ptr->cnt<=10){
        for(int i=ptr->cnt;i>=1;i--){
            lua_geti(L,-1,i);// -1 代表虚拟栈中 userdata关联的数组
            printf("cnt=%3d : %s\n",i,lua_tostring(L,-1));
            lua_pop(L,1);
        }
    }else{
        for(int i=0;i<10;i++){
            int cnt=ptr->cnt-i;
            int idx=(cnt-1)%10+1;
            lua_geti(L,-1,idx);//从userdata关联的数组中弹出str到虚拟栈中
            printf("cnt=%3d : %s\n",cnt,lua_tostring(L,-1));
            lua_pop(L,1);
        }
    }

    return 0;
}
static int newLog(lua_State*L){
    struct log*ptr=(struct log*)lua_newuserdata(L,sizeof(struct log));
    ptr->cnt=0;
    lua_createtable(L,10,0);//创建一个数组用于保存历史记录
    lua_setuservalue(L,-2);//这个tab绑定到userdata中
    if(luaL_newmetatable(L,"aron.log.newLog")){
        luaL_Reg tab[]={
            {"printLog",printLog},
            {"printLogHistory",printLogHistory},
            {NULL,NULL},
        };
        luaL_newlib(L,tab);// tab [printLog]=printLog
        lua_setfield(L,-2,"__index");
        //lua_setmetatable(L,-2);
        
    }
    lua_setmetatable(L,-2);
    return 1;
}


static const luaL_Reg l[]={
    {"new",newLog},
    {NULL,NULL},
};
int luaopen_ud(lua_State*L){
    luaL_newlib(L,l);//返回一张表出去
    return 1;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值