lua 和 c

lua程序其实本身并不能执行,它必须依靠c语言编写的解释器来解释执行,或者说解释器为lua脚本的执行,提供了一个运行环境(lua_state),其中包括函数堆栈,内存分配和回收等机制。

理论上,lua可以使用c提供的功能,如果需要在lua中使用我们特需的功能,我们可以通过编写自己的c库来进行扩展,当然,c也可以通过操作栈的方式来操作lua,这就是lua的强大之处。

在c中通过lua_state中的栈来操作lua

首先需要先引入头文件:

extern "C" {

#include "lua/lua.h"

#include "lua/lauxlib.h"

#include "lua/lualib.h"

}

创建一个运行lua脚本的环境:

lua_State *L = luaL_newstate();

luaL_openlibs(L); // 打开标准库

编译并且调用lua脚本,成功以后就可以用L来操作lua数据了:

if (luaL_loadfile(L, "./res/lua1.lua") || lua_pcall(L, 0, 0, 0)) { // 如果运行成功,两个函数都返回0,失败返回非0

lua_error(L);

}

获取lua文件中的全局变量:

lua_getglobal(L, "width"); // 获取变量名为width的全局变量,并压入栈,由于是第一个入栈,可以通过索引1或者-1来引用

LUA_NUMBER width = lua_tonumber(L, -1); // 获取栈中的width数据

获取lua文件中的table,如果需要获取数组元素,只能使用方式一,并使用lua_pushinteger(L, number):

方式一:

lua_getglobal(L, "size"); // 假如全局变量中有一个size的table,其中有"x"和"y"字段,必须先将table入栈

lua_pushstring(L, "x"); // 将‘‘x"字段入栈

lua_gettable(L, -2); // 从table中获取"x"字段的值,table现在的索引为-2,并将x的值入栈

lua_pushsting(L, "y"); // 将"y"字段入栈

lua_gettable(L, -3); // 获取y的值并入栈,table现在的索引为-3

方式二:

lua_getglobal(L, "size");

lua_getfield(L, -1, "x");

lua_getfield(L, -2, "y");

获取数据:

LUA_NUMBER x = lua_tonumber(L, "x");

LUA_NUMBER y = lua_tonumber(L, "y");

除了获取lua中的全局变量,我们还可以创建新的全局变量:

1. 创建一个number类型的全局变量:

lua_pushnumber(L, 123); // 先将一个number入栈

lua_setglobal(L, "count"); // 将123设置为全局变量count的值,并将123出栈

lua_getglobal(L, "count"); // 获取count的值并入栈

LUA_NUMBER count = lua_tonumber(L, -1);

2. 创建一个table类型的全局变量:

lua_newtable(L); // 创建一个新的table,并入栈

lua_pushnumber(L, 111); // 将一个number入栈

lua_setfield(L, -2, "x"); // 将111赋值给x字段,并将111出栈

lua_setglobal(L, "point"); // 将新的table的全局变量名设置为point,table出栈

lua_getglobal(L, "point"); // 获取新创建的table,point也相当于lua脚本中的一个全局变量了

c调用lua中的函数,函数调用同样需要操作栈来实现:

lua中的一个函数:

function lua_func (x, y)

  print("lua_func", x, y)

  return {x = x, y = y}

end

c调用lua_func:

lua_getglobal(L, "lua_func"); // 同样需要先获取函数,并入栈,其实lua函数和普通类型一样,只是函数是一种函数类型,函数执行体就相当于函数类型的值

lua_pushnumber(L, 12); // 将参数入栈,从前向后的顺序,现在入栈的是将会传递给x的参数

lua_pushnumber(L, 24); // 将会传递给y的参数

if (lua_pcall(L, 2, 1, 0)) { // 调用成返回0,否则返回非0值。2代表2个参数,1代表一个返回值,0是只发生错误时候,错误处理函数在堆栈中的索引,因此如果需要进行错误处理,需要先

              将错误出来函数入栈

  lua_error(L); // 如果调用失败,lua会将一条错误消息入栈

}

lua_getfield(L, -1, "x"); // 由于返回值是一个table,所以需要将table中的字段提取出来,并入栈

lua_getfield(L, -2, "y");

编写一个辅助函数,可以查看当时的堆栈情况:

static void dump(lua_State* L) {
    for (int i = 1, top = lua_gettop(L); i <= top; ++i) {
        int type = lua_type(L, i);
        switch (type) {
            case LUA_TSTRING:
                printf("%d %s \n", i, lua_tostring(L, i));
                break;
            case LUA_TBOOLEAN:
                printf("%d %s \n", i, lua_toboolean(L, i) ? "true" : "false");
                break;
            case LUA_TNUMBER:
                printf("%d %f \n", i, lua_tonumber(L, i));
                break;
            case LUA_TTABLE:
                printf("%d table \n", i);
                break;
            default:
                printf("%d type:%d %s \n", i, type, lua_typename(L, i));
                break;
        }
    }
}

1. 在c中操作lua的全局变量:

lua文件:

width = 200
height = 500
color = {r = 255, g = 56, b = 123, a = 255}
point  = {}

c文件:

  // 创建一个lua状态,用于保存脚本的所有数据   lua_State *L = luaL_newstate();
    luaL_openlibs(L); // 打开标准库
      // 编译并执行lua脚本
    if (luaL_loadfile(L, "./res/lua1.lua") || lua_pcall(L, 0, 0, 0)) {
        lua_error(L);
    }

    lua_getglobal(L, "width"); // 将width,height变量入栈
    lua_getglobal(L, "height");
    lua_getglobal(L, "color"); // 将color入栈,color是一个数组
    lua_getfield(L, 3, "r"); // 将color中的字段r g b a入栈
    lua_getfield(L, 3, "g");
    lua_getfield(L, 3, "b");
    lua_getfield(L, 3, "a");

    if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_istable(L, 3)) {
        lua_error(L);
    }

    int width = lua_tonumber(L, 1); // 获取栈中的数据
    int height = lua_tonumber(L, 2);
    int r = lua_tonumber(L, 4);
    int g = lua_tonumber(L, 5);
    int b = lua_tonumber(L, 6);
    int a = lua_tonumber(L, 7);

    lua_getglobal(L, "point"); // 将point入栈,point是一个数组
    lua_pushnumber(L, 100); // 为point数组赋值一个x字段,值尾100
    lua_setfield(L, 8, "x");
    lua_pushnumber(L, 120); // 赋值一个y字段,值尾120
    lua_setfield(L, 8, "y");

    lua_getfield(L, 8, "x"); // 将pint的x,y字段入栈
    lua_getfield(L, 8, "y");

    int px = lua_tonumber(L, -2); // 获取栈中的数据
    int py = lua_tonumber(L, -1);

// 创建一个新的table,全局变量名叫size,并设置size的x,y字段  lua_newtable(L);

lua_pushnumber(L, 10);

lua_setfield(L, -2, "w");

lua_pushnumber(L, 20);

lua_setfield(L, -2, "h");

lua_setglobal(L, "size");

lua_getglobal(L, "size");

lua_getfield(L, -1, "w");

lua_getfield(L, -2, "h");

    dump(L);

2. 在c中操作lua的函数:

lua文件:

function lua_func (x, y)
    print("lua_func", x, y)
    return {x = x, y = y}
end

function lua_error (...)
    print("lua_error")
end

c文件:

 lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    if (luaL_loadfile(L, "./res/lua1.lua") || lua_pcall(L, 0, 0, 0)) {
        lua_error(L);
    }

    lua_getglobal(L, "lua_error");

    lua_getglobal(L, "lua_func");
    lua_pushnumber(L, 12);
    lua_pushnumber(L, 24);
    if (lua_pcall(L, 2, 1, 1)) {
        lua_error(L);
    }

    lua_getfield(L, -1, "x");
    lua_getfield(L, -2, "y");

在lua中调用c函数

先定一个c函数,需要符合lua的对c函数要求,其函数原型是:typedef int (*lua_Function) (lua_State* L):

static int setPoint(lua_State* L) {

lua_Number x = luaL_checknumber(L, 1); // 检测lua调用时,传入的参数,此时函数调用的所有参数已经在栈中

lua_Number y = luaL_checknumber(L, 2);

lua_newtable(L); // 创建一个table并入栈,作为返回值使用

lua_pushnumber(L, x); // 为新创建的table赋值

lua_setfield(L, -2, "x");

lua_pushnumber(L, y);

lua_setfield(L, -2, "y");

return 1; // 表明返回值只有一个,解释器会返回距离栈顶的数据,函数调用完成后,会在自动清理栈相关的数据

}

在调用前,在lua的运行环境中注册一个c函数,相当于告诉lua函数的调用地址:

lua_pushcfunction(L, setPoint);

lua_setglobal(L, "set_point");

然后就可以在lua中进行调用:

local point = set_point(123, 456)

print(point.x, point.y)

注册c函数的模块,假如在一个模块中有多个函数,其实就是在一个全局的table中设置相关的函数而已

先定义一个模块中的相关函数:

static int setPoint(lua_State* L) {

  ...

  return 1;

}

static int printPoint(lua_State* L) {

  ...

  return 0;

}

static void register_point(lua_State* L) {

lua_newtable(L);

lua_pushcfunction(L, setPoint);

lua_setfield(L, -2, "set_point");

lua_pushcfunction(L, printPoint);

lua_setfield(L, -2, "print_point");

lua_setglobal(L, "Point"); // 为Point模块注册了两个函数

}

在lua中使用刚注册的c模块:

local point = Point.set_point(123, 456)
Point.print_point(point);

在c中操作lua中的数组:

1. void lua_rawgeti(lua_State* L, int index, int key);

index表示table在栈中的位置,key表示数组的索引,相当于:

lua_pushnumber(L, key);

lua_rawget(L, index);

2. void lua_rawseti(lua_State* L, int index, int key);

index表示table在栈中的位置,key表示数组的索引,相当于:

lua_pushnumber(L, key);

lua_insert(L, -2);

lua_rawset(L, index);

实例:

c代码:

static int map_table (lua_State* L) {
    luaL_checktype(L, 1, LUA_TTABLE); // 第一个参数是数组
    luaL_checktype(L, 2, LUA_TFUNCTION); // 第二个参数是在遍历数组元素时候,对元素的处理函数,返回新的元素

    int n = luaL_len(L, 1); // 获取数组的长度
    for (int i = 1; i <= n; ++i) {
        lua_pushvalue(L, 2); // 拷贝一份函数对象,并压入栈顶,因为函数在执行完成后会自动出栈
        lua_rawgeti(L, 1, i); // 获取数组中索引为i的元素,并压入栈
        lua_call(L, 1, 1); // 调用刚拷贝的函数,函数调用相关谁出栈,返回值会入栈
        lua_rawseti(L, 1, i); // 将栈顶的返回值设置为数组元素的新值,返回值出栈
    }

    return 0;
}

lua代码:

local t = {11, 22, 33}
function lua_map (v)
    print("lua_map", v);
    return v + 1;
end
map_table(t, lua_map);

for k, v in ipairs(t) do
    print(k, v)
end

c操作lua中的字符串:

c文件:

static int lua_split(lua_State* L) {
    const char* str = luaL_checkstring(L, 1);
    const char* split = luaL_checkstring(L, 2);

    lua_newtable(L); // 创建一个新表,用于返回值
    const char* e;
    int i = 0;
    while ((e = strchr(str, *split)) != nullptr) { // 找出分隔符的位置
        lua_pushlstring(L, str, e - str); // 将子串入栈
        lua_rawseti(L, -2, ++i); // 添加子串到数组中
        str = e + 1; // 更新字符串指针,准备匹配下一个分隔符的位置
    }
    lua_pushstring(L, str); // 添加最后一个子串
    lua_rawseti(L, -2, i+1);

    return 1;
}

lua文件:

local str = "123, 4434, win, dfnoe,, dfdfd,"

local t = split(str, ‘,‘);
for k, v in pairs(t) do
    print(k, v)
end
时间: 2024-07-29 21:16:15

lua 和 c的相关文章

cocos2dx lua中异步加载网络图片,可用于显示微信头像

最近在做一个棋牌项目,脚本语言用的lua,登录需要使用微信登录,用户头像用微信账户的头像,微信接口返回的头像是一个url,那么遇到的一个问题就是如何在lua中异步加载这个头像,先在引擎源码里找了下可能会提供这个功能的地方,发现好像没有提供类似功能,那么只能自己动手写.所以我在ImageView这个类里面添加了一个成员方法,其实可以不写在ImageView里,而且我觉得非必需情况下还是不要修改引擎源码的好,因为如果源码改动比较多的话,将来引擎版本升级会比较麻烦.我写在ImageView里纯粹是想偷

Lua 第一天

今天开始学习Lua语言,感觉Lua非常便捷.我用的编译器是SciTE,很不错. 举例一:无需引用,内置输出语句   print() print(6)   --> 6 print(type(6))  -->   number 举例二:对数字字符串进行数字化处理 print("2"+3 )  -->5 举例三:..连接俩个字符串,#返回字符串长度 举例四:print()输出函数,可以有多个输出值 a = 1,b =2,c = a+b    print(a,b,c,a,b,

lua协程一则报错解决“attempt to yield across metamethod/C-call boundary”

问题 attempt to yield across metamethod/C-call boundary 需求跟如下帖子中描述一致: http://bbs.chinaunix.net/forum.php?mod=viewthread&action=printable&tid=4065715 模拟一个场景,在C中创建出coroutine来执行Lua脚本,并且提供C API给Lua使用,当某些操作可能会阻塞时(如网络I/O),C函数中执行yield将协程切换出去,然后未来的某个时刻,如果条件

lua闭合函数

function count( ... ) local i = 0 return function( ... ) i = i+ 1 return i end end local func = count(...) print(func()) print(func()) print(func()) 结果如下: 1 2 3 [Finished in 0.1s] lua 闭合函数:一个函数加上该函数所需访问的所有“非局部变量”. 如上所示:count()函数返回了另一个函数,而这个函数使用了count

Mac下Lua Sublime Text2 开发环境搭建

1.安装Lua编译器 下载Lua, http://www.lua.org/: 解压后,cd进入该文件夹src目录下 在当前文件夹执行make macosx   然后回车 cd 到上一目录,执行sudo make install 完成之后执行lua -v 可以看到:Lua 5.3.3  Copyright (C) 1994-2016 Lua.org, PUC-Rio 2.安装Sublime Text2 1.下载http://sublime-text-2.cn.uptodown.com后打开: {

Nginx+Lua 积累

Nginx+Lua 积累 1.解析16进制编码的中文参数 复制代码 local encodeStr = "%E6%B0%94" local decodeStr = ""; for i = 2, #encodeStr - 1, 3 do local num = encodeStr:sub(i, i + 1); num = tonumber(num, 16); decodeStr = decodeStr .. string.char(num); end ngx.say(

nginx与Lua执行顺序

Nginx顺序 Nginx 处理每一个用户请求时,都是按照若干个不同阶段(phase)依次处理的,而不是根据配置文件上的顺序. Nginx 处理请求的过程一共划分为 11 个阶段,按照执行顺序依次是 post-read.server-rewrite.find-config.rewrite.post-rewrite. preaccess.access.post-access.try-files.content.log. post-read: 读取请求内容阶段 Nginx读取并解析完请求头之后就立即

在 Lua 里 使用 Cocos Studio 导出的 .csb 文件

1. 加载 节点到场景 第一种方法 local scene = cc.CSLoader:createNode("scene.csb") self:addChild(scene) 第二种方法 local scene = cc.uiloader:load("MainScene.csb"):addTo(self) 2.强转精灵类型 local sprite = tolua.cast(object,"cc.Sprite") CocoStudio 做的里面

深入lua栈交互—cpp调用lua数据

lua是通过lua_state这个栈来和c 交互的 1.....lua栈 index 下往上增长 如: 1 2 3 4 5 6 2.....lua栈 index 是循环的 如下 index 上到下 是 3 2 1 0 -1 -2 -3 ,栈对应的值为     1     2     3     x     1     2     3      3......lua函数多个返回值如果上面是function返回了3个返回值,那么return a ,b,c  中 a=3 b=2 c=1 第一个返回值先

[nginx] 由Lua 粘合的Nginx生态环境-- agentzh tech-club.org

[nginx] 由Lua 粘合的Nginx生态环境-- agentzh tech-club.org 演讲听录 [复制链接] kindle LT管理团队 Rank: 9Rank: 9Rank: 9 未绑定新浪微博 签到222 注册时间1970-1-1最后登录2015-6-5在线时间168 小时阅读权限200积分19025帖子119主题35精华2UID9223 LT总司令 LT元老 LT教授 串个门加好友打招呼发消息 电梯直达跳转到指定楼层 1# 发表于 2013-1-12 12:43:47 |只看