Lua C - достойный LuaJIT-совместимый способ выбросить ошибку в возобновляемую сопрограмму - PullRequest
2 голосов
/ 27 марта 2019

Короче говоря, хотя код, кажется, работает нормально, мне любопытно, существуют ли менее хакерские подходы (чем те, которые я придумал) с *. 1001 *

Предположим, вы создали сопрограмму с помощью lua_newthread , а затем приостановили ее из cclocure с помощью lua_yield . Ваша программа обходит код не-Lua, и теперь пришло время возобновить сопрограмму с помощью lua_resume , но - предположим, что аргументы, предоставленные кодом Lua, крайне недопустимы , и мы должен дать ему ошибку, чтобы указать, что.

Как вы, возможно, знаете, вы не можете вызвать lua_error (или luaL_error ) для состояния, если оно в данный момент не запущено. Таким образом, состояние должно возобновиться, но сразу же возникнет ошибка.

В 5.3 вы должны использовать lua_yieldk , предоставлять функцию продолжения и вызывать там lua_error или luaL_error. Et voilà.

Но, увы, LuaJIT не реализует lua_yieldk, так какие варианты у нас остались?

  1. Одноразовый крючок

    Предположим, сообщение об ошибке хранится в char error_text[256]. Затем мы могли бы связать хук для каждой инструкции непосредственно перед возобновлением,

    lua_sethook(L, throw_error, LUA_MASKCOUNT, 1);
    int result = lua_resume(L, NULL, ret_count);
    

    , а затем отсоедините крючок и добавьте туда ошибку

    void throw_error(lua_State *L, lua_Debug *ar) {
        lua_sethook(L, throw_error, 0, 0);
        if (error_text == nullptr) return; // trust no one, especially yourself
        luaL_error(L, "%s", error_text);
    }
    

    Если потребуется очистка строки, вы, конечно, предпочли бы объединить error_text до luaL_where(L, 1) перед вызовом _error, так как lua_error и luaL_error делают длинный прыжок и, таким образом, будут последним Ваша функция выполняет.

  2. Упаковщик на стороне Lua

    Предположим, вы решили использовать что-то вроде node.js и возобновить ваш код C с парой error, result, чтобы вы могли иметь функцию-обертку, такую ​​как

    function some(arg)
        local e, r = some_native(arg)
        if (e) then
            error(e)
        else
            return r
        end
    end
    

    Или, может быть, полностью реорганизовать ваш API, чтобы ошибки также обрабатывались с использованием того же шаблона, но это история для другого дня.

Вариант 1 кажется менее хакерским из двух.

Вариант 2, скорее всего, не вызовет никаких проблем.

Я не могу избавиться от ощущения, что есть намного лучший способ сделать это, что я упускаю из виду (в конце концов, lua_yieldk - относительно недавнее дополнение).

...