Rapptz / sol

A C++11 Lua wrapper
MIT License
209 stars 32 forks source link

error handling without exceptions #60

Open starwing opened 9 years ago

starwing commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

I have some ideas, the feature requires are:

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 commented 9 years ago

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 commented 9 years ago

@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 commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

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!