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 = , 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 =
height =
color = {r = , g = , b = , a = }
point = {}

c文件:

  // 创建一个lua状态,用于保存脚本的所有数据
   lua_State *L = luaL_newstate();
luaL_openlibs(L); // 打开标准库

  // 编译并执行lua脚本
if (luaL_loadfile(L, "./res/lua1.lua") || lua_pcall(L, , , )) {
lua_error(L);
} lua_getglobal(L, "width"); // 将width,height变量入栈
lua_getglobal(L, "height");
lua_getglobal(L, "color"); // 将color入栈,color是一个数组
lua_getfield(L, , "r"); // 将color中的字段r g b a入栈
lua_getfield(L, , "g");
lua_getfield(L, , "b");
lua_getfield(L, , "a"); if (!lua_isnumber(L, ) || !lua_isnumber(L, ) || !lua_istable(L, )) {
lua_error(L);
} int width = lua_tonumber(L, ); // 获取栈中的数据
int height = lua_tonumber(L, );
int r = lua_tonumber(L, );
int g = lua_tonumber(L, );
int b = lua_tonumber(L, );
int a = lua_tonumber(L, ); lua_getglobal(L, "point"); // 将point入栈,point是一个数组
lua_pushnumber(L, ); // 为point数组赋值一个x字段,值尾100
lua_setfield(L, , "x");
lua_pushnumber(L, ); // 赋值一个y字段,值尾120
lua_setfield(L, , "y"); lua_getfield(L, , "x"); // 将pint的x,y字段入栈
lua_getfield(L, , "y"); int px = lua_tonumber(L, -); // 获取栈中的数据
int py = lua_tonumber(L, -); // 创建一个新的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, , , )) {
lua_error(L);
} lua_getglobal(L, "lua_error"); lua_getglobal(L, "lua_func");
lua_pushnumber(L, );
lua_pushnumber(L, );
if (lua_pcall(L, , , )) {
lua_error(L);
} lua_getfield(L, -, "x");
lua_getfield(L, -, "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, , LUA_TTABLE); // 第一个参数是数组
luaL_checktype(L, , LUA_TFUNCTION); // 第二个参数是在遍历数组元素时候,对元素的处理函数,返回新的元素 int n = luaL_len(L, ); // 获取数组的长度
for (int i = ; i <= n; ++i) {
lua_pushvalue(L, ); // 拷贝一份函数对象,并压入栈顶,因为函数在执行完成后会自动出栈
lua_rawgeti(L, , i); // 获取数组中索引为i的元素,并压入栈
lua_call(L, , ); // 调用刚拷贝的函数,函数调用相关谁出栈,返回值会入栈
lua_rawseti(L, , i); // 将栈顶的返回值设置为数组元素的新值,返回值出栈
} return ;
}

lua代码:

local t = {, , }
function lua_map (v)
print("lua_map", v);
return v + ;
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, );
const char* split = luaL_checkstring(L, ); lua_newtable(L); // 创建一个新表,用于返回值
const char* e;
int i = ;
while ((e = strchr(str, *split)) != nullptr) { // 找出分隔符的位置
lua_pushlstring(L, str, e - str); // 将子串入栈
lua_rawseti(L, -, ++i); // 添加子串到数组中
str = e + ; // 更新字符串指针,准备匹配下一个分隔符的位置
}
lua_pushstring(L, str); // 添加最后一个子串
lua_rawseti(L, -, i+); return ;
}

lua文件:

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

local t = split(str, ',');
for k, v in pairs(t) do
print(k, v)
end

如果需要动态为字符串分配内存空间,是一件即麻烦又有风险的事,如果直接使用lua为我们提供的buffer,就无需担心内存分配和溢出问题:

luaL_Buffer B; 创建一个buffer

void luaL_buffinit (lua_State* L, luaL_Buffer* B); 初始化一个buffer

void luaL_addchar (luaL_Buffer* B, char c); 添加一个字符到buffer中

void luaL_addlstring (luaL_Buffer* B, const char* str, size_t len); 添加定长的字符串到buffer中

void luaL_addstring (luaL_Buffer* B, const char* str); 添加一个以“\0”结尾的字符串到buffer中

void luaL_pushresult (luaL_Buffer* B); 更新buffer,并将其留在栈上

void luaL_addvalue (luaL_Buffer* B); 直接将栈顶的值加入缓冲区,必须是字符串和数字

实例:

lua文件:

local str = "i am a student, how about you!"

local str2 = upper(str)
print("str2:", str2)

c文件:

static int lua_upper(lua_State* L) {
size_t len;
const char* str = luaL_checklstring(L, -, &len); luaL_Buffer buffer;
luaL_buffinit(L, &buffer); for (int i = ; i < len; ++i) {
luaL_addchar(&buffer, toupper((unsigned char)(str[i])));
}
luaL_pushresult(&buffer); return ;
}

lua 和 c的更多相关文章

  1. lua执行字节码的过程介绍

    前面一篇文章中介绍了lua给下面代码生成最终的字节码的整个过程,这次我们来看看lua vm执行这些字节码的过程. foo = "bar" local a, b = "a& ...

  2. lua 学习笔记(1)

    一.lua函数赋值与函数调用         在lua中函数名也是作为一种变量出现的,即函数和所有其他值一样都是匿名的,当要使用某个函数时,需要将该函数赋值给一个变量,这样在函数块的其他地方就可以通过 ...

  3. 在redis中使用lua脚本让你的灵活性提高5个逼格

    在redis的官网上洋洋洒洒的大概提供了200多个命令,貌似看起来很多,但是这些都是别人预先给你定义好的,但你却不能按照自己的意图进行定制, 所以是不是感觉自己还是有一种被束缚的感觉,有这个感觉就对了 ...

  4. 使用Nginx+Lua代理Hadoop HA

    一.Hadoop HA的Web页面访问 Hadoop开启HA后,会同时存在两个Master组件提供服务,其中正在使用的组件称为Active,另一个作为备份称为Standby,例如HDFS的NameNo ...

  5. 打印Lua的Table对象

    小伙伴们再也不用为打印lua的Table对象而苦恼了, 本人曾也苦恼过,哈哈 不过今天刚完成了这个东西, 以前在网上搜过打印table的脚本,但是都感觉很不理想,于是,自己造轮子了~ 打印的效果,自己 ...

  6. lua解析赋值类型代码的过程

    我们来看看lua vm在解析下面源码并生成bytecode时的整个过程: foo = "bar" local a, b = "a", "b" ...

  7. 怎么调试lua性能

    怎么调试lua性能 我们的游戏使用的是Cocos2dx-lua 3.9的项目,最近发现我们的游戏.运行比较缓慢.想做一次性能优化了.其实主要分为GPU.CPU的分别优化.GPU部分的优化.网上有很多优 ...

  8. Lua 安全调用 metatable 的简单应用

    事情的经过 我们的项目中存在好几个战斗界面,不过界面中的内容略有不同.跟同事出去吃饭的时候,他问我.我们现在的战斗界面.有很多是重复的,但是也有偶尔几个地方不太一样.我在战斗过程中驱动这些界面的时候. ...

  9. 让Lua自己把文件夹下面的所有文件自动加载起来吧

    没有想到我也做了一回标题党.其实这里边说的自动还是有夸大其词的部分.其实只是指定文件夹,然后根据指定文件夹数据,加载目录下边的内容而已. 怎么来进行Lua文件的加载 一般情况下,相关的功能需要给他创建 ...

  10. 让Lua支持Linq吧

    第一次接触Linq是在使用C#的时候,这种语法,在处理列表数据非常方便.如果想了解Linq的更多内容可以百度一下Linq,不过你不了解也没关系,让我在Lua中给你展示一下Linq的魅力.简单点说,Li ...

随机推荐

  1. Python的平凡之路(10)

    异步IO 数据库 队列 缓存 1.Gevent协程 定义:用户态的轻量级线程.协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下 ...

  2. iOS 服务器端推送证书p12文件制作

    A.苹果服务器地址: Production和development用的push的服务器不同pdev是:$apnsHost = 'gateway.sandbox.push.apple.com';pro是 ...

  3. this面试题

    // 考题1 /*function Fn() { console.log(this);//window } Fn(); new Fn();//Fn实例 Fn.apply(Fn); //将this指向F ...

  4. Ubuntu操作系统安装使用教程 (转)

    随着微软的步步紧逼,包括早先的Windows黑屏计划.实施,逮捕番茄花园作者并判刑,种种迹象表明,中国用户免费使用盗版Windows的日子将不会太长久了,那么这个世界上有没有即免费又易用的操作系统呢? ...

  5. ubuntu频繁掉线 转

    好长好长时间没来百度空间了,最近闲来无事,正好弥补之前的空缺了!跟Ubuntu打交道已有很长一段时间了,期间遇到了很多问题,我把遇到的一些问题及找到的解决方案记录下来,我想这可能会对那些跟我有同样境遇 ...

  6. Http协议(一)

    Http是一种无状态,面向连接的协议.是客户端与服务端进行超文本传输协议(HTTP)的一种通信协议.目前我们使用的是Http/1.1版本. Cookie是解决http无状态,相当于一个只有一天记忆的人 ...

  7. node.js之windows下环境终极配置

    大家都知道现在node.js相当流行,出门在外,如果都没听说过node.js,基本上算是out了,前段时间做一个项目,用到了实时通讯功能,当时用的就是node.js来做的,我有幸有研究了一番,别的不敢 ...

  8. 传智播客JavaWeb day02笔记

    2015年1月21日 今天的主要内容:介绍了几款常用Javaweb服务器,重点介绍了tomcat以及tomcat的安装和怎么样检测安装成功 1.JavaWeb常见服务器 Tomcat(免费但是只支持部 ...

  9. [转]Redis实现分析

    Redis实现分析 浏览次数:1018次 KITERUNNER_T 2014年10月19日 字号: 大 中 小 分享到: QQ空间 新浪微博 腾讯微博 人人网 豆瓣网 开心网 更多 1   1 环境准 ...

  10. IOS开发-当遇到tableView整体上移时的解决方案

    方案一在使用了navigationController后,当界面进行跳转往返后,时而会出现tableView上移的情况,通常会自动上移64个像素,那么这种情况,我们可以关闭tableView的自动适配 ...