We use tolua ++ to manually bind the c/c ++ interface to lua. In the bound interface implementation, we need to retrieve the input parameters. Tolua ++ provides a series of tolua_toxxx functions, such:
Lua_Number tolua_tonumber (lua_State * L, int narg, lua_Number def)
Const char * tolua_tostring (lua_State * L, int narg, const char * def)
All these functions have a def parameter. At first glance, these functions are easy to use. Input lua_State, input the location of the parameter in the stack, and then pass a default value returned after a failure.
What I want to talk about is this failure. According to the understanding of normal programmers, what is the case for lua as a failure? In lua, function parameters cannot be passed. If the real parameter is nil, converting nil to a c type will inevitably fail. If the parameter type is incorrect, is it not a failure? If you upload a user data file and use numbers in c to retrieve the data, this is also a failure.
What do you need to do with such a simple API? Then, we wrote hundreds of interfaces in a great deal of ways. What is tolua_tostring/tolua_tonumber? I mean, there are also 500?
Then one day, the server went down, with a null pointer:
/* "" Is returned for failure, and NULL pointer judgment can be saved */
Const char * name = tolua_tostring (L, 1 ,"");
If (name [0] = '\ 0') {/* null string must be determined */
...
}
After tracking, we found that nil is passed in the script, and the name here is NULL, rather than "" (address ). Then I threw out this API, and worked hard to modify all the similar Code to increase the NULL pointer judgment. I don't think much about it.
The story goes on. One day, although the server is not down, its function is abnormal:
Float angle = (float) tolua_tonumber (L, 1, 2 * PI );
...
This means that the default value of parameter 1 of this function is 2 * PI. What is the default value? In lua, if a function parameter is not transmitted or nil is passed, the default value is used. If this parameter is not input, the real parameter itself is nil. However, tolua_tonumber does not act like this, and its implementation is really lazy:
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 );
}
This means that the default value is returned only when you do not pass the API. Otherwise, the default value is left for lua APIs, which do not support the default parameters of the application layer, 0 is returned for lua_tonumber errors, and NULL is returned for lua_tostring errors.
This kind of API design, which is inconsistent with the common sense it brings, is really annoying. What is common sense? Like a button in the UI library, we all know that there are click events, hover events, and UI library documents that do not even need to be explained. What is click and what is hover, there is consensus and no nonsense. This is the common sense. Just like tolua's APIs, they are very common. You may expect that you will return the def value in case of an accident. But it is not. Actually, you can imitate the implementation of lua's check series functions:
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;
}
That is, there is no need to check the stack, and the package check is directly performed after lua_tonumber. What's more, does lua need to check the stack? When you access elements outside the stack, lua will automatically return a global constant luaO_nilobject:
Static TValue * index2adr (lua_State * L, int idx ){
...
If (o> = L-> top) return cast (TValue *, luaO_nilobject );
}
In addition, the program tragedy also comes from speculation.
From loop_in_codes