• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            loop_in_codes

            低調做技術__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx

            tolua的tolua_toxxx系列API設計

            原文鏈接:http://codemacro.com/2012/05/10/tolua-api/

            我們使用tolua++手工綁定c/c++接口到lua中,在綁定的接口實現里,就需要取出傳入的參數。tolua++中提供了一系列tolua_toxxx函數,例如:

            lua_Number tolua_tonumber(lua_State *L, int narg, lua_Number def)
            const char *tolua_tostring(lua_State *L, int narg, const char *def)
            

            這些函數都有一個def參數。乍一看,這些函數使用起來很簡單。傳入lua_State,傳入參數在棧中的位置,然后再傳一個失敗后返回的默認值。

            我重點要說的是這里這個失敗,按正常程序員的理解,針對lua而言,什么情況下算失敗呢?lua語言里函數參數支持不傳,此時實參為nil,將nil轉換為一個c類型必然失敗;參數類型不正確算不算失敗?你傳一個user data,c里按數字來取,這也算失敗。

            這么簡單的API還需要多糾結什么呢?然后我們浩浩蕩蕩地寫了上百個接口,什么tolua_tostring/tolua_tonumber的使用少說也有500了吧?

            然后有一天,服務器宕機了,空指針:

            /* 失敗返回"",還能省空指針的判斷 */
            const char *name = tolua_tostring(L, 1, "");
            if (name[0] == '\0') { /* 空串總得判斷吧 */
             ...
            }
            

            跟蹤后發現,腳本里傳入的是nil,這里的name取出來是NULL,而不是”“(的地址)。然后吐槽了一下這個API,辛苦地修改了所有類似代碼,增加對空指針的判斷。我沒有多想。

            故事繼續,有一天服務器雖然沒宕機,但功能不正常了:

            float angle = (float) tolua_tonumber(L, 1, 2 * PI);
            ...
            

            這個意思是,這個函數的參數1默認是2*PI,什么是默認?lua里某函數參數不傳,或傳nil就是使用默認。因為不傳的話,這個實參本身就是nil。但,tolua_tonumber的行為不是這樣的,它的實現真是偷懶:

            TOLUA_API lua_Number tolua_tonumber (lua_State* L, int narg, lua_Number def)
            {
             return lua_gettop(L)<abs(narg) ? def : lua_tonumber(L,narg);
            }
            TOLUA_API const char* tolua_tostring (lua_State* L, int narg, const char* def)
            {
             return lua_gettop(L)<abs(narg) ? def : lua_tostring(L,narg);
            }
            

            意思是,只有當你不傳的時候,它才返回默認值,否則就交給lua的API來管,而lua這些API是不支持應用層的默認參數的,對于lua_tonumber錯誤時就返回0,lua_tostring錯誤時就返回NULL。

            這種其行為和其帶來的common sense不一致的API設計,實在讓人蛋疼。什么是common sense呢?就像一個UI庫里的按鈕,我們都知道有click事件,hover事件,UI庫的文檔甚至都不需要解釋什么是click什么是hover,因為大家看到這個東西,就有了共識,無需廢話,這就是common sense。就像tolua的這些API,非常普通,大家一看都期待在意外情況下你能返回def值。但它竟然不是。實在不行,你可以模仿lua的check系列函數的實現嘛:

            LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) {
             lua_Number d = lua_tonumber(L, narg);
             if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */
             tag_error(L, narg, LUA_TNUMBER);
             return d;
            }
            

            即,根本不用去檢查棧問題,直接在lua_tonumber之后再做包裝檢查。何況,lua需要你去檢查棧嗎?當你訪問了棧外的元素時,lua會自動返回一個全局常量luaO_nilobject:

            static TValue *index2adr(lua_State *L, int idx) {
             ...
             if (o >= L->top) return cast(TValue*, luaO_nilobject);
            }
            

            另,程序悲劇也來源于臆想。

            posted on 2012-05-10 15:38 Kevin Lynx 閱讀(5290) 評論(0)  編輯 收藏 引用 所屬分類: c/c++lua

            99re这里只有精品热久久| 亚洲国产成人久久精品99| 97香蕉久久夜色精品国产| 久久亚洲日韩看片无码| 国产欧美久久久精品影院| 久久午夜无码鲁丝片秋霞| 久久亚洲美女精品国产精品| 99久久精品午夜一区二区| 国产毛片久久久久久国产毛片| 久久精品人人做人人爽电影蜜月 | 99久久精品午夜一区二区| 93精91精品国产综合久久香蕉| 久久人人青草97香蕉| 91久久成人免费| 亚洲AV日韩AV天堂久久| 品成人欧美大片久久国产欧美... 品成人欧美大片久久国产欧美 | 亚洲精品乱码久久久久久按摩| 国产精品久久国产精品99盘| 国产69精品久久久久观看软件| 国产精品岛国久久久久| 久久人人爽人人人人片av| 成人国内精品久久久久一区| 亚洲国产天堂久久综合| 久久天天日天天操综合伊人av| a级成人毛片久久| 大美女久久久久久j久久| 一级做a爰片久久毛片人呢| 国产精品伊人久久伊人电影| 精品多毛少妇人妻AV免费久久| 久久久精品免费国产四虎| 久久久久黑人强伦姧人妻| 久久久精品国产免大香伊| 亚洲国产精品久久66| 久久精品国产福利国产秒| 亚洲国产精品无码成人片久久| 久久久久久久97| 精品国产综合区久久久久久| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 国产69精品久久久久9999APGF| 久久亚洲AV无码西西人体| 亚洲国产欧洲综合997久久|