Ошибка 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 может подавиться строкой.

Другие вопросы по тегам