Ошибка Lua не прошла логическое - PullRequest
3 голосов
/ 11 июля 2010

Это работает ...

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.

Так почему же работает первая, вторая и третья ошибки?

EDIT:

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 ]

1 голос
/ 11 июля 2010

Оператор 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" )

, поскольку оператор ~= гарантированно возвращает логическое значение.

1 голос
/ 12 июля 2010

Как вы видели, любой объект может иметь булеву интерпретацию в 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;
}
0 голосов
/ 11 июля 2010

Это зависит от того, что происходит внутри SetNoClip (т.е. это не обязательно вызовет проблемы в целом).Тем не менее, я почти уверен, что проблема в том, что and и or возвращают значения с обеих сторон, а не оценивают в true или false.Другими словами,

foo and bar

вернет foo, если foo будет nil или false, в противном случае он вернет bar.

В вашем случаеисходный код передает true или false в SetNoClip, тогда как в других примерах вы либо передаете "water", либо логическое значение в SetNoClip.SetNoClip может подавиться строкой.

...