小冰在网上搜了一下Lua c api,貌似对于用c实现lua闭包的的文章几乎没有<没有科学依据的说>。
萌新小冰就来教一下大家如何用C写一个lua的闭包函数吧~
等会会用用到的LUA API:
- lua_gettop
- luaL_checkinteger
- lua_tointeger
- lua_setupvalue
- lua_upvalueindex
- lua_pushinteger
- lua_pushcclosure
- luaL_newlib
嘻嘻,相信大家的自学能力还是蛮ok哒
栈的元素特点就是先进后出嘛,栈顶是-1,栈低是1
大家也看过科幻片吧,这个平常用到栈就比作时现实空间<科幻>,等会会用到异元次空间<科幻>:伪索引。就像现实和虚拟,不能不承认,他们都是存在。
光说不行呀<实践是理论的唯一标准>
先来一段lua闭包
local range
range = function (n)
local start = 0
local target = n
local step = 1
local now = start
local function range_element()
if now >= target then
return nil
end
now = now + step
return now --闭包的返回值
end
return range_element --把闭包函数返回给 for
end
print("value:")
for i in range(10) do
print(i)
end
结果:
[Shell] 纯文本查看 复制代码 value:
1
2
3
4
5
6
7
8
9
10
上面的range函数中的局部变量叫做range_element的上值,emmm,这是chinglish吧,英文名叫做:upvalue
上值的作用就是给闭包函数用的,作为闭包函数每次的返回值,作为迭代器结束循环的条件,作为,,,,好像没啦~
前面扯淡了那么多,主题来了!!!
问题来了,在C中咋整????
其实原理跟lua代码差不多,上代码吧
//file: myiter.c
#define LUA_LIB
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
static int range_element(lua_State *L)
{
lua_Integer now, start, target, step;
//把值从异次元空间取出来
start = lua_tointeger(L, lua_upvalueindex(1));
target = lua_tointeger(L, lua_upvalueindex(2));
step = lua_tointeger(L, lua_upvalueindex(3));
now = lua_tointeger(L, lua_upvalueindex(4));
if (now >= target) return 0; //返回nil结束lua中的for
lua_pushinteger(L, now); //要返回的值
now += step;
lua_pushinteger(L, now); //记录本次的值
lua_replace(L, lua_upvalueindex(4)); //替换旧值
return 1;
}
static int l_range(lua_State *L)
{
lua_Integer now, start = 0, target, step = 1;
now = start;
luaL_checkinteger(L, 1);
lua_pushcclosure(L, range_element, 4);
//把上值传入异元次空间
//最小值
lua_pushinteger(L, start);
lua_setupvalue(L, -2, 1); //闭包函数位于栈的-2位置,而这个1上值的伪索引
//再来几个上值
lua_pushinteger(L, target);
lua_setupvalue(L, -2, 2);
//------------------
lua_pushinteger(L, step);
lua_setupvalue(L, -2, 3);
//这个记录当前量
lua_pushinteger(L, now);
lua_setupvalue(L, -2, 4);
return 1;
}
static const struct luaL_Reg myiter[] =\
{
{"range", l_range},
{NULL, NULL}
};
LUALIB_API int luaopen_myiter(lua_State *L)
{
luaL_newlib(L, myiter);
return 1;
}
--file: myiter.lua
myiter = require "myiter"
local range = myiter.range
print("value:")
for i in range(10) do
print(i)
end
ok!编译并执行!
[Shell] 纯文本查看 复制代码 gcc myiter.c -o myiter.so -shared -fPIC -llua
[Shell] 纯文本查看 复制代码 lua myiter,lua
value:
0
1
2
3
4
5
6
7
8
9
哈哈哈看来已经接近range函数啦。
再来完善一下吧~~
直接上代码:
/*
* author: 小冰哟
*/
/*
* range([start,] target[, step])<不支持浮点数>类似于python中的range(...)
*/
#define LUA_LIB
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
static int l_closurefunc(lua_State *L)
{
lua_Integer now, start, target, step;
start = lua_tointeger(L, lua_upvalueindex(1));
target = lua_tointeger(L, lua_upvalueindex(2));
step = lua_tointeger(L, lua_upvalueindex(3));
now = lua_tointeger(L, lua_upvalueindex(4));
if (((now >= target) && (step > 0)) \
|| ((now <= target) && (step < 0)))
return 0;
lua_pushinteger(L, (now != start) ? now : start);
now += step;
lua_pushinteger(L, now);
lua_replace(L, lua_upvalueindex(4)); //当前变量
return 1;
}
static int l_range(lua_State *L)
{
lua_Integer n[3] = {0, 0, 1}; //[0]:start|[1]:target|[2]:step
unsigned char i;
switch (lua_gettop(L)) {
case 1:
n[1] = luaL_checkinteger(L, 1);
if (n[1] == 0) return 0;
n[2] = (n[1] > 0) ? n[2] : -n[2];
break;
case 2:
n[0] = luaL_checkinteger(L, 1);
n[1] = luaL_checkinteger(L, 2);
break;
case 3:
n[0] = luaL_checkinteger(L, 1);
n[1] = luaL_checkinteger(L, 2);
n[2] = luaL_checkinteger(L, 3);
break;
default:
return 0;
}
lua_pushcclosure(L, l_closurefunc, 4);
for (i = 0; i < 3; i++) {
lua_pushinteger(L, n[i]);
lua_setupvalue(L, -2, (i + 1));
}
//当前变量
lua_pushinteger(L, n[0]);
lua_setupvalue(L, -2, 4);
return 1;
}
static const struct luaL_Reg l_miter[] =\
{
{"range", l_range},
{NULL, NULL}
};
LUALIB_API int luaopen_miter(lua_State *L)
{
luaL_newlib(L, l_miter);
lua_pushstring(L, "author");
lua_pushstring(L, "小冰哟|lovingxiaobing");
lua_rawset(L, 3);
return 1;
}
好啦,小冰就跟大家唠嗑到这啦~
给大家一个查询lua c api 的网,中文的哟。
https://www.runoob.com/manual/lua53doc/contents.html
喜欢小冰的文章,支持一下哟,mua~
小甲鱼:programming change the world!
|