There are some very confusing behaviors in Lua C API. Here are some explanations for these. (Tested on Lua 5.4)
1. Type of number, integer, string
Operations like lua_isnumber, lua_pushinteger, lua_isstring don’t mean checking the value’s type:
- Values with type
numbercan also get true oflua_isstring. (Number is also string) - There are no integer types in Lua. Values generated by
lua_pushintegerhave typenumber. - Only values generated by
lua_pushintegercan get true oflua_isinteger. (But get false afterlua_tostring) lua_isnumbergets true if the value is a realnumberor astringbut convertible to number.
lua_tostring will implicitly convert number to string:
- After calling
lua_tostringon a value of typenumber, the value’s type will change tostringimplicitly, but it is stilllua_isnumber. - After calling
lua_tostringon a value generated bylua_pushinteger, it cannot get true oflua_isintegeranymore. (String is always not integer)
Test cases
| Lua value generated by | lua_type | lua_typename | lua_isinteger | lua_isnumber | lua_isstring |
|---|---|---|---|---|---|
lua_pushinteger |
LUA_TNUMBER : 3 | number | true | true | true |
lua_pushinteger then lua_tostring |
LUA_TSTRING : 4 | string | false | true | true |
lua_pushnumber[1] |
LUA_TNUMBER : 3 | number | false | true | true |
lua_pushnumber[1] then lua_tostring |
LUA_TSTRING : 4 | string | false | true | true |
lua_pushstring on string of number[2] |
LUA_TSTRING : 4 | string | false | true | true |
lua_pushstring on string of not number[3] |
LUA_TSTRING : 4 | string | false | false | true |
[1] Applies to all float or integer numbers. e.g. lua_pushnumber(L, 0), lua_pushnumber(L, 123), lua_pushnumber(L, 0.1), etc.
[2] e.g. “123”, “1.23”, “12.”, “.12”, “-1.23”, “+1.23”, “1.2e3”, “-1.2E-3”, etc.
[3] e.g. “1.2.3”, “1+2”, “hello”, “inf”, etc.
Conclusion
- Integer is not a type. Integer is a special case of number.
- Number is also string.
- String is also number if it is convertible to number.
- String is always not integer.
lua_tostringwill implicitly convert number to string.
More to say
Not like lua_tostring, luaL_tolstring won’t convert number to string.
But it can convert any value to string, while lua_tostring will return NULL
if the value is not a string or number.
2. Type of userdata, light userdata
Userdata and light userdata have different type ID, but they have a same type name.
lua_islightuserdata is a subset of lua_isuserdata:
lua_isuserdatareturns true for both userdata and light userdata. (Light userdata is userdata)lua_islightuserdataonly returns true for light userdata.
Test cases
| Lua value generated by | lua_type | lua_typename | lua_islightuserdata | lua_isuserdata |
|---|---|---|---|---|
lua_pushlightuserdata |
LUA_TLIGHTUSERDATA : 2 | userdata | true | true |
lua_newuserdata |
LUA_TUSERDATA : 7 | userdata | false | true |
Conclusion
- Type ID of light userdata is “LUA_TLIGHTUSERDATA” (value 2), type ID of userdata is “LUA_TUSERDATA” (value 7), but they have a same type name “userdata”.
- Light userdata is a subset of userdata, although they have different type ID.
- Light userdata is userdata.
3. Metatable of light userdata
Light userdata (unlike heavy userdata) have no per-value metatables. All light userdata share the same metatable, which by default is not set (nil).