Как обрабатывать исключения C ++ при вызове функций из Lua? - PullRequest
22 голосов
/ 06 января 2011

У меня есть рабочая функция C ++, которую я могу вызвать из Lua. Чтобы продемонстрировать мою проблему, вот пример:

int PushHello(lua_State *L){
    string str("Hello");
    lua_pushlstring(L, str.data(), str.length());
    return 1;
}

Примечание: я знаю, что мне не нужно использовать строковую переменную там, но она есть, чтобы продемонстрировать проблему.

Вот две мои проблемы:

  1. Когда я вызываю эту функцию из Lua, конструктор строки может выдать исключение. Это проблема? Будет ли Lua справиться с этим и правильно раскрутить стек Lua? Я так не думаю. Как я могу решить это? Нужно ли добавлять try/catch вокруг всего такого кода и преобразовывать исключение в lua_error? Нет ли лучшего решения?

  2. Другая проблема, которую я, вероятно, решил, скомпилировав Lua в C ++, это когда lua_pushlstring() вызывает lua_error() строковый деструктор, который не будет вызываться при использовании longjmp. Решена ли проблема путем компиляции как C ++ и выдачи исключений вместо использования longjmp?

Чтобы уточнить, возможное решение проблемы 1 было бы следующим:

int PushHello(lua_State *L){
    string str;
    try{
        str.assign("Hello");
    catch(exception &e){
        luaL_error(L, e.what());
    }
    lua_pushlstring(L, str.data(), str.length());
    return 1;
}

Но это очень уродливо и подвержено ошибкам, так как try/catch нужно будет добавить во многие места. Это можно сделать как макрос и обойти каждую команду, которая может выдать, но это не будет намного лучше.

Ответы [ 5 ]

8 голосов
/ 06 января 2011

Я нашел разумное решение. Вопрос в том, правильно ли это. Вместо экспорта (или вызова через lua_cpcall) исходная функция int PushHello(lua_State *L) обертка int SafeFunction<PushHello>(lua_State *L) экспортируется / вызывается Обёртка выглядит так:

template<lua_CFunction func>
int SafeFunction(lua_State *L){
    int result = 0;
    try{
        result = func(L);
    }
    // transform exception with description into lua_error
    catch(exception &e){
        luaL_error(L, e.what());
    }
    // rethrow lua error - C++ Lua throws lua_longjmp*
    catch(lua_longjmp*){
        throw;
    }
    // any other exception as lua_error with no description
    catch(...){
        luaL_error(L, "Unknown error");
    }

    return result;
}

Что вы думаете об этом? Вы видите какие-либо проблемы?

4 голосов
/ 25 ноября 2012

Juraj Blaho отличный ответ.Однако у него есть недостаток: для каждой функции, которую вы экспортируете с помощью int SafeFunction<PushHello>(lua_State *L), компилятор сгенерирует копию всего кода из шаблона, как если бы это был макрос.Когда экспортируется множество мелких функций, это будет пустой тратой.

Вы можете легко избежать этой проблемы, определив общую функцию static, выполняющую всю работу, а функция template просто вызывает эту общуюфункция:

static int SafeFunctionCommon(lua_State *L, lua_CFunction func){
    int result = 0;
    try{
        result = func(L);
    }
    // transform exception with description into lua_error
    catch(exception &e){
        luaL_error(L, e.what());
    }
    // rethrow lua error - C++ Lua throws lua_longjmp*
    catch(lua_longjmp*){
        throw;
    }
    // any other exception as lua_error with no description
    catch(...){
        luaL_error(L, "Unknown error");
    }

    return result;
}

template<lua_CFunction func>
int SafeFunction(lua_State *L){
    return SafeFunctionCommon(L, func);
}
1 голос
/ 06 января 2011

Lua не поймает исключение C ++.Если вы не перехватите его, он будет передан в стек вызовов, пока он не будет перехвачен каким-либо другим блоком кода или не вызовет сбой программы (необработанное исключение).Если функции, которые вы предоставляете Lua, вызывают функции, которые могут генерировать исключения, вы должны обработать их в этой функции.

1 голос
/ 06 января 2011

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

Также это дубликат Разматывание стека в C ++ при использовании Lua

Редактирование

Если вы беспокоитесь овызывается деструктор строки, почему бы не сделать это:

try
{
    string str("Hello");
    lua_pushlstring(L, str.data(), str.length());
}
catch (exception& e)
{
     luaL_error(L, e.what());
}

Я понимаю, что это тонкое изменение того, что вы предложили, но есть разница.Если выброшено исключение, что-либо в стеке в try{} будет уничтожено.Просто убедитесь, что все, что вы хотите уничтожить, находится в пределах этой попытки.

0 голосов
/ 06 января 2011

Если вы компилируете Lua как C ++, они будут использовать исключения C ++ в качестве ошибок, тогда как если вы компилируете как C, они будут использовать longjmp / setjmp. Это в основном означает, что нет ничего страшного в том, чтобы выдавать такое исключение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...