add a new kind of error handling script call, updating docs and examples

This commit is contained in:
ThePhD 2017-03-31 17:38:04 -04:00
parent 1e367ab80c
commit 59b196a1db
8 changed files with 301 additions and 15 deletions

View File

@ -58,9 +58,34 @@ This function takes a number of :ref:`sol::lib<lib-enum>` as arguments and opens
sol::function_result script(const std::string& code);
sol::function_result script_file(const std::string& filename);
template <typename Func>
sol::protected_function_result script(const std::string& code, Func&& func);
template <typename Func>
sol::protected_function_result script_file(const std::string& filename, Func&& func);
These functions run the desired blob of either code that is in a string, or code that comes from a filename, on the ``lua_State*``. It will not run isolated: any scripts or code run will affect code in the ``lua_State*`` the object uses as well (unless ``local`` is applied to a variable declaration, as specified by the Lua language). Code ran in this fashion is not isolated. If you need isolation, consider creating a new state or traditional Lua sandboxing techniques.
If your script returns a value, you can capture it from the returned :ref:`function_result<function-result>`.
If your script returns a value, you can capture it from the returned :ref:`sol::function_result<function-result>`/:ref:`sol::protected_function_result<protected-function-result>`.
To handle errors when using the second overload, provide a callable function/object that takes a ``lua_State*`` as its first argument and a ``sol::protected_function_result`` as its second argument. Then, handle the errors any way you like:
.. code-block:: cpp
:caption: running code safely
:name: state-script-safe
int main () {
sol::state lua;
// the default handler panics or throws, depending on your settings
auto result1 = lua.script("bad.code", &sol::default_on_error);
auto result2 = lua.script("123 bad.code", [](lua_State* L, sol::protected_function_result pfr) {
// pfr will contain things that went wrong, for either loading or executing the script
// the user can do whatever they like here, including throw. Otherwise,
// they need to return the protected_function_result
// You can also just return it, and let the call-site handle the error if necessary.
return pfr;
});
}
.. code-block:: cpp
:caption: function: require / require_file

View File

@ -5,6 +5,10 @@ how to handle exceptions or other errors
Here is some advice and some tricks for common errors about iteration, compile time / linker errors, and other pitfalls, especially when dealing with thrown exceptions, error conditions and the like in Sol.
Running Scripts
---------------
Scripts can have syntax errors, can load from the file system wrong, or have runtime issues. Knowing which one can be troublesome. There are various small building blocks to load and run code, but to check errors you can use the overloaded :ref:`script/script_file functions on sol::state/sol::state_view<state-script-function>`
Linker Errors
-------------

View File

@ -47,6 +47,23 @@ running lua code
int value = lua.script("return 54");
// value == 54
To run Lua code but have an error handler in case things go wrong:
.. code-block:: cpp
sol::state lua;
// the default handler panics or throws, depending on your settings
auto result1 = lua.script("bad.code", &sol::default_on_error);
auto result2 = lua.script("123 herp.derp", [](lua_State* L, sol::protected_function_result pfr) {
// pfr will contain things that went wrong, for either loading or executing the script
// Can throw your own custom error
// You can also just return it, and let the call-site handle the error if necessary.
return pfr;
});
To check the success of a loading operation:
.. code-block:: cpp
@ -54,16 +71,24 @@ To check the success of a loading operation:
// load file without execute
sol::load_result script1 = lua.load_file("path/to/luascript.lua");
script1(); //execute
// load string without execute
sol::load_result script2 = lua.load("a = 'test'");
script2(); //execute
sol::protected_function_result script2result = script2(); //execute
// optionally, check if it worked
if (script2result.valid()) {
// yay!
}
else {
// aww
}
sol::load_result script3 = lua.load("return 24");
int value2 = script3(); // execute, get return value
// value2 == 24
To check whether a script was successfully run or not (after loading is assumed to be successful):
To check whether a script was successfully run or not (if the actual loading is successful):
.. code-block:: cpp

View File

@ -38,7 +38,9 @@ Or using your favorite IDE / tool after setting up your include paths and librar
If you get an avalanche of errors (particularly referring to ``auto``), you may not have enabled C++14 / C++17 mode for your compiler. Add one of ``std=c++14``, ``std=c++1z`` OR ``std=c++1y`` to your compiler options. By default, this is always-on for VC++ compilers in Visual Studio and friends, but g++ and clang++ require a flag (unless you're on `GCC 6.0`_).
If this works, you're ready to start! The first line creates the ``lua_State`` and will hold onto it for the duration of the scope its declared in (e.g., from the opening ``{`` to the closing ``}``). It will automatically close / cleanup that lua state when it gets destructed. The second line opens a single lua-provided library, "base". There are several other libraries that come with lua that you can open by default, and those are included in the :ref:`sol::lib<lib-enum>` enumeration. You can open multiple base libraries by specifying multiple ``sol::lib`` arguments:
If this works, you're ready to start! The first line creates the ``lua_State`` and will hold onto it for the duration of the scope its declared in (e.g., from the opening ``{`` to the closing ``}``). It will automatically close / cleanup that lua state when it gets destructed.
The second line opens a single lua-provided library, "base". There are several other libraries that come with lua that you can open by default, and those are included in the :ref:`sol::lib<lib-enum>` enumeration. You can open multiple base libraries by specifying multiple ``sol::lib`` arguments:
.. code-block:: cpp
:linenos:
@ -59,6 +61,8 @@ If this works, you're ready to start! The first line creates the ``lua_State`` a
If you're interested in integrating Sol with a project that already uses some other library or Lua in the codebase, check out the :doc:`existing example<existing>` to see how to work with Sol when you add it to a project (the existing example covers ``require`` as well)!
Some more ways of loading scripts and handling errors is shown `in this example`_!
Next, let's start :doc:`reading/writing some variables<variables>` from Lua into C++, and vice-versa!
@ -77,3 +81,5 @@ Next, let's start :doc:`reading/writing some variables<variables>` from Lua into
.. _github repository here: https://github.com/ThePhD/sol2
.. _Lua page on getting started: https://www.lua.org/start.html
.. _in this example: https://github.com/ThePhD/sol2/blob/develop/examples/basic.cpp

View File

@ -3,17 +3,39 @@
#include <iostream>
int main() {
// create an empty lua state
std::cout << "=== basic example ===" << std::endl;
// create an empty lua state
sol::state lua;
// by default, libraries are not opened
// you can open libraries by using open_libraries
// the libraries reside in the sol::lib enum class
lua.open_libraries(sol::lib::base);
// you can open all libraries by passing no arguments
//lua.open_libraries();
// call lua code directly
std::cout << "=== basic example ===" << std::endl;
lua.script("print('hello world')");
// call lua code, and check to make sure it has loaded and run properly:
auto handler = &sol::default_on_error;
lua.script("print('hello again, world')", handler);
// Use a custom error handler if you need it
// This gets called when the result is bad
auto simple_handler = [](lua_State*, sol::protected_function_result result) {
// You can just pass it through to let the call-site handle it
return result;
};
auto result = lua.script("print('hello hello again, world') \n return 24", simple_handler);
if (result.valid()) {
std::cout << "the code worked, and a double-hello statement should appear above this one!" << std::endl;
int value = result;
assert(value == 24);
}
else {
std::cout << "the code failed, check the result type for more information!" << std::endl;
}
std::cout << std::endl;
}

View File

@ -20,8 +20,8 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// This file was generated with a script.
// Generated 2017-03-30 18:11:26.562402 UTC
// This header was generated with sol v2.16.0 (revision 92d31b3)
// Generated 2017-03-31 21:37:49.518087 UTC
// This header was generated with sol v2.16.0 (revision 1e367ab)
// https://github.com/ThePhD/sol2
#ifndef SOL_SINGLE_INCLUDE_HPP
@ -3324,7 +3324,9 @@ namespace sol {
runtime = LUA_ERRRUN,
memory = LUA_ERRMEM,
handler = LUA_ERRERR,
gc = LUA_ERRGCMM
gc = LUA_ERRGCMM,
syntax = LUA_ERRSYNTAX,
file = LUA_ERRFILE,
};
enum class thread_status : int {
@ -3363,6 +3365,61 @@ namespace sol {
table | boolean | function | userdata | lightuserdata
};
inline const std::string& to_string(call_status c) {
static const std::array<std::string, 8> names{{
"ok",
"yielded",
"runtime",
"memory",
"handler",
"gc",
"syntax",
"file",
}};
switch (c) {
case call_status::ok:
return names[0];
case call_status::yielded:
return names[1];
case call_status::runtime:
return names[2];
case call_status::memory:
return names[3];
case call_status::handler:
return names[4];
case call_status::gc:
return names[5];
case call_status::syntax:
return names[6];
case call_status::file:
return names[7];
}
return names[0];
}
inline const std::string& to_string(load_status c) {
static const std::array<std::string, 8> names{ {
"ok",
"memory",
"gc",
"syntax",
"file",
} };
switch (c) {
case load_status::ok:
return names[0];
case load_status::memory:
return names[1];
case load_status::gc:
return names[2];
case load_status::syntax:
return names[3];
case load_status::file:
return names[4];
}
return names[0];
}
enum class meta_function {
construct,
index,
@ -12674,6 +12731,25 @@ namespace sol {
return kb;
}
inline protected_function_result default_on_error( lua_State* L, const protected_function_result& pfr ) {
type t = type_of(L, pfr.stack_index());
std::string err = to_string(pfr.status()) + " error";
if (t == type::string) {
err += " ";
err += stack::get<std::string>(L, pfr.stack_index());
}
#ifdef SOL_NO_EXCEPTIONS
if (t != type::nil) {
lua_pop(L, 1);
}
stack::push(L, err);
lua_error(L);
#else
throw error(detail::direct_error, err);
#endif
return pfr;
}
class state_view {
private:
lua_State* L;
@ -12856,15 +12932,41 @@ namespace sol {
}
protected_function_result do_string(const std::string& code) {
sol::protected_function pf = load(code);
load_status x = static_cast<load_status>(luaL_loadstring(L, code.c_str()));
if (x != load_status::ok) {
return protected_function_result(L, -1, 0, 1, static_cast<call_status>(x));
}
protected_function pf(L, -1);
return pf();
}
protected_function_result do_file(const std::string& filename) {
sol::protected_function pf = load_file(filename);
load_status x = static_cast<load_status>(luaL_loadfile(L, filename.c_str()));
if (x != load_status::ok) {
return protected_function_result(L, -1, 0, 1, static_cast<call_status>(x));
}
protected_function pf(L, -1);
return pf();
}
template <typename Fx>
protected_function_result script(const std::string& code, Fx&& on_error) {
protected_function_result pfr = do_string(code);
if (!pfr.valid()) {
return on_error(L, pfr);
}
return pfr;
}
template <typename Fx>
protected_function_result script_file(const std::string& filename, Fx&& on_error) {
protected_function_result pfr = do_file(filename);
if (!pfr.valid()) {
return on_error(L, pfr);
}
return pfr;
}
function_result script(const std::string& code) {
int index = lua_gettop(L);
stack::script(L, code);

View File

@ -52,6 +52,25 @@ namespace sol {
return kb;
}
inline protected_function_result default_on_error( lua_State* L, const protected_function_result& pfr ) {
type t = type_of(L, pfr.stack_index());
std::string err = to_string(pfr.status()) + " error";
if (t == type::string) {
err += " ";
err += stack::get<std::string>(L, pfr.stack_index());
}
#ifdef SOL_NO_EXCEPTIONS
if (t != type::nil) {
lua_pop(L, 1);
}
stack::push(L, err);
lua_error(L);
#else
throw error(detail::direct_error, err);
#endif
return pfr;
}
class state_view {
private:
lua_State* L;
@ -234,15 +253,41 @@ namespace sol {
}
protected_function_result do_string(const std::string& code) {
sol::protected_function pf = load(code);
load_status x = static_cast<load_status>(luaL_loadstring(L, code.c_str()));
if (x != load_status::ok) {
return protected_function_result(L, -1, 0, 1, static_cast<call_status>(x));
}
protected_function pf(L, -1);
return pf();
}
protected_function_result do_file(const std::string& filename) {
sol::protected_function pf = load_file(filename);
load_status x = static_cast<load_status>(luaL_loadfile(L, filename.c_str()));
if (x != load_status::ok) {
return protected_function_result(L, -1, 0, 1, static_cast<call_status>(x));
}
protected_function pf(L, -1);
return pf();
}
template <typename Fx>
protected_function_result script(const std::string& code, Fx&& on_error) {
protected_function_result pfr = do_string(code);
if (!pfr.valid()) {
return on_error(L, pfr);
}
return pfr;
}
template <typename Fx>
protected_function_result script_file(const std::string& filename, Fx&& on_error) {
protected_function_result pfr = do_file(filename);
if (!pfr.valid()) {
return on_error(L, pfr);
}
return pfr;
}
function_result script(const std::string& code) {
int index = lua_gettop(L);
stack::script(L, code);

View File

@ -345,7 +345,9 @@ namespace sol {
runtime = LUA_ERRRUN,
memory = LUA_ERRMEM,
handler = LUA_ERRERR,
gc = LUA_ERRGCMM
gc = LUA_ERRGCMM,
syntax = LUA_ERRSYNTAX,
file = LUA_ERRFILE,
};
enum class thread_status : int {
@ -384,6 +386,61 @@ namespace sol {
table | boolean | function | userdata | lightuserdata
};
inline const std::string& to_string(call_status c) {
static const std::array<std::string, 8> names{{
"ok",
"yielded",
"runtime",
"memory",
"handler",
"gc",
"syntax",
"file",
}};
switch (c) {
case call_status::ok:
return names[0];
case call_status::yielded:
return names[1];
case call_status::runtime:
return names[2];
case call_status::memory:
return names[3];
case call_status::handler:
return names[4];
case call_status::gc:
return names[5];
case call_status::syntax:
return names[6];
case call_status::file:
return names[7];
}
return names[0];
}
inline const std::string& to_string(load_status c) {
static const std::array<std::string, 8> names{ {
"ok",
"memory",
"gc",
"syntax",
"file",
} };
switch (c) {
case load_status::ok:
return names[0];
case load_status::memory:
return names[1];
case load_status::gc:
return names[2];
case load_status::syntax:
return names[3];
case load_status::file:
return names[4];
}
return names[0];
}
enum class meta_function {
construct,
index,