【问题标题】:What's the difference behind normal function call and pcall普通函数调用和 pcall 有什么区别
【发布时间】:2013-05-20 03:21:58
【问题描述】:

我正在使用 lua,并且我知道 pcall 用于受保护的调用,我的问题是,两种调用方式是否都归结为相同的 C 代码。例如

function a(arg)
   ...
end

正常调用:

a(arg)

受保护的调用:

pcall(a, arg)

实际上我正在使用 'lua_lock/lua_unlock' 来保护 lua_State 免受损坏。并形成源代码(lua 5.1.4)我可以看到'lua_pcall'正在调用'lua_lock/lua_unlock',但我不确定正常的函数调用方式是基于'lua_pcall'还是使用'lua_lock/lua_unlock'?如果不是,是否意味着我必须更改所有调用 'pcall(...)' 的函数才能从 'lua_lock/lua_unlock' 中受益?

谁能解释一下?谢谢

【问题讨论】:

    标签: lua


    【解决方案1】:

    pcall 用于处理 lua 中的错误。我制作了以下示例来演示如何使用它:

    首先我们创建一个我知道会产生错误的函数

    function makeError(n)
        return 'N'+n;
    end 
    

    现在作为我们的第一个示例,我们定义以下内容

    function pcallExample1()
        if pcall(makeError,n) then 
            print("no error!")
        else 
            print("That method is broken, fix it!")
        end 
    end 
    

    我们调用pcallExample1

    pcallExample1()
    

    并得到输出:

    Lua 5.1.3  Copyright (C) 1994-2008 Lua.org, PUC-Rio
    That method is broken, fix it!
    

    相反的证明:

    function pcallExample2()
        if makeError(5) then 
            print("no error!")
        else 
            print("That method is broken, fix it!")
        end 
    end 
    

    调用它会使错误保持未处理并冒泡显示:

    lua: /Users/henryhollinworth/Desktop/s.lua:2: attempt to perform arithmetic on a string value
    

    对于C,pcall定义为

    static int luaB_pcall (lua_State *L) {
      int status;
      luaL_checkany(L, 1);
      lua_pushnil(L);
      lua_insert(L, 1);  /* create space for status result */
      status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, pcallcont);
      return finishpcall(L, (status == LUA_OK));
    }
    

    lua_pcallk 在哪里

    LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc,
                            int ctx, lua_CFunction k) {
      struct CallS c;
      int status;
      ptrdiff_t func;
      lua_lock(L);
      api_check(L, k == NULL || !isLua(L->ci),
        "cannot use continuations inside hooks");
      api_checknelems(L, nargs+1);
      api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
      checkresults(L, nargs, nresults);
      if (errfunc == 0)
        func = 0;
      else {
        StkId o = index2addr(L, errfunc);
        api_checkvalidindex(L, o);
        func = savestack(L, o);
      }
      c.func = L->top - (nargs+1);  /* function to be called */
      if (k == NULL || L->nny > 0) {  /* no continuation or no yieldable? */
        c.nresults = nresults;  /* do a 'conventional' protected call */
        status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
      }
      else {  /* prepare continuation (call is already protected by 'resume') */
        CallInfo *ci = L->ci;
        ci->u.c.k = k;  /* save continuation */
        ci->u.c.ctx = ctx;  /* save context */
        /* save information for error recovery */
        ci->u.c.extra = savestack(L, c.func);
        ci->u.c.old_allowhook = L->allowhook;
        ci->u.c.old_errfunc = L->errfunc;
        L->errfunc = func;
        /* mark that function may do error recovery */
        ci->callstatus |= CIST_YPCALL;
        luaD_call(L, c.func, nresults, 1);  /* do the call */
        ci->callstatus &= ~CIST_YPCALL;
        L->errfunc = ci->u.c.old_errfunc;
        status = LUA_OK;  /* if it is here, there were no errors */
      }
      adjustresults(L, nresults);
      lua_unlock(L);
      return status;
    }
    

    对比lua_callk

    LUA_API void lua_callk (lua_State *L, int nargs, int nresults, int ctx,
                            lua_CFunction k) {
      StkId func;
      lua_lock(L);
      api_check(L, k == NULL || !isLua(L->ci),
        "cannot use continuations inside hooks");
      api_checknelems(L, nargs+1);
      api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
      checkresults(L, nargs, nresults);
      func = L->top - (nargs+1);
      if (k != NULL && L->nny == 0) {  /* need to prepare continuation? */
        L->ci->u.c.k = k;  /* save continuation */
        L->ci->u.c.ctx = ctx;  /* save context */
        luaD_call(L, func, nresults, 1);  /* do the call */
      }
      else  /* no continuation or no yieldable */
        luaD_call(L, func, nresults, 0);  /* just do the call */
      adjustresults(L, nresults);
      lua_unlock(L);
    }
    

    请注意,两者都使用lua_lock()lua_unlock()。锁定和解锁lua_State

    【讨论】:

    • 请注意,通常 - 特别是对于开发 - 使用xpcall 是更可取的,因为您实际上可以获得错误消息并从中进行回溯,而在发生错误时它仍然不会影响 Lua 运行时。
    • 我们还能从中得到原来的错误字符串吗?
    猜你喜欢
    • 1970-01-01
    • 2017-12-23
    • 1970-01-01
    • 2014-04-08
    • 1970-01-01
    • 1970-01-01
    • 2019-05-24
    • 2020-09-22
    • 1970-01-01
    相关资源
    最近更新 更多