Chinaunix首页 | 论坛 | 博客
  • 博客访问: 201186
  • 博文数量: 81
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 540
  • 用 户 组: 普通用户
  • 注册时间: 2014-05-04 12:48
文章分类
文章存档

2015年(23)

2014年(58)

我的朋友

分类: C/C++

2014-09-24 17:32:19

    当我练习这一部分的时候,发现了一个问题——直接调用luaopen_io会使C程序crash。我使用VC++ 2005编译C代码,使用由lua-5.1.4生成的DLL。


    我查了Lua5.1参考手册,上面有几处涉及到了这个问题:


    To have access to these libraries, the C host program should call the luaL_openlibs function, which opens all standard libraries. Alternatively, it can open them individually by calling luaopen_base(for the basic library), ... and 
luaopen_debug
(for the debug library). These functions are declared in 
lualib.h
and should not be called directly: you must call them like any other Lua C function, e.g., by using lua_call. 


        ...


        The 
luaopen_*
functions (to open libraries) cannot be called directly, like a regular C function. They must be called through Lua, like a Lua function.


    大体意思就是说,你不能直接调用luaopen_*这些用来打开标准库的函数,你必须通过Lua来调用它们,比如使用lua_call。


    没有具体例子。那就靠自己琢磨了。


    之后我尝试用lua_pcall来修改对luaopen_io的直接调用,方法如下:


    lua_getglobal(L, "luaopen_io");


    lua_pcall(L, 0, 0, 0);


    但是结果令我很失望,lua_pcall返回2,即LUA_ERRRUN:运行时错误。


    然后我又尝试使用lua_cpcall,方法如下:


    lua_cpcall(L, luaopen_io, NULL);


    让人欣喜的是,它可以顺利运行。


    我不满足于现状,又翻出源码来看。在lua-5.1.4的源码中,linit.c这个文件里实现了luaL_openlibs这个函数,具体如下:


    static const luaL_Reg lualibs[] = {
      {"", luaopen_base},
      {LUA_LOADLIBNAME, luaopen_package},
      {LUA_TABLIBNAME, luaopen_table},
      {LUA_IOLIBNAME, luaopen_io},
      {LUA_OSLIBNAME, luaopen_os},
      {LUA_STRLIBNAME, luaopen_string},
      {LUA_MATHLIBNAME, luaopen_math},
      {LUA_DBLIBNAME, luaopen_debug},
      {NULL, NULL}
    };




    LUALIB_API void luaL_openlibs (lua_State *L) {


      const luaL_Reg *lib = lualibs;
      for (; lib->func; lib++) {
        lua_pushcfunction(L, lib->func);
        lua_pushstring(L, lib->name);
        lua_call(L, 1, 0);
      }
    }


    有源码,自然一切一目了然。使用lua_call来间接调用luaopen_*的方法就是彩色字体显示的,先将函数压栈,再将参数(库的名字)压栈,最后调用lua_call(一个参数,0个返回值)。


    我又上网查找相关主题。时间充裕的情况下,我对解决方案的态度是务求尽善尽美。


    网上对相关主题的解释很少,都是提问题,没找到完美的解答。


    我在这里总结一下吧,希望对读这篇文章的人有所裨益。




首先,官方文档里面提到不要直接调用luaopen_*这些函数,那么就不要直接调用了。 
不能直接调用的原因,我也不清楚。表面看来就是liolib.c中的luaopen_io函数的实现里调用了三个newfenv和一个lua_setfenv,它们创建并设置了新的环境。但是为什么新的环境就使程序crash了呢?以我现在的能力还无法给出解释。
使用lua_call、lua_pcall、lua_cpcall来间接调用这些函数。 
具体方法:
      1. lua_cpcall(L, luaopen_io, NULL);


      2. lua_pushcfunction(L, luaopen_io);
         lua_pushstring(L, LUA_IOLIBNAME);
         lua_pcall(L, 1, 0, 0);      


      3. lua_pushcfunction(L, luaopen_io);
         lua_pushstring(L, LUA_IOLIBNAME);
         lua_call(L, 1, 0);




需要补充说明的是, lua_cpcall、lua_pcall都可以检查返回值来判断是否出错;lua_call没有返回值,如果调用这个函数出问题,典型结果就是程序crash。所以我比较倾向于使用lua_cpcall或lua_pcall,因为它们让你来决定如何处理打开标准库失败的情况。lua_pcall、lua_call既可以调用Lua函数也可以调用lua_CFunction类型的C函数,而lua_cpcall只能用来调用lua_CFunction类型的C函数。当然,想了解细节还是要阅读参考手册和源码。
      给大家一个整体感觉吧:


      ...  




int main(void)
{


 ...
 lua_State *L = lua_open();
 if ( NULL == L ) {
  fprintf(stderr, "lua_open() failed\n");
  return -1;
 }
 lua_pushcfunction(L, luaopen_base); /* 间接调用luaopen_base */
 lua_pushstring(L, "");
 lua_call(L, 1, 0);
 lua_pushcfunction(L, luaopen_math); /* 间接调用luaopen_math */
 lua_pushstring(L, LUA_MATHLIBNAME);
 if (lua_pcall(L, 1, 0, 0) != 0)
  goto EXITAPP;
 if (lua_cpcall(L, luaopen_io, NULL) != 0) /* 间接调用luaopen_io */
  goto EXITAPP;
 if (lua_cpcall(L, luaopen_os, NULL) != 0)
  goto EXITAPP;
 if (lua_cpcall(L, luaopen_package, NULL) != 0)
  goto EXITAPP;
 if (lua_cpcall(L, luaopen_string, NULL) != 0)
  goto EXITAPP;
 if (lua_cpcall(L, luaopen_table, NULL) != 0)
  goto EXITAPP;


 ...


 lua_close(L);
 return 0;


EXITAPP:
 lua_close(L);
 return -1;
}


以上的想法、做法都是特定于我自己的开发环境。如果你的开发环境和我的不一样,我提供的方法仅供参考。你要尝试不同方法来得到解决问题的办法,多加努力。
阅读(1475) | 评论(0) | 转发(0) |
0

上一篇:extern "C"的用法解析

下一篇:lua的Debug库

给主人留下些什么吧!~~