Ошибка 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 только для буквальных логических значений. Поэтому, если он увидит ноль, он вернет, что логическое значение не было передано. Я только что удалил проверку ошибок для логического литерала, так как люди (включая меня) обычно полагаются на аргументы, которые не являются логическими литералами и правильно обрабатываются как логические.


person random    schedule 11.07.2010    source источник
comment
FWIW, сообщения об ошибках - это не просто тарабарщина. Всякий раз, когда вы получаете ошибки, всегда полезно публиковать сообщения. Обычно они говорят вам, где именно и в чем проблема. Мы будем рады помочь расшифровать их, чтобы вы могли воспользоваться ими в следующий раз.   -  person Cogwheel    schedule 11.07.2010
comment
Я разместил это. Это не было тарабарщиной. Было очень ясно, в чем проблема. Не передал логическое значение в SetNoClip.   -  person random    schedule 11.07.2010
comment
О, извините, я пропустил это ›‹ нужно больше кофеина (как и человек, который проголосовал за мой комментарий: P). Я сделал его более заметным...   -  person Cogwheel    schedule 11.07.2010
comment
FWIW, это, кажется, противоречит идиомам Lua. Обычно логическая функция должна принимать любые входные данные и рассматривать их истинность, а не сравнивать их напрямую с true или false. Меня бы раздражал этот API...   -  person Cogwheel    schedule 11.07.2010


Ответы (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" )

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

person Amber    schedule 11.07.2010
comment
Спасибо, именно то, что мне нужно было знать. Однако это не объясняет, пока он все еще оценивается как логическое значение в условном выражении if. Делайте операторы if, выполняющие неявное преобразование типов, а передача аргументов — нет (т. е. if ( nil ) преобразует nil в false, а SetNoClip ( nil ) — нет). - person random; 11.07.2010
comment
Потому что так было задумано if. Когда мы говорим «считается истинным» и «считается ложным», if является одной из вещей, которые принимают во внимание. Все значения считаются true, кроме false и nil. - person Cogwheel; 11.07.2010
comment
Да, но почему бы это не применить и к передаче аргументов функции? Если функция ожидает логическое значение и получает nil, можно подумать, что она преобразует его в false, как это делают условные операторы. Для этого есть причина? - person random; 11.07.2010
comment
Это конструктивный недостаток функции, ИМО. Функции, которые ожидают логических значений, должны обрабатывать значения так же, как if. - person Cogwheel; 11.07.2010
comment
Ну, SetNoClip использует lua_toboolean. И в документации говорится... Преобразует значение Lua с заданным допустимым индексом в логическое значение C (0 или 1). Как и все тесты в Lua, lua_toboolean возвращает 1 для любого значения Lua, отличного от false и nil; в противном случае он возвращает 0. Он также возвращает 0 при вызове с недопустимым индексом. (Если вы хотите принимать только фактические логические значения, используйте lua_isboolean для проверки типа значения.) Таким образом, nil должен оцениваться как false при вызове lua_toboolean. В чем проблема? P.S. Пришлось не отвечать. Не работает. (Не сохранил файл перед тестированием). - person random; 11.07.2010
comment
Установив это как ответ, поскольку он затрагивает основную часть проблемы, но часть этого (к сожалению) не может быть решена из предоставленной мной информации. Размещение дополнительной информации в редактировании. - person random; 11.07.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;
}
person u0b34a0f6ae    schedule 12.07.2010
comment
Да, именно так функция была изменена. Обратите внимание на часть редактирования, в которой говорится «имело» это определение. - person random; 12.07.2010

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

foo and bar

вернет foo, если foo равно nil или false, иначе вернет bar.

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

person Cogwheel    schedule 11.07.2010