Lua接口学习
今天在程序里面使用lua_call,导致程序没有任何错误提示直接退出,找了好久原因,才知道lua_call不是一个受保护的方法,如果其中发生错误,会导致宿主程序直接退出直到看到这篇文章才恍然大悟:最后查看官方文档,更换使用了lua_pcall。没有错误的问题真心可怕啊。。。
为何使用lua_atpanic
当调用无保护的lua_call后,如果调用栈发生错误(lua_error),那么默认行为是直接退出宿主程序。(可以参考这篇文章)
要避免这样的情况,一种方法是定义自己的panic函数,并作为参数调用lua_atpanic;此外为了避免退出宿主程序,自定义的panic函数应该永不返回(通常是做一个长跳转,令其跳转至lua_call调用点,不过这种做法几乎与lua_pcall无异)
lua_atpanic 1. lua_atpanic设置新panic函数并返回旧的panic函数。
2. 当在无保护环境下发生错误,lua调用当前的panic函数并呼叫exit(EXIT_FAILURE)退出宿主程序。
3. 我们可以提供自己的panic函数以避免退出宿主程序。(一种方法是做一个长跳转(long jump))
4. panic函数可以访问位于栈顶的错误讯息。
示例
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>
#include <setjmp.h> static jmp_buf jbuf; int
panichandler(lua_State *L)
printf("%s\n", lua_tostring(L, 1));
lua_pop(L, 1);
longjmp(&jbuf, 1);
}
int main(int argc, char *argv[])
lua_State *L;
int ret = 0;
if ((L = lua_open()) == NULL) {
printf("lua_open() failed!\n");
return 1;
} luaL_openlibs(L);
lua_atpanic(L, panichandler);
if (luaL_loadfile(L, "main.lua") == 0) {
if (setjmp(&jbuf) == 0) /* first jmp */
lua_call(L, 0, 0);
else /* second jmp, error */
ret = 1; else
ret = 1; lua_close(L);
return ret;
}
luaL_checkxxx和luaL_argcheck内部调用luaL_error,luaL_error内部调用lua_error,在调用lua_error前会格式化错误信息压入栈顶,最后调用lua_error,lua_error不会返回,而是做一个长跳转。所以在C函数中使用return luaL_error也不会返回,而是跳转到调用lua_pcall点 即lua_pcall返回。
C库函数的例子
int dir(lua_State *L)
{
HANDLE h;
WIN32_FIND_DATAA fd;
const char *dirpath;
dirpath = luaL_checkstring(L, 1); /* 一旦出错立刻压入错误,并从lua_pcall返回,luaL_error也一样 */
if ((h = FindFirstFileA(dirpath, &fd)) == INVALID_HANDLE_VALUE)
return luaL_error(L, "failed to find '%s', win32 error code %d", dirpath, GetLastError());
return 0;
}
lua_error在内部调用luaD_throw函数:
void luaD_throw (lua_State *L, int errcode) {
if (L->errorJmp) {
L->errorJmp->status = errcode;
LUAI_THROW(L, L->errorJmp);
}
else {
L->status = cast_byte(errcode);
if (G(L)->panic) {
resetstack(L, errcode);
lua_unlock(L);
G(L)->panic(L);
}
exit(EXIT_FAILURE);
}
}
1. 当一个跳转点存在时,lua_error将做一个长跳转
2. 否则调用一个panic函数(如果存在),并退出宿主程序。
现在的问题是跳转点何时会存在,位于何处?
答案:LUA库函数中运行于保护模式的函数会设置跳转点,这包括lua_pcall, lua_cpcall, 以及由此函数衍生的一系列宏(如lua_dofile, luaL_dostring等)
观察lua_pcall的源码,该函数调用luaD_rawrunprotected
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
struct lua_longjmp lj;
lj.status = 0;
lj.previous = L->errorJmp; /* chain new error handler */
L->errorJmp = &lj;
LUAI_TRY(L, &lj,
(*f)(L, ud);
);
L->errorJmp = lj.previous; /* restore old error handler */
return lj.status;
}
这段代码显示了设置跳转点的过程。相比之下无保护的lua_call就没有类似的代码。
1.调用lua_pcall后,如果调用栈上发生任何错误(即lua_error被调用),lua_error将直接跳转至lua_pcall的调用点,并从lua_pcall返回。
2.调用lua_call后发生错误,lua_error将直接调用panic函数并退出宿主程序;除非通过设置自定义panic函数永不返回(通常是做长跳转以避免退出)。
当使用lua_call时,用lua_atpanic为其设置panic函数
为何使用lua_atpanic
当调用无保护的lua_call后,如果调用栈发生错误(lua_error),那么默认行为是直接退出宿主程序。要避免这样的情况,一种方法是定义自己的panic函数,并作为参数调用lua_atpanic;此外为了避免退出宿主程序,自定义的panic函数应该永不返回(通常是做一个长跳转,令其跳转至lua_call调用点,不过这种做法几乎与lua_pcall无异)
1. lua_atpanic设置新panic函数并返回旧的panic函数。
2. 当在无保护环境下发生错误,lua调用当前的panic函数并呼叫exit(EXIT_FAILURE)退出宿主程序。
3. 我们可以提供自己的panic函数以避免退出宿主程序。(一种方法是做一个长跳转(long jump))
4. panic函数可以访问位于栈顶的错误讯息。
示例
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>
#include <setjmp.h>
static jmp_buf jbuf;
int
panichandler(lua_State *L)
{
printf("%s\n", lua_tostring(L, 1));
lua_pop(L, 1);
longjmp(&jbuf, 1);
}
int main(int argc, char *argv[])
{
lua_State *L;
int ret = 0;
if ((L = lua_open()) == NULL)
{
printf("lua_open() failed!\n");
return 1;
}
luaL_openlibs(L);
lua_atpanic(L, panichandler);
if (luaL_loadfile(L, "main.lua") == 0)
{
if (setjmp(&jbuf) == 0) /* first jmp */
lua_call(L, 0, 0);
else /* second jmp, error */
ret = 1;
}
else
ret = 1;
lua_close(L);
return ret;
}
Cocos2d-x-environment-set-up
Cocos2d-x专题之一:开发环境搭建
Windows 7+Visual Studio 2012+cocos2d-x-2.1.5配置cocos2d-x win32环境
下载cocos2d-x
官网:http://cocos2d-x.org/download
(注意: 由于 2.1.2版本之后不需要使用 VC模板向导创建项目,所以2.1.2之后的版本需要 使用project-creator.py这个python脚本进行工程项目的创建和配置,不适用向导的方式创建。当然,网上也能找到很多 不通过python脚本创建的方法.)
下载python
官网:http://www.python.org/download/
建议使用2.7.x的版本,因为使用Python3以上版本可能会出现莫名其妙的问题.默认安装,路径D:\Python27
将Python加入Path环境变量
打开 [计算机]-[属性] 窗口,最左侧找到 [高级系统设置],然后会弹出 [系统属性] 页面.选择[高级] 选项卡,下边的位置有一个 [环境变量],
点击按钮,便打开[环境变量]的窗口 在[系统变量]中 [变量] 这一列中找到[Path],选择[编辑]按钮,在[变量值] 的最后位置,添加上"D:\Python27",这里要根据自己安装Python 的实际目录 填写,每个值用";" 分号分割.
加入环境变量之后,运行"cmd",输入python会出现一系列东西,配置成功
创建工程
在解压后的cocos2d-x-2.1.5文件目录"~\cocos2d-x-2.1.5\tools\project-creator"下有createproject.py脚本,运行"cmd"进入"~\cocos2d-x-2.1.5\tools\project-creator"目录(createproject.py脚本所在目录),运行以下命令生成HelloWorld cpp项目:
python create_project.py -project HelloWorld -package com.einverne.helloworld -language cpp
-project 工程名
-package Android工程包名
-language 可选{cpp|lua|javascript}分别创建C++工程,lua绑定工程,javascript绑定工程 参考官网wiki
创建成功之后在"cocos2d-x-2.1.5/projects/"目录下就可以找到HelloWorld工程,找到proj.win32工程下的sln文件用vs打开.
参考:1
Android SDK+Android NDK+Eclipse+cocos2d-x-2.1.5配置cocos2d-x Android开发环境
下载和配置Android SDK和NDK
1.Android SDK:http://developer.android.com/sdk/index.html
2.Android NDK:http://developer.android.com/tools/sdk/ndk/index.html
下载完之后解压,在SDK里有Eclipse并且各种ADT,CDT插件均已经安装好(默认JDK已经安装好了)
将NDK添加到系统环境变量(仿照Python添加系统环境变量),新建系统变量,变量名"NDK_ROOT",变量值"D:\Android\android-ndk-r9\"(根据你自己解压目录来),并在Path中添加%NDK_ROOT%,以分号隔开
配置工程
1.打开Eclipse并将"~/cocos2d-x-2.1.5/projects/HelloWorld/proj.android/"工程导入 (通过上面create_project.py方法新建工程默认已经创建Android工程)
2.导入之后有错误不要怕,一步一步来,首先,Eclipse找不到cocos2d-x Java classes,解决方案:选中Project,右击"Properties",在左边栏中找到Java Build Path,在Source Tab中找到Link Source Browse找到
$COCOS2DX-HOME/cocos2dx/platform/android/java/src/
在Folder name中写"cocos2dx src",Finish->OK.
3.项目属性->C/C++ Build->"Builder Settings"->"Build command"->改为"${NDKROOT}\ndk-build.cmd"
4.项目属性->C/C++ Build->"Environment"->添加名为"NDKMODULEPATH",值为"~\cocos2d-x-2.1.5;~\cocos2d-x-2.1.5\cocos2dx\platform\thirdparty\android\prebuilt\"的变量,根据自己的实际所在地址填写.
5.将Resources中资源文件拷贝到assets文件夹中,并且适当修改Android.mk文件
还有其他错误,我也说不上来了.安装环境的时候忘了记录,如有问题可以提出来.
参考:1
Add lua support to cpp cocos2d-x project
I am asking a question on stackoverflowNow I find a solution.
1.右击解决方案,添加项目,将"$(cocos2d-x-home)/scripting/lua/proj.win32/liblua.vcxproj"添加到解决方案
2.右击自己工程->属性->通用属性->添加新引用,勾选liblua,即可