Skip to content

Commit

Permalink
Merge pull request #104 from AnonymousProgrammerLandau/enableUseOfExc…
Browse files Browse the repository at this point in the history
…eptions

Enable use of exceptions
  • Loading branch information
jeremyong committed Oct 5, 2015
2 parents 7395155 + 7595e57 commit 4e4a0c5
Show file tree
Hide file tree
Showing 10 changed files with 358 additions and 60 deletions.
16 changes: 15 additions & 1 deletion include/selene/BaseFun.h
@@ -1,8 +1,11 @@
#pragma once

#include "exotics.h"
#include <exception>
#include "exception.h"
#include <functional>
#include <tuple>
#include "util.h"

namespace sel {
struct BaseFun {
Expand All @@ -14,7 +17,18 @@ namespace detail {

inline int _lua_dispatcher(lua_State *l) {
BaseFun *fun = (BaseFun *)lua_touserdata(l, lua_upvalueindex(1));
return fun->Apply(l);
try {
return fun->Apply(l);
} catch (std::exception & e) {
lua_pushstring(l, e.what());
Traceback(l);
store_current_exception(l, lua_tostring(l, -1));
} catch (...) {
lua_pushliteral(l, "<Unknown exception>");
Traceback(l);
store_current_exception(l, lua_tostring(l, -1));
}
return lua_error(l);
}

template <typename Ret, typename... Args, std::size_t... N>
Expand Down
1 change: 0 additions & 1 deletion include/selene/Obj.h
Expand Up @@ -3,7 +3,6 @@
#include "ObjFun.h"
#include <functional>
#include <memory>
#include "State.h"
#include <string>
#include <utility>
#include <vector>
Expand Down
44 changes: 32 additions & 12 deletions include/selene/Selector.h
@@ -1,13 +1,13 @@
#pragma once

#include "exception.h"
#include "exotics.h"
#include <functional>
#include "Registry.h"
#include <string>
#include <tuple>
#include <vector>

#include "util.h"
#include <vector>

#ifdef HAS_REF_QUALIFIERS
# undef HAS_REF_QUALIFIERS
Expand Down Expand Up @@ -42,6 +42,7 @@ class Selector {
private:
lua_State *_state;
Registry &_registry;
ExceptionHandler *_exception_handler;
std::string _name;
using Fun = std::function<void()>;
using PFun = std::function<void(Fun)>;
Expand All @@ -60,13 +61,13 @@ class Selector {
using Functor = std::function<void(int)>;
mutable Functor _functor;

Selector(lua_State *s, Registry &r, const std::string &name,
Selector(lua_State *s, Registry &r, ExceptionHandler &eh, const std::string &name,
std::vector<Fun> traversal, Fun get, PFun put)
: _state(s), _registry(r), _name(name), _traversal(traversal),
: _state(s), _registry(r), _exception_handler(&eh), _name(name), _traversal(traversal),
_get(get), _put(put) {}

Selector(lua_State *s, Registry &r, const std::string& name)
: _state(s), _registry(r), _name(name) {
Selector(lua_State *s, Registry &r, ExceptionHandler &eh, const std::string& name)
: _state(s), _registry(r), _exception_handler(&eh), _name(name) {
const auto state = _state; // gcc-5.1 doesn't support implicit member capturing
// `name' is passed by value because lambda's lifetime may be longer than lifetime of `name'
_get = [state, name]() {
Expand Down Expand Up @@ -102,6 +103,7 @@ class Selector {
Selector(const Selector &other)
: _state(other._state),
_registry(other._registry),
_exception_handler(other._exception_handler),
_name(other._name),
_traversal(other._traversal),
_get(other._get),
Expand All @@ -112,6 +114,7 @@ class Selector {
Selector(Selector&& other)
: _state(other._state),
_registry(other._registry),
_exception_handler(other._exception_handler),
_name(other._name),
_traversal(other._traversal),
_get(other._get),
Expand All @@ -120,12 +123,22 @@ class Selector {
other._functor = nullptr;
}

~Selector() {
~Selector() noexcept(false) {
// If there is a functor is not empty, execute it and collect no args
if (_functor) {
_traverse();
_get();
_functor(0);
if (std::uncaught_exception())
{
try {
_functor(0);
} catch (...) {
// We are already unwinding, ignore further exceptions.
// As of C++17 consider std::uncaught_exceptions()
}
} else {
_functor(0);
}
}
lua_settop(_state, 0);
}
Expand All @@ -139,7 +152,8 @@ class Selector {
constexpr int num_args = sizeof...(Args);
Selector copy{*this};
const auto state = _state; // gcc-5.1 doesn't support implicit member capturing
copy._functor = [state, tuple_args, num_args](int num_ret) {
const auto eh = _exception_handler;
copy._functor = [state, eh, tuple_args, num_args](int num_ret) {
// install handler, and swap(handler, function) on lua stack
int handler_index = SetErrorHandler(state);
int func_index = handler_index - 1;
Expand All @@ -155,10 +169,15 @@ class Selector {
#endif
// call lua function with error handler
detail::_push(state, tuple_args);
lua_pcall(state, num_args, num_ret, handler_index - 1);
auto const statusCode =
lua_pcall(state, num_args, num_ret, handler_index - 1);

// remove error handler
lua_remove(state, handler_index - 1);

if (statusCode != LUA_OK) {
eh->Handle_top_of_stack(statusCode, state);
}
};
return copy;
}
Expand Down Expand Up @@ -373,6 +392,7 @@ class Selector {
}
auto ret = detail::_pop(detail::_id<sel::function<R(Args...)>>{},
_state);
ret._enable_exception_handler(_exception_handler);
lua_settop(_state, 0);
return ret;
}
Expand Down Expand Up @@ -432,7 +452,7 @@ class Selector {
lua_setfield(state, -2, name.c_str());
lua_pop(state, 1);
};
return Selector{_state, _registry, n, traversal, get, put};
return Selector{_state, _registry, *_exception_handler, n, traversal, get, put};
}
Selector operator[](const char* name) const REF_QUAL_LVALUE {
return (*this)[std::string{name}];
Expand All @@ -453,7 +473,7 @@ class Selector {
lua_settable(state, -3);
lua_pop(state, 1);
};
return Selector{_state, _registry, name, traversal, get, put};
return Selector{_state, _registry, *_exception_handler, name, traversal, get, put};
}

friend bool operator==(const Selector &, const char *);
Expand Down
34 changes: 25 additions & 9 deletions include/selene/State.h
@@ -1,5 +1,6 @@
#pragma once

#include "exception.h"
#include <iostream>
#include <memory>
#include <string>
Expand All @@ -15,17 +16,20 @@ class State {
lua_State *_l;
bool _l_owner;
std::unique_ptr<Registry> _registry;
std::unique_ptr<ExceptionHandler> _exception_handler;

public:
State() : State(false) {}
State(bool should_open_libs) : _l(nullptr), _l_owner(true) {
State(bool should_open_libs) : _l(nullptr), _l_owner(true), _exception_handler(new ExceptionHandler) {
_l = luaL_newstate();
if (_l == nullptr) throw 0;
if (should_open_libs) luaL_openlibs(_l);
_registry.reset(new Registry(_l));
HandleExceptionsPrintingToStdOut();
}
State(lua_State *l) : _l(l), _l_owner(false) {
State(lua_State *l) : _l(l), _l_owner(false), _exception_handler(new ExceptionHandler) {
_registry.reset(new Registry(_l));
HandleExceptionsPrintingToStdOut();
}
State(const State &other) = delete;
State &operator=(const State &other) = delete;
Expand Down Expand Up @@ -58,25 +62,29 @@ class State {
bool Load(const std::string &file) {
int status = luaL_loadfile(_l, file.c_str());
#if LUA_VERSION_NUM >= 502
if (status != LUA_OK) {
auto const lua_ok = LUA_OK;
#else
if (status != 0) {
auto const lua_ok = 0;
#endif
if (status != lua_ok) {
if (status == LUA_ERRSYNTAX) {
const char *msg = lua_tostring(_l, -1);
_print(msg ? msg : (file + ": syntax error").c_str());
_exception_handler->Handle(status, msg ? msg : file + ": syntax error");
} else if (status == LUA_ERRFILE) {
const char *msg = lua_tostring(_l, -1);
_print(msg ? msg : (file + ": file error").c_str());
_exception_handler->Handle(status, msg ? msg : file + ": file error");
}
lua_remove(_l , -1);
return false;
}
if (!lua_pcall(_l, 0, LUA_MULTRET, 0))

status = lua_pcall(_l, 0, LUA_MULTRET, 0);
if(status == lua_ok) {
return true;
}

const char *msg = lua_tostring(_l, -1);
_print(msg ? msg : (file + ": dofile failed").c_str());
_exception_handler->Handle(status, msg ? msg : file + ": dofile failed");
lua_remove(_l, -1);
return false;
}
Expand All @@ -91,6 +99,14 @@ class State {
#endif
}

void HandleExceptionsPrintingToStdOut() {
*_exception_handler = ExceptionHandler([](int, std::string msg, std::exception_ptr){_print(msg);});
}

void HandleExceptionsWith(ExceptionHandler::function handler) {
*_exception_handler = ExceptionHandler(std::move(handler));
}

void Push() {} // Base case

template <typename T, typename... Ts>
Expand All @@ -114,7 +130,7 @@ class State {
}
public:
Selector operator[](const char *name) {
return Selector(_l, *_registry, name);
return Selector(_l, *_registry, *_exception_handler, name);
}

bool operator()(const char *code) {
Expand Down
118 changes: 118 additions & 0 deletions include/selene/exception.h
@@ -0,0 +1,118 @@
#pragma once
#include <functional>
#include "primitives.h"
#include <string>

extern "C" {
#include <lua.h>
#include <lauxlib.h>
}

namespace sel {

struct stored_exception {
std::string what;
std::exception_ptr exception;
};

inline std::string const * _stored_exception_metatable_name() {
static std::string const name = "selene_stored_exception";
return &name;
}

inline int _delete_stored_exception(lua_State * l) {
void * user_data = lua_touserdata(l, -1);
static_cast<stored_exception *>(user_data)->~stored_exception();
return 0;
}

inline int _push_stored_exceptions_what(lua_State * l) {
void * user_data = lua_touserdata(l, -1);
std::string const & what = static_cast<stored_exception *>(user_data)->what;
detail::_push(l, what);
return 1;
}

inline void _register_stored_exception_metatable(lua_State * l) {
luaL_newmetatable(l, _stored_exception_metatable_name()->c_str());
lua_pushcfunction(l, _delete_stored_exception);
lua_setfield(l, -2, "__gc");
lua_pushcclosure(l, _push_stored_exceptions_what, 0);
lua_setfield(l, -2, "__tostring");
}

inline void store_current_exception(lua_State * l, char const * what) {
void * user_data = lua_newuserdata(l, sizeof(stored_exception));
new(user_data) stored_exception{what, std::current_exception()};

luaL_getmetatable(l, _stored_exception_metatable_name()->c_str());
if(lua_isnil(l, -1)) {
lua_settop(l, -2);
_register_stored_exception_metatable(l);
}

lua_setmetatable(l, -2);
}

inline stored_exception * test_stored_exception(lua_State *l) {
if(lua_isuserdata(l, -1)) {
void * user_data = luaL_testudata(l, -1, _stored_exception_metatable_name()->c_str());
if(user_data != nullptr) {
return static_cast<stored_exception *>(user_data);
}
}
return nullptr;
}

inline bool push_stored_exceptions_what(lua_State * l) {
stored_exception * stored = test_stored_exception(l);
if(stored != nullptr) {
detail::_push(l, static_cast<const std::string &>(stored->what));
return true;
}
return false;
}

inline std::exception_ptr extract_stored_exception(lua_State *l) {
stored_exception * stored = test_stored_exception(l);
if(stored != nullptr) {
return stored->exception;
}
return nullptr;
}

class ExceptionHandler {
public:
using function = std::function<void(int,std::string,std::exception_ptr)>;

private:
function _handler;

public:
ExceptionHandler() = default;

explicit ExceptionHandler(function && handler) : _handler(handler) {}

void Handle(int luaStatusCode, std::string message, std::exception_ptr exception = nullptr) {
if(_handler) {
_handler(luaStatusCode, std::move(message), std::move(exception));
}
}

void Handle_top_of_stack(int luaStatusCode, lua_State *L) {
stored_exception * stored = test_stored_exception(L);
if(stored) {
Handle(
luaStatusCode,
stored->what,
stored->exception);
} else {
Handle(
luaStatusCode,
detail::_pop(detail::_id<std::string>(), L));
}
}

};

}

0 comments on commit 4e4a0c5

Please sign in to comment.