Есть ли способ shim Lua 5.1 для поддержки мета-методов сравнения? - PullRequest
2 голосов
/ 09 января 2020

Большинство версий Lua поддерживают арифметические c операторы сравнения для таблиц. Например, рассмотрим этот сценарий, упрощенный как абстракция для обработки длин различных единиц:

local function convert_units (input)
    if type(input) ~= "table" then
        return input
    else
        if input.unit == "cm" then
            return input.amount * 10
        else
            return input.amount
        end
    end
end

local mt = {
    __lt = function (self, other)
        return convert_units(self) < convert_units(other)
    end
}

local a = {
    amount = 1.2,
    unit = "cm"
}

local b = {
    amount = 14,
    unit = "mm"
}

setmetatable(a, mt)
setmetatable(b, mt)

print(a < b)

Это выведет true, потому что в мета-таблице есть метод __lt, который приводит объекты в совместимость единиц, а затем сравнивает их. Этот код будет работать в Lua 5.1, 5.2 и 5.3

Проблема возникает, когда вы пытаетесь найти несоответствующие типы, например:

print (a < 13)

Это будет работать в Lua 5.2 и 5.3, но в Lua 5.1 будет выдано сообщение об ошибке:

lua5.1: попытка сравнить число с таблицей

Математический мета-метод совершенно способен обрабатывать необработанное число на одной стороне сравнения, но Lua 5.1 отказывается даже пытаться.

К сожалению, мне нужно иметь возможность поддерживать диапазон Lua интерпретаторов. С наименьшим общим знаменателем, равным Lua 5.1, это будет означать либо всегда делать сайт с необработанными числами аналогичным экземпляром объекта, либо всегда использовать convert_units() при написании сравнений.

Учитывая объем и Сложность кода Было бы очень хорошо, если бы я мог Shim Lua 5.1 для поддержки этого. Есть ли способ убедить его в том, что он позволяет сравнить таблицу с числом?

1 Ответ

3 голосов
/ 09 января 2020

Боюсь, это невозможно. Это может помочь, если вы сравните реализацию luaV_lessthan для 5.1 и 5.3:

int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
  int res;
  if (ttype(l) != ttype(r))
    return luaG_ordererror(L, l, r);
  else if (ttisnumber(l))
    return luai_numlt(nvalue(l), nvalue(r));
  else if (ttisstring(l))
    return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0;
  else if ((res = call_orderTM(L, l, r, TM_LT)) != -1)
    return res;
  return luaG_ordererror(L, l, r);
}

5.3:

int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
  int res;
  if (ttisnumber(l) && ttisnumber(r))  /* both operands are numbers? */
    return LTnum(l, r);
  else if (ttisstring(l) && ttisstring(r))  /* both are strings? */
    return l_strcmp(tsvalue(l), tsvalue(r)) < 0;
  else if ((res = luaT_callorderTM(L, l, r, TM_LT)) < 0)  /* no metamethod? */
    luaG_ordererror(L, l, r);  /* error */
  return res;
}

Как вы можете видеть, при выполнении функции lessthan, который генерируется в результате обнаружения < в коде, эти реализации делают совершенно разные вещи. Когда старый интерпретатор видит операнды разных типов, он выручает очень быстро. Так что просто невозможно сопоставить развернутое число с таблицей.

...