LUA_API lua_arith (1)

8180 ワード

冒頭
このセクションの目標はlua_arithです.文字通りaritharithmeticの略、つまり算数の意味です.Luaの演算と密接に関係していることがわかり,Luaの算術規則を理解するのに役立つことを理解した.くだらないことは言わないで,馬を放しなさい.
解析lua_arithlua.hで宣言されています.
// lua.h 211
LUA_API void  (lua_arith) (lua_State *L, int op);
lua_Stateはluaのステータスマシンの内容であり、しばらくは関係なく、適切なタイミングで徐々に説明を展開します.これに加えて、lua_arithはパラメータとして整数データopを受信し、opoperatorの略であり、演算子を意味する.演算子は整数型なので、必ず様々な演算子の定義があり、苦労して探す必要はありません.宣言の上にあります.
#define LUA_OPADD   0   // +        /* ORDER TM, ORDER OP */    
#define LUA_OPSUB   1   // -  
#define LUA_OPMUL   2   // *  
#define LUA_OPMOD   3   // %   
#define LUA_OPPOW   4   // ^    
#define LUA_OPDIV   5   // /  
#define LUA_OPIDIV  6   // //       
#define LUA_OPBAND  7   //  
#define LUA_OPBOR   8   //  
#define LUA_OPBXOR  9   //   
#define LUA_OPSHL   10  //     
#define LUA_OPSHR   11  //     
#define LUA_OPUNM   12  //   
#define LUA_OPBNOT  13  //  
lua_arithの実装を推測すると、想像では演算子に基づいてswicthを行い、それぞれのcaseで具体的に異なる演算を実現するのが一般的でしょう.まずこのように決めて、それからその実現を見てみましょう.
// lua_api.c 302
LUA_API void lua_arith (lua_State *L, int op) {
  lua_lock(L);
  if (op != LUA_OPUNM && op != LUA_OPBNOT)  
    api_checknelems(L, 2);  /* all other operations expect two operands */
  else {  /* for unary operations, add fake 2nd operand */
    api_checknelems(L, 1);
    setobjs2s(L, L->top, L->top - 1);
    api_incr_top(L);
  }
  /* first operand at top - 2, second at top - 1; result go to top - 2 */
  luaO_arith(L, op, L->top - 2, L->top - 1, L->top - 2);
  L->top--;  /* remove second operand */
  lua_unlock(L);
}

1行目、lua_lock(L);、何の役に立つか分からないので、その定義を見てください.
// llimits.h 213
/*
** macros that are executed whenever program enters the Lua core
** ('lua_lock') and leaves the core ('lua_unlock')
*/
#if !defined(lua_lock)
#define lua_lock(L) ((void) 0)
#define lua_unlock(L)   ((void) 0)
#endif

注記は、Luaプログラムがコアcoreに入るとlua_lockが実行され、コアcoreから離れるとlua_unlockが実行されることを意味する.しかし、私たちが見たのは((void) 0)です.お母さん、これは何もしていないのではないでしょうか.何の意味があるの?仕方なくGoogleの、そこで1篇のPurpose of luaを探し当てますlock and luc_unlock、以下は節選と翻訳です.
If you port Lua to another platform, you are "allowed"to overwrite lua_lock with your own definition; and this definition should essentially be a mutex, to disallow cross-thread operations on the same Lua objects. Essentially, when implemented, it should act similarly to Python's Global Interpreter Lock (GIL). Luaを他のプラットフォームに移植する必要がある場合、lua_lockを書き換えることができる.同じLuaオブジェクトに対するスレッド間の動作を回避するために、lua_lockの定義は反発しなければならず、その実装においてその動作はPythonのグローバル解釈ロック(GIL)と類似しなければならないことに留意されたい.lua_locklua_unlockは主にスレッド間通信に用いられる場合であり、一般的には考慮する必要はなく、Luaは公式にも保持されており、マルチスレッド操作に関わる必要がある場合は、開発者自身が反発行為を実現する必要があるという意味である.もっと知りたいなら、このLua Threads Tutorialを見てみましょう.
2行目を見てみると、if (op != LUA_OPUNM && op != LUA_OPBNOT)という分岐条件が現れました.LUA_OPUNMおよびLUA_OPBNOTは単眼演算子であり、その後の注釈も補足し、その下の操作には2つの操作数、すなわち両目演算が必要である.では、else分岐は明らかに単一演算に対して、単一演算に対して、2番目の擬似オペランドが追加されたという説明がある.どういう意味ですか.私たちは後で説明します.
3行目のapi_checknelems(L, 2);に移動し、定義を見つけました.
// lapi.h 20
#define api_checknelems(L,n)    api_check(L, (n) < (L->top - L->ci->func), \
                  "not enough elements in the stack")

前節では,(L->top - L->ci->func)を紹介したが,その意味は現在のスタック内の要素の個数を知ることであり,api_checknelems(L, n)の意図は,現在のスタック内の要素が十分であるかどうかを調べることであり,インデックスnがスタック内の要素より大きい場合,スタック内に存在しない要素にアクセスすることは当然限界であると考えられる.私たちは引き続き見て、そうかどうか見てみましょう.api_checkを探し続けます.
// llimits.h 97
/*
** assertion for checking API calls
*/
#if !defined(luai_apicheck)
#define luai_apicheck(l,e)  lua_assert(e)
#endif

#define api_check(l,e,msg)  luai_apicheck(l,(e) && msg)
api_checkluai_apicheckによってバインドされ、luai_apichecklua_assertによってバインドされることに留意されたい.注記:この方法は、API呼び出しの断言を確認するために使用される.わあ、本当にTM心が疲れて、こんなに長い間探して、結果は断言であることを発見して、lua_assertの行為は今まだ明らかになっていないで、依然としてぼんやりしていて、更に探すしかありません.luai_apicheckを探して、他の定義があるかどうか見てみましょう.結果は本当にあって、疲れて、見てみましょう:
// luaconf.h 683
/*
@@ LUA_USE_APICHECK turns on several consistency checks on the C API.
** Define it as a help when debugging C code.
*/
#if defined(LUA_USE_APICHECK)
#include 
#define luai_apicheck(l,e)  assert(e)
#endif
LUA_USE_APICHECKがオンになっている場合、luai_apicheckは標準のC assertを使用します.上記に関連して、LUA_USE_APICHECKがオンでない場合、luai_apicheckLua lua_asserを使用する.OK、やった!今、私たちは安心してlua_assertを探すことができます.その結果、ニマ、また2つの定義があることが分かった!まず最初の場所を見てみましょう
// llimits.h 84
/* internal assertions for in-house debugging */
#if defined(lua_assert)
#define check_exp(c,e)      (lua_assert(c), (e))
/* to avoid problems with conditions too long */
#define lua_longassert(c)   ((c) ? (void)0 : lua_assert(0))
#else
#define lua_assert(c)       ((void)0)
#define check_exp(c,e)      (e)
#define lua_longassert(c)   ((void)0)
#endif

私たちはlua_assertが定義した状況をスキップして(その下の内容は私たちの理解にとって何の役にも立たないので)、定義されていない時の状況を見ます:#define lua_assert(c) ((void)0)よ、万歳、また何もしません.注釈がどのように解釈されているかを見てください.これは内部デバッグに使用される内部断言です.ふんふん、どういう意味?冗談で言って、また番頭を振ったんだよ.
亲爱なる客官よ、もしあなたが内部のデバッグの需要があれば、自分でlua_assertの行为を定义してください.そうしないと、当店は一切特殊な処理をしません.あなたは自分で拡張して、逆さまにしましょう、ハハハ!いらっしゃいませ!ありがとうございます.
2つ目はlualib.h 53行にあります.
// lublib.h 53
#if !defined(lua_assert)
#define lua_assert(x)   ((void)0)
#endif
lualib.hは、Luaの標準ライブラリを定義する.同じように、まだ処理されていません.断言行為を定義していなかったら、これからも特別な処理はしませんよ.
では、二つの場所は何が違うのでしょうか.推測:最初の場所は、ソースコードを修正する必要がある場合に内部デバッグを行う場合に使用されます.2つ目は外部のC APIユーザー向けですが、なぜですか?標準ライブラリのみのためです!
こんなにたくさん見て、やっとapi_checknelems(L, 2);がスタック内の要素が2つ以上あるかどうかを検査するためであることを知って、もしないならば断言を投げ出します;すなわち,スタック内の要素がオペランドの要求を満たさないとエラーとなる.同様に、単眼演算api_checknelems(L, 1);についても同様である.
苦労して、山を越えて水を渡って、私たちはついに第5行:setobjs2s(L, L->top, L->top - 1);に来ました.今回は定義を見つけて、次のようにします.
// lobject.h 190
#define checkliveness(L,obj) \
    lua_longassert(!iscollectable(obj) || \
        (righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj)))))

// lobject.h 259
#define setobj(L,obj1,obj2) \
    { TValue *io1=(obj1); *io1 = *(obj2); \
      (void)L; checkliveness(L,io1); }

// lobject.h 269
/* from stack to (same) stack */
#define setobjs2s   setobj

マクロ定義を置換すると、setobjs2s(L, L->top, L->top - 1);を次のように展開できます.
{
    TValue *io1=(L->top); 
    *io1 = *(L->top - 1);
    (void)L; 
    checkliveness(L,io1);//       ,       ,          Lua         
}


続いて、api_incr_top(L)が実行される.
#define api_incr_top(L)   {L->top++; api_check(L, L->top <= L->ci->top, \
                "stack overflow");}

スタックトップインデックスを自己増加させ、L->top <= L->ci->topを確認します.ここのスタックトップインデックスについて、なぜ自己増加しますか?コンテキストに連絡するには、2番目の擬似オペランドに空間を空ける必要があります.
次に、10行目を見てみましょう.なぜこの注釈を特に見るのか.ここには重要なヒントがあります.
/* first operand at top - 2, second at top - 1; result go to top - 2 */

以上の準備が万全になった後、現在の状況では、最初の操作数はスタック内のtop - 2カ所、2番目の操作数はtop - 1カ所にあり、その後、演算結果はtop - 2カ所に置かれます.
この節は長すぎるので,筆者も疲れたので,まず筆を置いて,次の節は続けよう.
終了
  • 勉強は煩わしくないが、紙面が長すぎて、しばらく休んでから戦うのは煩わしくない.
  • ソースコードを见て本当に多くのものを理解することができて、だから恐れないでください、いつも収获があなたを待っています.

  • リファレンス
    ** lua_lock **
  • Purpose of lua_lock and lua_unlock
  • Threads Tutorial

  • ** lua_assert **
  • lua_assert macro definition