Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

error handling without exceptions #60

Open
starwing opened this issue May 9, 2015 · 7 comments
Open

error handling without exceptions #60

starwing opened this issue May 9, 2015 · 7 comments

Comments

@starwing
Copy link

starwing commented May 9, 2015

Hi, I found this library do not use lua_pcall. I prefer to call a lua function with a message handler to offer a stack trace in error. so a function like lua.where() and lua.error([msg before where]) is good. have some plans to support this?

@Rapptz
Copy link
Owner

Rapptz commented May 10, 2015

Hello.

Just to understand a little bit more, you mean instead of lua_call you want to use lua_pcall and allow you to use a message handler along with it? It sounds plausible but I'm not sure how to add it to the API sanely.

I had wanted to (unrelated to this issue slightly) allow users to set the lua_atpanic function to disable exceptions so that's on the roadmap sometime in the future too.

@starwing
Copy link
Author

I have some ideas, the feature requires are:

  • have stack trace when a Lua error occurs.
  • do not throw C++ exception when Lua error occurs (we don't use exceptions).

The first is simple, just use lua_pcall instead (and maybe add a luaL_traceback wrapper or just retrieve debug.traceback()). But I don't have mind about the second: what result the call() return? maybe a result_or_errmsg class or some better ones?

@ThePhD
Copy link
Contributor

ThePhD commented Jun 3, 2015

I can start on this by setting up a function pointer to be called on lua_atpanic. Since it's coupled to the lua_State, it can be passed into the constructor for sol::state like or set up using sol::state lua; lua.at_panic( my_function );. We'll allow for any C++ function that matches the signature, (placed into a std::function).

As a sidenote, I've never worked without exceptions. If you turn them off, what happens when you throw in code? Does the compiler demand you remove all instances of throw ? Or does it just insert some std::terminate-like code everywhere a library uses "throw"?

@ThePhD
Copy link
Contributor

ThePhD commented Jun 3, 2015

@starwing As a quick question, do you use -fno-exceptions or is it just your codebase policy to never try/catch and still have them on? I ask because this (http://stackoverflow.com/questions/7249378/disabling-c-exceptions-how-can-i-make-any-std-throw-immediately-terminate) says its a compile-time error for folks who use -fno-exceptions, and that'll be a bit harder to work around.

@ThePhD
Copy link
Contributor

ThePhD commented Oct 22, 2015

Preliminary support. I do not have the mechanism for having a "error message handler", but I believe I would add functionality to add that kind of trampoline to sol::function. Right now, there is no message handler but we do use pcall if you do not automatically convert the return type:

#include "sol.hpp"
#include <iostream>

int main( int argc, char * const argv[] ) {

    sol::state lua;

    // Some function; just using a lambda to be quick
    auto SOULS = []() { 
        throw std::exception( "dun goofed, little man" );
    };

    // Set the function
    lua.set_function( "doom", SOULS );

    sol::function luaSOULS = lua[ "doom" ];

    // can also do auto x = ...;
    sol::function_result x = luaSOULS();

    if ( x.valid() ) {
        // call succeeded
        std::cout << "Mmm, delicious success";
    }
    else {
        // call failed, USUALLY lua leaves a message string on top or something
        // Not always the case if you do something zany with the message handler, but we don't
        // have code in `sol` that allows for custom message handlers (yet)
        // This will also be the case if a regular std::exception (or derived) is thrown
        std::string errorstring = x;
        std::cout << "OH GOD SAVE US ALL!\n"
            << "The harbinger said: \"" << errorstring << "\"";
    }

}

Note that if you captured lua or got the lua_State* somehow, this example would also work if you just used luaL_error instead of throw std::exception(...).

@ThePhD
Copy link
Contributor

ThePhD commented Oct 22, 2015

Custom handling seems to work in #62. But, in order to make this work with handlers there's a wee bit of extra overhead (to set the special handler). Should probably use a little extra template magic to eliminate the runtime check for a handler if the user specifies that its a no-fail function.

#include "sol.hpp"
#include <iostream>

int main( int argc, char * const argv[] ) {

    sol::state lua;
    lua.open_libraries( sol::lib::base, sol::lib::debug );

    // Some function; just using a lambda to be cheap
    auto doom = []() {
        // Bypasses handler function: puts information directly into lua error
        throw std::exception( "dun goofed, little man" );
    };
    auto luadoom = [&lua]() {
        // Does not bypass error function, will call it
        lua_error( lua.lua_state() );
    };

    // Set the function
    lua.set_function( "doom", doom );
    lua.set_function( "luadoom", luadoom );
    lua.script(
        "function handler ( message )"
        "   return message .. debug.traceback()"
        "end" 
    );

    sol::function func = lua[ "doom" ];
    sol::function luafunc = lua[ "luadoom" ];
    sol::function luahandler = lua[ "handler" ];

    // Make sure handler works
    luahandler();
    // Set it

    // function_result pops the results when its destructor is called
    // so scope it appropriately!
    // If you don't scope it, runtime will have to remove arguments from middle
    // of the stack (which can (???, untested)) be costly
    sol::function_result result1 = func();
    if ( result1.valid() ) {
        // call succeeded
        std::cout << "result 1";
        // manipulate contents
    }
    else {
        // call failed, 
        // USUALLY lua leaves a message string on top or something
        std::string errorstring = result1;
        std::cout << "result1 error:\n" << errorstring << '\n';
    }

    // Appropriately scoped
    luafunc.error_handler = luahandler;
    sol::function_result result2 = luafunc();
    if ( result2.valid() ) {
        // call succeeded
        std::cout << "result2 success";
        // manipulate contents
    }
    else {
        // call failed, 
        // USUALLY lua leaves a message string on top or something
        std::string errorstring = result2;
        std::cout << "result2 error:\n" << errorstring << '\n';
    }
}

@starwing
Copy link
Author

starwing commented Nov 4, 2015

That's awesome! Sorry for late reply as I'm working hard on Lua scripts for Unity client :(

I will look into this but this is really awesome! Thank you for your really outstanding work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants