调试C调lua模块之打印luaL_dofile错误信息

1 篇文章 0 订阅

【测试环境】

lua版本: 5.2.1

【问题场景】

在”luaL_dofile”处报错,并且没有任何错误信息。
代码如下:

#ifdef __cplusplus
extern "C" {
#endif
#include "luajit-2.0/lua.h"
#include "luajit-2.0/lualib.h"
#include "luajit-2.0/lauxlib.h"
#ifdef __cplusplus
}
#endif
static lua_State* L;

static int MyLuaInit(const char *data_dir)
{
        int ret=0;
        L = lua_open();
        if (L) {
                char lua_path[512];
                luaL_openlibs(L);           //load Lua base libraries
                sprintf(lua_path, "%s/lua/", data_dir);
                set_lua_path(L, lua_path);
                strcat(lua_path, "test.lua");
                ret = luaL_dofile(L, lua_path);
                if (0==ret) {
                        printf("import lua script success.\n");
                        return -1;
                }
                lua_close(L);
                L = NULL;
        }
        printf("import lua script error, reason: %d.\n", ret);
        return -1;
}

【问题定位】

0x00 查看API手册(传送门

函数: luaL_dofile

函数声明:

int luaL_dofile (lua_State *L, const char *filename);

函数定义:

 (luaL_loadfile(L, filename) || lua_pcall(L, 0, LUA_MULTRET, 0))

函数描述: load并run给定的lua脚本文件.如果运行成功返回值为0,失败则为1.

题外话,stackoverflow上有人问lual_dofile(); wont load script with C++ and Lua,答案很简单,题主把luaL_dofile函数错写为luaL_loadfileluaL_loadfile 这个函数只load不运行。

回到正文,这个函数的文档说“我”只能告诉你执行成功或者失败,原因不归我管。跪了。线断了

0x01 查stackoverflow

果不其然,有这个问题的答案。解决办法就是使用:

static void print_error(lua_State *L)
{
        fprintf(stderr, "\nFATAL ERROR:%s\n\n", lua_tostring(L, -1));
        exit(1);
}

问题就此解决,晚上吃鸡。


( ̄▽ ̄)~■干杯□~( ̄▽ ̄)


╮(╯_╰)╭


○| ̄|_


问题还没有解决,不开心。。

为什么用lua_tostring这个函数就能输出error信息,莫非是专门打印error函数?

0x02 再次翻看API手册

函数:lua_tostring
函数声明:

const char *lua_tolstring (lua_State *L, int index, size_t *len);

函数定义:
略(太长了,没有被写入手册)
函数描述: 等价于len=NULL的lua_tolstring。这个函数根据index从栈中取出值,并convert为C string。

也就是说,这个函数是从栈中去出C能打印的字符串的。那么,问题来了,取error信息的时候,index=-1,这能说明什么?说明栈上为-1的位置保存了错误信息,那么这个错误信息是谁推到栈里的?真相只有一个,突然想到luaL_dofile这个函数是由执行两个函数拼出来的。
关键就在于lua_pcall.

0x03 又看API手册

函数:lua_pcall
函数声明:

int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);

函数定义:

函数描述:

Calls a function in protected mode.

Both nargs and nresults have the same meaning as in lua_call. If there are no errors during the call, lua_pcall behaves exactly like lua_call. However, if there is any error, lua_pcall catches it, pushes a single value on the stack (the error message), and returns an error code. Like lua_call, lua_pcall always removes the function and its arguments from the stack.

If errfunc is 0, then the error message returned on the stack is exactly the original error message. Otherwise, errfunc is the stack index of an error handler function. (In the current implementation, this index cannot be a pseudo-index.) In case of runtime errors, this function will be called with the error message and its return value will be the message returned on the stack by lua_pcall.

Typically, the error handler function is used to add more debug information to the error message, such as a stack traceback. Such information cannot be gathered after the return of lua_pcall, since by then the stack has unwound.

The lua_pcall function returns 0 in case of success or one of the following error codes (defined in lua.h):

LUA_ERRRUN: a runtime error.
LUA_ERRMEM: memory allocation error. For such errors, Lua does not call the error handler function.
LUA_ERRERR: error while running the error handler function.

划重点,if there is any error, lua_pcall catches it, pushes a single value on the stack (the error message), and returns an error code.在执行lua的时候发生错误,则会将错误信息推到栈顶。故,-1的位置可以取到错误信息。

0x04 题外话

这个发现问题其实不难解决,不过引出了一个新的问题。在lua的大部分说明都跟栈有关系,可以看出来c中调用lua跟栈绝对有莫大关系。

要理解Lua和C++交互,首先要理解Lua堆栈。

简单来说,Lua和C/C++语言通信的主要方法是一个无处不在的虚拟栈。栈的特点是先进后出。

在Lua中,Lua堆栈就是一个struct,堆栈索引的方式可是是正数也可以是负数,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Lua与C的相互用是Lua的一个重要特性,可以使得Lua获得更高的灵活性和扩展性。Lua提供了一系列的API函数,使得Lua与C之间的交互变得更加容易。下面我将介绍一下Lua与C相互用的一些基本知识和方法: 1. Lua用C函数 Lua用C函数的方法很简单,只需要将C函数注册到Lua即可。我们可以使用lua_register函数将C函数注册到Lua,然后在Lua脚本使用该函数即可。例如,下面的代码将一个名为add的C函数注册到Lua: ``` int add(lua_State* L) { int a = luaL_checknumber(L, 1); int b = luaL_checknumber(L, 2); lua_pushnumber(L, a + b); return 1; } int luaopen_mylib(lua_State* L) { lua_register(L, "add", add); return 1; } ``` 在上面的代码,我们定义了一个名为add的C函数,该函数接受两个整数参数,然后将它们相加并将结果压入Lua。接着我们使用lua_register函数将该函数注册到Lua,并将其命名为add。最后我们将该函数打包成一个Lua模块,并使用luaopen_mylib函数将其注册到Lua。 在Lua脚本,我们可以像下面这样使用add函数: ``` local mylib = require "mylib" print(mylib.add(1, 2)) -- 输出3 ``` 2. CLua函数 CLua函数的方法比较复杂,需要使用一系列的API函数。下面是一个简单的示例: ``` int call_lua_function(lua_State* L) { // 加载Lua脚本 luaL_dofile(L, "test.lua"); // 获取Lua全局变量test lua_getglobal(L, "test"); // 判断test是否为函数 if (!lua_isfunction(L, -1)) { printf("test is not a function\n"); return 0; } // 压入函数参数 lua_pushnumber(L, 1); lua_pushnumber(L, 2); // 用函数 lua_call(L, 2, 1); // 获取函数返回值 int result = lua_tonumber(L, -1); printf("result = %d\n", result); return 0; } ``` 在上面的代码,我们首先使用luaL_dofile函数加载了一个名为test.luaLua脚本,然后使用lua_getglobal函数获取了一个名为test的全局变量,接着使用lua_pushnumber函数压入了两个参数,最后使用lua_call函数用了test函数,并将返回值压入了Lua。我们可以使用lua_tonumber函数获取返回值并将其转换为C语言的整数类型。 在Lua脚本,我们可以这样定义test函数: ``` function test(a, b) return a + b end ``` 在这个示例,我们定义了一个名为test的函数,该函数接受两个参数,然后将它们相加并返回结果。 这就是Lua与C相互用的基本方法,通过这些方法,我们可以在Lua使用C函数,也可以在C程序Lua函数,从而实现更加灵活和高效的程序设计。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值