利用lua的错误堆栈改写tolua++ tests,方便看到lua语法错误、print、assert以及出错信息
NND,好长的题目啊!这个东东是仿照CEGUI里面整的,很好用,解决了我一直郁闷的一个问题,所以做个笔记,估计也会对像我一样的lua菜鸟有用。
int
_tmain(
int
argc, _TCHAR
*
argv[])
{
Test::Tst_B * b = new Test::Tst_B; // instance used in Lua code
lua_State * L = lua_open();
luaL_openlibs(L);
tolua_tclass_open(L);
int top = lua_gettop(L);
int loaderr = luaL_loadfile(L, " tclass.lua " );
if (loaderr)
{
const char * errMsg = lua_tostring(L, - 1 );
lua_settop(L,top);
#ifdef _DEBUG
::printf_s( " [LuaScriptModule]: %s\n " , errMsg);
#endif
}
// call it
if (lua_pcall(L, 0 , 0 , 0 ))
{
const char * errMsg = lua_tostring(L, - 1 );
lua_settop(L,top);
#ifdef _DEBUG
::printf_s( " [LuaScriptModule]: %s\n " , errMsg);
#endif
}
lua_settop(L,top); // just in case :P
lua_close(L);
delete b;
return 0 ;
}
{
Test::Tst_B * b = new Test::Tst_B; // instance used in Lua code
lua_State * L = lua_open();
luaL_openlibs(L);
tolua_tclass_open(L);
int top = lua_gettop(L);
int loaderr = luaL_loadfile(L, " tclass.lua " );
if (loaderr)
{
const char * errMsg = lua_tostring(L, - 1 );
lua_settop(L,top);
#ifdef _DEBUG
::printf_s( " [LuaScriptModule]: %s\n " , errMsg);
#endif
}
// call it
if (lua_pcall(L, 0 , 0 , 0 ))
{
const char * errMsg = lua_tostring(L, - 1 );
lua_settop(L,top);
#ifdef _DEBUG
::printf_s( " [LuaScriptModule]: %s\n " , errMsg);
#endif
}
lua_settop(L,top); // just in case :P
lua_close(L);
delete b;
return 0 ;
}
对于lua的堆栈一直感到很困惑,罗列问题:
Q:到底是堆还是栈呢?貌似只有栈吧?
Q: lua交互为何用堆栈?
为何起用lua堆栈
当在Lua和C之间交换数据时我们面临着两个问题:
动态与静态类型系统的不匹配和自动与手动内存管理的不一致。
在Lua中,我们写下a[k]=v时,k和v可以有几种不同的类型(由于metatables的存在,a也可能有不同的类型)。
如果我们想在C中提供类似的操作,无论怎样,操作表的函数(settable)必定有一个固定的类型。
我们将需要几十个不同的函数来完成这一个的操作(三个参数的类型的每一种组合都需要一个函数)。
在C中声明一些union类型来解决这个问题,称之为lua_Value,它能够描述所有类型的Lua值。
,就可以这样声明settable
void lua_settable (lua_Value a, lua_Value k, lua_Value v);
这个解决方案有两个缺点。
1。要将如此复杂的类型映射到其它语言可能很困难;Lua不仅被设计为与C/C++易于交互,
Java,Fortran以及类似的语言也一样。
2。Lua负责垃圾回收:如果将Lua值保存在C变量中,Lua引擎没有办法了解这种用法;
它可能错误地认为某个值为垃圾并收集他。
Lua API没有定义任何类似lua_Value的类型。替代的方案,
它用一个抽象的栈在Lua与C之间交换值。
栈中的每一条记录都可以保存任何Lua值。
无论你何时想要从Lua请求一个值(比如一个全局变量的值),
1.将这个值压入栈,
2。调用Lua(这个值将被弹出)。
仍然需要一个不同的函数将每种C类型压入栈和一个不同函数从栈上取值(译注:只是取出不是弹出),
但是我们避免了组合式的爆炸(combinatorial explosion)。另外,因为栈是由Lua来管理的,
垃圾回收器知道那个值正在被C使用。几乎所有的API函数都用到了栈。
lua_pcall从栈上获取要被调用的函数并把任何临时的错误信息放在这里。
Lua以一个严格的LIFO规则(后进先出;也就是,始终存取栈顶)来操作栈。
当你调用Lua时,它只会改变栈顶部分。你的C代码却有更多的自由;更明确的来讲,
你可以查询栈上的任何元素,甚至是在任何一个位置插入和删除元素。
API有一系列压栈的函数,它将每种可以用C来描述的Lua类型压栈:
空值(nil) 用lua_pushnil,
数值型(doubele) 用lua_pushnumber,
布尔型(在C中用整数表示) 用lua_pushboolean,
任意的字符串(char*类型,允许包含'\0'字符) 用lua_pushlstring,
C语言风格(以'\0'结束)的字符串(const char*)用lua_pushstring:
void lua_pushnil (lua_State *L);
void lua_pushboolean (lua_State *L, int bool);
void lua_pushnumber (lua_State *L, double n);
void lua_pushlstring (lua_State *L, const char *s,size_t length);
void lua_pushstring (lua_State *L, const char *s);
同样也有将C函数和userdata值压入栈的函数,稍后会讨论到它们。
Lua中的字符串不是以零为结束符的;它们依赖于一个明确的长度,
因此可以包含任意的二进制数据。
将字符串压入串的正式函数是lua_pushlstring,它要求一个明确的长度作为参数。
对于以零结束的字符串,你可以用lua_pushstring(它用strlen来计算字符串长度)。
Lua是拷贝字符串而不是保持指向外部字符串(或任何其它对象,除了C函数——它总是静态指针)的指针。
对于它保持的所有字符串,Lua要么做一份内部的拷贝要么重新利用已经存在的字符串。
检测栈上是否有足够你需要的空间
int lua_checkstack (lua_State *L, int sz);
查询元素
API用索引来访问栈中的元素。
在栈中的第一个元素(也就是第一个被压入栈的)有索引1,下一个有索引2,以此类推。
我们也可以用栈顶作为参照来存取元素,利用负索引。
在这种情况下,-1指出栈顶元素(也就是最后被压入的),-2指出它的前一个元素,以此类推。
如,调用lua_tostring(L, -1)以字符串的形式返回栈顶的值。
在某些场合使用正索引访问栈较方便,另外一些情况下,使用负索引访问栈更方便。
API提供了一套lua_is*函数来检查一个元素是否是一个指定的类型,*可以是任何Lua类型。
因此有lua_isnumber,lua_isstring,lua_istable以及类似的函数。所有这些函数都有同样
的原型:
int lua_is... (lua_State *L, int index);
lua_isnumber和lua_isstring函数不检查这个值是否是指定的类型,而是看它是否能被转
换成指定的那种类型。例如,任何数字类型都满足lua_isstring。
还有一个lua_type函数,它返回栈中元素的类型。
(lua_is*中的有些函数实际上是用了这个函数定义的宏)
为了从栈中获得值,这里有lua_to*函数:
int lua_toboolean (lua_State *L, int index);
double lua_tonumber (lua_State *L, int index);
const char * lua_tostring (lua_State *L, int index);
size_t lua_strlen (lua_State *L, int index);
即使给定的元素的类型不正确,调用上面这些函数也没有什么问题。在这种情况下,
lua_toboolean、lua_tonumber和lua_strlen返回0,其他函数返回NULL。
由于ANSI C没有提供有效的可以用来判断错误发生数字值,所以返回的0是没有什么用处的。
对于其他函数而言,我们一般不需要使用对应的lua_is*函数:我们只需要调用lua_is*,
测试返回结果是否为NULL即可。
//返回的是一个指针,则需要另外找个地址拷贝值。。。。。
Lua_tostring函数返回一个指向字符串的内部拷贝的指针。你不能修改它(使你想起那里有一个const)。
只要这个指针对应的值还在栈内,Lua会保证这个指针一直有效。
当一个C函数返回后,Lua会清理他的栈,所以,有一个原则:永远不要将指向Lua字符串的指针
保存到访问他们的外部函数中。
Lua_string返回的字符串结尾总会有一个字符结束标志0,但是字符串中间也可能包含0,
lua_strlen返回字符串的实际长度。特殊情况下,假定栈顶的值是一个字符串,
下面的断言(assert)总是有效的:
//s是一个指向某个字符串地址的指针
const char *s = lua_tostring(L, -1); // any Lua string
size_t l = lua_strlen(L, -1); // its length
assert(s[l] == '\0');
assert(strlen(s) <= l);
Q:一堆的lua API老子都不清楚干了些啥,老子很不爽,老子把上面两个问题搞清楚了后,一天两个API,单步跟踪,脱掉lua MM的衣服 ^_^