Why statement return error in current Lua(Codea)?

本文探讨了在Lua 5.3中使用table.insert时出现的positionoutofbounds错误原因。通过对比Lua 5.1与Lua 5.3的行为差异,分析了源代码实现的不同之处,并解释了为何某些插入操作会导致错误。

Why statement "table.insert(touches, touch.id, touch)" return error in current Lua(Codea)?

Introduction

When I tried to run some Codea code on the new version, I found that some code about touch always return error. First the error is float without integer representation, after modified it with math.floor, the error changed to position out of bounds. It made me confuse(I had a post in the forum [SOLVED] Should touch.id be a int or a float?). I tried to look for the reason, below is the whole record.

Test code

The code can run on the old version Codea which using the lua-5.1, and the current Codea is using lua-5.3, so I guess the bug is because of the different version of lua. For the easy testing, I do the debug on my Raspberry Pi which have two versions of lua. the test code is below:

Test Code 1

touches = {}
touch={id=100}
table.insert(touches, math.floor(touch.id), touch)

return error in Lua-5.3.2:

pi@rpi /opt/software/lua-5.3.2 $ lua
Lua 5.3.2  Copyright (C) 1994-2015 Lua.org, PUC-Rio
>
> touches = {}
> touch={id=100}
> table.insert(touches, math.floor(touch.id), touch)
stdin:1: bad argument #2 to 'insert' (position out of bounds)
stack traceback:
        [C]: in function 'table.insert'
        stdin:1: in main chunk
        [C]: in ?
> 

return success in Lua-5.1.5:

pi@rpi /opt/software $ lua5.1
Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio
>
> touches = {}
> touch={id=100}
> table.insert(touches, math.floor(touch.id), touch)
> 

Test Code 2

return error in Lua-5.3.2:

kano@kano ~ $ lua
Lua 5.3.2  Copyright (C) 1994-2015 Lua.org, PUC-Rio
> my={}
> table.insert(my,123,12)
stdin:1: bad argument #2 to 'insert' (position out of bounds)
stack traceback:
	[C]: in function 'table.insert'
	stdin:1: in main chunk
	[C]: in ?
> table.insert(my,1,12)
> table.insert(my,2,12)
> table.insert(my,4,12)
stdin:1: bad argument #2 to 'insert' (position out of bounds)
stack traceback:
	[C]: in function 'table.insert'
	stdin:1: in main chunk
	[C]: in ?
> my[123]=123
> #my
2
> unpack(my)
stdin:1: attempt to call a nil value (global 'unpack')
stack traceback:
	stdin:1: in main chunk
	[C]: in ?
> table.unpack(my)
12	12
> for k,v in pairs(my) do print(k,v) end
1	12
2	12
123	123
> 

return success in Lua-5.1.5:

pi@rpi /opt/software/lua-5.3.2/src $ lua
Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio
> my={}
> table.insert(my,123,12)
> #my
stdin:1: unexpected symbol near '#'
> table.length(my)
stdin:1: attempt to call field 'length' (a nil value)
stack traceback:
        stdin:1: in main chunk
        [C]: ?
> table.len(my)
stdin:1: attempt to call field 'len' (a nil value)
stack traceback:
        stdin:1: in main chunk
        [C]: ?
> table.insert(my,1,12)
> table.insert(my,2,12)
> table.insert(my,4,12)
> my[123]=123
> print(#my)
4
> table.unpack(my)
stdin:1: attempt to call field 'unpack' (a nil value)
stack traceback:
        stdin:1: in main chunk
        [C]: ?
> for k,v in pairs(my) do print(k,v) end
1       12
2       12
4       12
123     123
> 

Conclusion

It is easy to see:

  • Use table.insert on a empty table must start with position 1
  • 123 only be treat with the hash key in my, not array index.
  • The table length will not include the elements which stored with hash.

Analyze the lua source code

Use the command git grep -n "error info", we can find the relational function source code:

pi@rpi /opt/software/lua-5.3.2 $ git grep -n "position out of bounds"
src/ltablib.c:90:      luaL_argcheck(L, 1 <= pos && pos <= e, 2, "position out of bounds");
src/ltablib.c:110:    luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, "position out of bounds");
pi@rpi /opt/software/lua-5.3.2 $ 

The result is clear, we know that the code is in file src/ltablib.c, line 90 and 110, the code is below:

 79 static int tinsert (lua_State *L) {
 80   lua_Integer e = aux_getn(L, 1, TAB_RW) + 1;  /* first empty element */
 81   lua_Integer pos;  /* where to insert new element */
 82   switch (lua_gettop(L)) {
 83     case 2: {  /* called with only 2 arguments */
 84       pos = e;  /* insert new element at the end */
 85       break;
 86     }
 87     case 3: {
 88       lua_Integer i;
 89       pos = luaL_checkinteger(L, 2);  /* 2nd argument is the position */
 90       luaL_argcheck(L, 1 <= pos && pos <= e, 2, "position out of bounds");
 91       for (i = e; i > pos; i--) {  /* move up elements */
 92         lua_geti(L, 1, i - 1);
 93         lua_seti(L, 1, i);  /* t[i] = t[i - 1] */
 94       }
 95       break;
 96     }
 97     default: {
 98       return luaL_error(L, "wrong number of arguments to 'insert'");
 99     }
100   }
101   lua_seti(L, 1, pos);  /* t[pos] = v */
102   return 0;
103 }
104 
105 
106 static int tremove (lua_State *L) {
107   lua_Integer size = aux_getn(L, 1, TAB_RW);
108   lua_Integer pos = luaL_optinteger(L, 2, size);
109   if (pos != size)  /* validate 'pos' if given */
110     luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, "position out of bounds");
111   lua_geti(L, 1, pos);  /* result = t[pos] */
112   for ( ; pos < size; pos++) {
113     lua_geti(L, 1, pos + 1);
114     lua_seti(L, 1, pos);  /* t[pos] = t[pos + 1] */
115   }
116   lua_pushnil(L);
117   lua_seti(L, 1, pos);  /* t[pos] = nil */
118   return 1;
119 }

We can see the check 1 <= pos && pos <= e in function tinsert and the check 1 <= pos && pos <= size + 1 in function tremove. And we can find the define of e and size:

lua_Integer e = aux_getn(L, 1, TAB_RW) + 1; /* first empty element */

The relational code:

 27 #define TAB_R   1           /* read */
 28 #define TAB_W   2           /* write */
 29 #define TAB_L   4           /* length */
 30 #define TAB_RW  (TAB_R | TAB_W)     /* read/write */
 31 
 32 
 33 #define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n))
 34 
 35 
 36 static int checkfield (lua_State *L, const char *key, int n) {
 37   lua_pushstring(L, key);
 38   return (lua_rawget(L, -n) != LUA_TNIL);
 39 }
 40 
 41 
 42 /*
 43 ** Check that 'arg' either is a table or can behave like one (that is,
 44 ** has a metatable with the required metamethods)
 45 */
 46 static void checktab (lua_State *L, int arg, int what) {
 47   if (lua_type(L, arg) != LUA_TTABLE) {  /* is it not a table? */
 48     int n = 1;  /* number of elements to pop */
 49     if (lua_getmetatable(L, arg) &&  /* must have metatable */
 50         (!(what & TAB_R) || checkfield(L, "__index", ++n)) &&
 51         (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) &&
 52         (!(what & TAB_L) || checkfield(L, "__len", ++n))) {
 53       lua_pop(L, n);  /* pop metatable and tested metamethods */
 54     }
 55     else
 56       luaL_argerror(L, arg, "table expected");  /* force an error */
 57   }
 58 }

Now we know the meaning: check the insert position, if it is bigger than the current size of the table, then return error with info "position out of bounds".

In version 5.1, we can insert in any position:

pi@rpi /opt/software $ lua5.1
Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio
> touches = {}
> touch={id=100}
> table.insert(touches, 1000000, touch)
> 

Let us see the source code in version 5.1:

 90 static int tinsert (lua_State *L) {
 91   int e = aux_getn(L, 1) + 1;  /* first empty element */
 92   int pos;  /* where to insert new element */
 93   switch (lua_gettop(L)) {
 94     case 2: {  /* called with only 2 arguments */
 95       pos = e;  /* insert new element at the end */
 96       break;
 97     }
 98     case 3: {
 99       int i;
100       pos = luaL_checkint(L, 2);  /* 2nd argument is the position */
101       if (pos > e) e = pos;  /* `grow' array if necessary */
102       for (i = e; i > pos; i--) {  /* move up elements */
103         lua_rawgeti(L, 1, i-1);
104         lua_rawseti(L, 1, i);  /* t[i] = t[i-1] */
105       }
106       break;
107     }
108     default: {
109       return luaL_error(L, "wrong number of arguments to " LUA_QL("insert"));
110     }
111   }
112   luaL_setn(L, 1, e);  /* new size */
113   lua_rawseti(L, 1, pos);  /* t[pos] = v */
114   return 0;
115 }
116 
117 
118 static int tremove (lua_State *L) {
119   int e = aux_getn(L, 1);
120   int pos = luaL_optint(L, 2, e);
121   if (!(1 <= pos && pos <= e))  /* position is outside bounds? */
122    return 0;  /* nothing to remove */
123   luaL_setn(L, 1, e - 1);  /* t.n = n-1 */
124   lua_rawgeti(L, 1, pos);  /* result = t[pos] */
125   for ( ;pos<e; pos++) {
126     lua_rawgeti(L, 1, pos+1);
127     lua_rawseti(L, 1, pos);  /* t[pos] = t[pos+1] */
128   }
129   lua_pushnil(L);
130   lua_rawseti(L, 1, e);  /* t[e] = nil */
131   return 1;
132 }

We can see that, it is a different dealing with the insert position argument.

if (pos > e) e = pos;  /* `grow' array if necessary */

If the pos is bigger than the current max position(the e), then expand the table with the pos, It is a "grow" array, but it need alloc more memery. Maybe for the optimizing reason, in 5.3 this is not allowed, then the old code get error.

More details of source code

如果想了解更清楚, 可以在源代码里搜索一下函数(或者宏) luaL_argcheck:

pi@rpi /opt/software/lua-5.3.2 $ git grep -n "luaL_argcheck"
...
src/lauxlib.h:114:#define luaL_argcheck(L, cond,arg,extramsg)   \
...

看样子是个宏, 打开 src/lauxlib.h, 查到如下宏定义:

114 #define luaL_argcheck(L, cond,arg,extramsg) \
115         ((void)((cond) || luaL_argerror(L, (arg), (extramsg))))

发现又调用了一个 luaL_argerror, 先在本文件里查一下, 发现有函数声明:

38 LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);

那么函数定义应该在 src/lauxlib.c 中, 再用 git grep -n 搜一把, 如下:

pi@rpi /opt/software/lua-5.3.2 $ git grep -n "luaL_argerror"
...
src/lauxlib.c:164:LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
...

很好, 打开看看具体代码:

 164 LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
 165   lua_Debug ar;
 166   if (!lua_getstack(L, 0, &ar))  /* no stack frame? */
 167     return luaL_error(L, "bad argument #%d (%s)", arg, extramsg);
 168   lua_getinfo(L, "n", &ar);
 169   if (strcmp(ar.namewhat, "method") == 0) {
 170     arg--;  /* do not count 'self' */
 171     if (arg == 0)  /* error is in the self argument itself? */
 172       return luaL_error(L, "calling '%s' on bad self (%s)",
 173                            ar.name, extramsg);
 174   }
 175   if (ar.name == NULL)
 176     ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?";
 177   return luaL_error(L, "bad argument #%d to '%s' (%s)",
 178                         arg, ar.name, extramsg);
 179 }

看得出来, 我们的试验代码触发了最后一条判断语句:

 ...
 175   if (ar.name == NULL)
 176     ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?";
 177   return luaL_error(L, "bad argument #%d to '%s' (%s)",
 178                         arg, ar.name, extramsg);
 ...

--The End

转载于:https://my.oschina.net/freeblues/blog/683114

在C++中集成和使用Lua作为脚本语言通常涉及以下几个步骤: 1. **下载和安装Lua**: 首先,你需要从lua.org获取Lua库及其开发工具包(luadll、lua.hpp等)。选择适合你系统的版本,并按照说明安装。 2. **包含头文件**: 在C++项目中,需要包含lua相关的头文件,如`#include <lua.hpp>`,引入lua的基本函数和数据结构。 3. **创建Lua环境**: 创建一个Lua解释器环境,通常是通过`luaL_newstate()`函数,返回一个新的lua_State实例。 4. **加载Lua脚本**: 使用`luaL_dofile()`或`luaL_loadstring()`函数加载 Lua 脚本文件或字符串内容。 5. **绑定C++对象到Lua**: 使用`lua_register()`或`lua_pushlightuserdata()`将C++对象暴露给Lua,以便可以在脚本中调用其方法。 6. **执行Lua脚本**: 使用`lua_pcall()`或`lua_call()`来执行Lua函数并处理错误。 7. **访问和修改Lua变量**: 通过索引来操作Lua表或栈中的值,比如`lua_gettable()`、`lua_setglobal()`等。 8. **清理**: 当完成脚本交互后,记得释放资源,例如使用`lua_close()`关闭Lua状态。 ```cpp #include "lua.hpp" int main() { lua_State* L = luaL_newstate(); // 创建Lua环境 if (L == nullptr) { std::cerr << "Failed to create Lua state" << std::endl; return 1; } if (!luaL_openlibs(L)) { // 加载标准库 std::cerr << "Error opening Lua libraries" << std::endl; lua_close(L); return 1; } if (luaL_loadfile(L, "script.lua") != 0) { std::cerr << "Error loading script: " << lua_tostring(L, -1) << std::endl; lua_close(L); return 1; } lua_pcall(L, 0, 0, 0); // 执行脚本 // 在这里可以访问和操作Lua变量 lua_close(L); // 清理资源 return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值