程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> 關於C >> tolua的tolua_toxxx系列API設計

tolua的tolua_toxxx系列API設計

編輯:關於C

我們使用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);
}

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

 


摘自 loop_in_codes

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved