Ошибка Lua не прошла логическое
Это работает...
if ( tileType == "water" or
( otherObj and otherObj:GetType() == "IceBlock" )) then
self:SetNoClip( true )
else
self:SetNoClip( false )
end
- Это не...
self:SetNoClip( tileType == "water" or
( otherObj and otherObj:GetType() == "IceBlock" ))
//----------------------------------------------------------
local noClip = ( tileType == "water" or
( otherObj and otherObj:GetType == "IceBlock" ))
self:SetNoClip( noClip )
otherObj
Тест только оценивает, является ли otherObj nil
или нет. Указанные переменные извлекаются в предыдущей строке. Ошибка, которую я получаю при запуске приложения:
unprotected error to call in Lua API(script path...: Did not pass boolean to SetNoClip).
SetNoClip - это функция в приложении, которая захватывает аргумент, помещенный в стек lua, через lua_toboolean
,
Так почему же работает первая и вторая и третья ошибки?
РЕДАКТИРОВАТЬ:
SetNoClip имел это определение.
int GameObject::LuaSetNoClip( lua_State *L ) {
if ( !lua_isboolean( L, -1 )) {
LogLuaErr( "Did not pass boolean to SetNoClip for GameObject: " + m_type );
return luaL_error( L, "Did not pass boolean to SetNoClip" );
}
m_noClip = lua_toboolean( L, -1 );
return 0;
}
Проблема в том, что lua_isboolean
не делает никаких неявных преобразований типов (но lua_toboolean
делает) и будет возвращать true только для литеральных логических значений. Поэтому, если он увидит ноль, он вернет, что логическое значение не было передано. Я только что удалил проверку ошибок для логического литерала, так как люди (включая меня) обычно полагаются на аргументы, которые не являются логическими литералами и обрабатываются правильно как логические.
3 ответа
and
оператор возвращает свой первый аргумент, если это значение считается неверным, и второй аргумент в противном случае.
or
оператор возвращает свой первый аргумент, если он считается истинным, а второй аргумент в противном случае.
Таким образом, A or (B and C)
может теоретически вернуть любое из следующего:
A
еслиA
это значение считается истиннымB
еслиB
это значение считается ложным иA
считается ложнымC
если ни один из вышеперечисленных не имеет место
Обратите внимание, что A
, B
, а также C
не обязательно должны быть действительными логическими значениями - только то, что может быть интерпретировано как логическое значение. поскольку nil
считается ложным значением, возможно, что второй случай происходит для вас, и аргумент передается SetNoClip
является nil
вместо true
или же false
,
Один из способов исправить это - явно сравнить с nil, а не просто использовать объект:
( otherObj ~= nil and otherObj:GetType() == "IceBlock" )
так как ~=
Оператор гарантированно вернет логическое значение.
Как вы уже видели, любой объект может иметь логическую интерпретацию в Lua. Из-за этого ваша реализация LuaSetNoClip не соответствует духу и практике Lua, если вы ожидаете, что будет передан только логический объект. Вы должны использовать lua_toboolean
непосредственно.
Я думаю, что единственная проверка аргументов, которую вы можете разумно сделать, это luaL_checkany
:
int GameObject::LuaSetNoClip( lua_State *L ) {
luaL_checkany(L, 1);
m_noClip = lua_toboolean( L, 1 );
return 0;
}
Это зависит от того, что происходит внутри SetNoClip (т.е. это не обязательно вызовет проблемы в целом). Тем не менее, я уверен, что проблема в том, что and
а также or
вернуть значения по обе стороны, а не оценки true
или же false
, Другими словами,
foo and bar
вернусь foo
если foo
либо nil
или же false
иначе вернется bar
,
В вашем случае, оригинальный код проходит true
или же false
в SetNoClip, тогда как в других примерах вы либо передаете "water"
или логическое значение для SetNoClip. SetNoClip может подавиться строкой.