diff --git a/README.md b/README.md index 7fb33d3d..52ceb662 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Sol 2.17 +## Sol 2.18 [![Join the chat at https://gitter.im/chat-sol2/Lobby](https://badges.gitter.im/chat-sol2/Lobby.svg)](https://gitter.im/chat-sol2/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) diff --git a/docs/source/api/stack_reference.rst b/docs/source/api/stack_reference.rst index cb2ddbfe..1527cace 100644 --- a/docs/source/api/stack_reference.rst +++ b/docs/source/api/stack_reference.rst @@ -13,4 +13,48 @@ All of the base types have ``stack`` versions of themselves, and the APIs are id stack_aligned_function ---------------------- -This type is particular to working with the stack. It does not push the function object on the stack before pushing the arguments, assuming that the function present is already on the stack before going ahead and invoking the function it is targeted at. It is identical to :doc:`sol::function` and has a protected counterpart as well. If you are working with the stack and know there is a callable object in the right place (i.e., at the top of the Lua stack), use this abstraction to work with the very top of the stack and have it call your function while still having the easy-to-use Lua abstractions on top. +This type is particular to working with the stack. It does not push the function object on the stack before pushing the arguments, assuming that the function present is already on the stack before going ahead and invoking the function it is targeted at. It is identical to :doc:`sol::function` and has a protected counterpart as well. If you are working with the stack and know there is a callable object in the right place (i.e., at the top of the Lua stack), use this abstraction to have it call your stack-based function while still having the easy-to-use Lua abstractions. + +Furthermore, if you know you have a function in the right place alongside proper arguments on top of it, you can use the ``sol::stack_count`` structure and give its constructor the number of arguments off the top that you want to call your pre-prepared function with: + +.. code-block:: cpp + :caption: stack_aligned_function.cpp + :linenos: + :name: stack-top-example + + #define SOL_CHECK_ARGUMENTS 1 + #include + + #include + + int main (int, char*[]) { + sol::state lua; + lua.set_function("func", [](int a, int b) { return (a + b) * 2;}); + + sol::reference func_ref = lua["func"]; + lua_State* L = lua.lua_state(); + + // for some reason, you need to use the low-level API + func_ref.push(); // function on stack now + + // maybe this is in a lua_CFunction you bind, + // or maybe you're trying to work with a pre-existing system + sol::stack_aligned_function func(L, -1); + lua_pushinteger(L, 5); // argument 1 + lua_pushinteger(L, 6); // argument 2 + // take 2 arguments from the top, + // and use "stack_aligned_function" to call + int result = func(sol::stack_count(2)); + + // make sure everything is clean + assert(result == 22); + assert(lua.stack_top() == 0); // stack is empty/balanced + + return 0; + } + +Finally, there is a special abstraction that provides further stack optimizations for ``sol::protected_function`` variants that are aligned, and it is called ``sol::stack_aligned_stack_handler_protected_function``. This typedef expects you to pass a ``stack_reference`` handler to its constructor, meaning that you have already placed an appropriate error-handling function somewhere on the stack before the aligned function. You can use ``sol::stack_count`` with this type as well, + +.. warning:: + + Do not use ``sol::stack_top`` with a ``sol::stack_aligned_protected_function``. The default behavior checks if the ``error_handler`` member variable is valid, and attempts to push the handler onto the stack in preparation for calling the function. This inevitably changes the stack. Only use ``sol::stack_aligned_protected_function`` if you know that the handler is not valid (``nil``), or if you use ``sol::stack_aligned_stack_handler_protected_function``, which references an existing stack index that can be before the precise placement of the function and its arguments. diff --git a/docs/source/api/state.rst b/docs/source/api/state.rst index 0e312510..72ee9eb6 100644 --- a/docs/source/api/state.rst +++ b/docs/source/api/state.rst @@ -52,26 +52,28 @@ members This function takes a number of :ref:`sol::lib` as arguments and opens up the associated Lua core libraries. .. code-block:: cpp - :caption: function: script / script_file + :caption: function: script / safe_script / script_file / safe_script_file / unsafe_script / unsafe_script_file :name: state-script-function - sol::function_result script(const std::string& code); - sol::protected_function_result script(const std::string& code, const environment& env); + function_result script(const string_view& code, const std::string& chunk_name = "[string]", load_mode mode = load_mode::any); + protected_function_result script(const string_view& code, const environment& env, const std::string& chunk_name = "[string]", load_mode mode = load_mode::any); template - sol::protected_function_result script(const std::string& code, ErrorFunc&& on_error); + protected_function_result script(const string_view& code, ErrorFunc&& on_error, const std::string& chunk_name = "[string]", load_mode mode = load_mode::any); template - sol::protected_function_result script(const std::string& code, const environment& env, ErrorFunc&& on_error); + protected_function_result script(const string_view& code, const environment& env, ErrorFunc&& on_error, const std::string& chunk_name = "[string]", load_mode mode = load_mode::any); - sol::function_result script_file(const std::string& filename); - sol::protected_function_result script_file(const std::string& filename, const environment& env); + function_result script_file(const std::string& filename, load_mode mode = load_mode::any); + protected_function_result script_file(const std::string& filename, const environment& env, load_mode mode = load_mode::any); template - sol::protected_function_result script_file(const std::string& filename, ErrorFunc&& on_error); + protected_function_result script_file(const std::string& filename, ErrorFunc&& on_error, load_mode mode = load_mode::any); template - sol::protected_function_result script_file(const std::string& filename, const environment& env, ErrorFunc&& on_error); + protected_function_result script_file(const std::string& filename, const environment& env, ErrorFunc&& on_error, load_mode mode = load_mode::any); + +If you need safety, please use the version of these functions with ``safe_script(_file)`` appended in front of them. They will always check for errors and always return a ``sol::protected_function_result``. If you explicitly do not want to check for errors and want to simply invoke ``lua_error`` in the case of errors, use ``unsafe_script(_file)`` versions. 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:`sol::function_result`/:ref:`sol::protected_function_result`. Note that the plain versions that do not take an environment or a callback function assume that the contents internally not only loaded properly but ran to completion without errors, for the sake of simplicity. +If your script returns a value, you can capture it from the returned :ref:`sol::function_result`/:ref:`sol::protected_function_result`. Note that the plain versions that do not take an environment or a callback function assume that the contents internally not only loaded properly but ran to completion without errors, for the sake of simplicity and performance. 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: @@ -82,8 +84,8 @@ To handle errors when using the second overload, provide a callable function/obj 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) { + auto result1 = lua.safe_script("bad.code"); + auto result2 = lua.safe_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 @@ -117,15 +119,17 @@ Thanks to `Eric (EToreo) for the suggestion on this one`_! These functions *load* the desired blob of either code that is in a string, or code that comes from a filename, on the ``lua_State*``. That blob will be turned into a Lua Function. It will not be run: it returns a ``load_result`` proxy that can be called to actually run the code, when you are ready. It can also be turned into a ``sol::function``, a ``sol::protected_function``, or some other abstraction that can serve to call the function. If it is called, it will run on the object's current ``lua_State*``: it is not isolated. If you need isolation, consider using :doc:`sol::environment`, creating a new state, or other Lua sandboxing techniques. +Finally, if you have a custom source of data, you can use the ``lua_Reader`` overloaded function alongside passing in a ``void*`` pointing to a single type that has everything you need to run it. Use that callback to provide data to the underlying Lua implementation to read data, as explained `in the Lua manual`_. + This is a low-level function and if you do not understand the difference between loading a piece of code versus running that code, you should be using :ref:`state_view::script`. .. code-block:: cpp :caption: function: do_string / do_file :name: state-do-code - sol::protected_function_result do_string(const std::string& code); + sol::protected_function_result do_string(const string_view& code); sol::protected_function_result do_file(const std::string& filename); - sol::protected_function_result do_string(const std::string& code, sol::environment env); + sol::protected_function_result do_string(const string_view& code, sol::environment env); sol::protected_function_result do_file(const std::string& filename, sol::environment env); These functions *loads and performs* the desired blob of either code that is in a string, or code that comes from a filename, on the ``lua_State*``. It *will* run, and then return a ``protected_function_result`` proxy that can be examined for either an error or the return value. This function does not provide a callback like :ref:`state_view::script` does. It is a lower-level function that performs less checking and directly calls ``load(_file)`` before running the result, with the optional environment. @@ -187,4 +191,5 @@ Creates a table. Forwards its arguments to :ref:`table::create`. A .. _standard lua libraries: http://www.lua.org/manual/5.3/manual.html#6 .. _luaL_requiref: https://www.lua.org/manual/5.3/manual.html#luaL_requiref -.. _Eric (EToreo) for the suggestion on this one: https://github.com/ThePhD/sol2/issues/90 \ No newline at end of file +.. _Eric (EToreo) for the suggestion on this one: https://github.com/ThePhD/sol2/issues/90 +.. _in the Lua manual: https://www.lua.org/manual/5.3/manual.html#lua_Reader diff --git a/docs/source/conf.py b/docs/source/conf.py index 250fe906..3906305b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -51,7 +51,7 @@ master_doc = 'index' # General information about the project. project = 'Sol' -copyright = '2016, ThePhD' +copyright = '2017, ThePhD' author = 'ThePhD' # The version info for the project you're documenting, acts as replacement for @@ -59,9 +59,9 @@ author = 'ThePhD' # built documents. # # The short X.Y version. -version = '2.17' +version = '2.18' # The full version, including alpha/beta/rc tags. -release = '2.17.5' +release = '2.18.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/containers.rst b/docs/source/containers.rst index f94bd4e4..a4cc4021 100644 --- a/docs/source/containers.rst +++ b/docs/source/containers.rst @@ -1,8 +1,18 @@ containers ========== +*working with containers in sol2* Containers are objects that are meant to be inspected and iterated and whose job is to typically provide storage to a collection of items. The ``std::`` library has several containers of varying types, and all of them have ``begin()`` and ``end()`` function which return iterators. C-style arrays are also containers, and sol2 will detect all of them for use and bestow them with special properties and functions. +* Containers from C++ are stored as ``userdata`` with special ``usertype`` metatables with :ref:`special operations` +* Containers can be manipulated from C++ and Lua and, like userdata, will `reflect changes if you use a reference`_ to the data. +* This means containers **do not automatically serialize as Lua tables** + - If you need tables, consider using ``sol::as_table`` and ``sol::nested`` + - See `this table serialization example`_ for more details +* Lua 5.1 has different semantics for ``pairs`` and ``ipairs``: see the example for plain containers in the :doc:`api documentation page for containers` +* You can override container behavior by overriding :ref:`the detection trait` and :ref:`specializing the container_traits template` +* You can bind typical C-style arrays, but must follow :ref:`the rules` + .. _container-c-array: .. note:: @@ -40,6 +50,8 @@ You can also specialize ``sol::is_container`` to turn off container detection This will let the type be pushed as a regular userdata. +.. _container-traits: + container overriding -------------------- @@ -71,6 +83,7 @@ The various operations provided by ``container_traits`` are expected to be li Exception handling **WILL** be provided around these particular raw C functions, so you do not need to worry about exceptions or errors bubbling through and handling that part. It is specifically handled for you in this specific instance, and **ONLY** in this specific instance. The raw note still applies to every other raw C function you make manually. +.. _container-operations:: container operations ------------------------- @@ -167,3 +180,5 @@ When you serialize a container into sol2, the default container handler deals wi .. _online version's information: https://www.lua.org/pil/26.html +.. _reflect changes if you use a reference: https://github.com/ThePhD/sol2/blob/develop/examples/containers.cpp +.. _this table serialization example: https://github.com/ThePhD/sol2/blob/develop/examples/containers_as_table.cpp diff --git a/examples/lua_inheritance.cpp b/examples/lua_inheritance.cpp new file mode 100644 index 00000000..bc87bc37 --- /dev/null +++ b/examples/lua_inheritance.cpp @@ -0,0 +1,41 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include +#include + +struct cpp_class { + int value = 0; +}; + +int main(int, char*[]) { + std::cout << "=== runtime_additions example ===" << std::endl; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_usertype("object"); + + // runtime additions: through the sol API + lua["object"]["func"] = [](cpp_class& o) { + ++o.value; + return o.value; + }; + // runtime additions: through a lua script + lua.script(R"( +function object:print () + print(self:func()) +end + )"); + + // see it work + lua.script(R"( +obj = object.new() +obj:print() + )"); + + object& obj = lua["obj"]; + assert(obj.value == 1); + + return 0; +} diff --git a/examples/require_dll_example/my_object.cpp b/examples/require_dll_example/my_object.cpp index bf27ee28..1b421568 100644 --- a/examples/require_dll_example/my_object.cpp +++ b/examples/require_dll_example/my_object.cpp @@ -16,7 +16,7 @@ namespace my_object { return module; } -} +} // namespace my_object extern "C" int luaopen_my_object(lua_State* L) { // pass the lua_State, diff --git a/examples/require_dll_example/my_object.hpp b/examples/require_dll_example/my_object.hpp index 03aedf98..ff86a502 100644 --- a/examples/require_dll_example/my_object.hpp +++ b/examples/require_dll_example/my_object.hpp @@ -2,6 +2,14 @@ #include "my_object_api.hpp" +// forward declare as a C struct +// so a pointer to lua_State can be part of a signature +extern "C" { + struct lua_State; +} +// you can replace the above if you're fine with including +// earlier than absolutely necessary + namespace my_object { struct test { @@ -11,7 +19,7 @@ namespace my_object { test(int val) : value(val) {} }; -} // my_object +} // namespace my_object // this function needs to be exported from your // dll. "extern 'C'" should do the trick, but diff --git a/examples/require_dll_example/my_object_api.hpp b/examples/require_dll_example/my_object_api.hpp index 1c1ab7a0..6af5f423 100644 --- a/examples/require_dll_example/my_object_api.hpp +++ b/examples/require_dll_example/my_object_api.hpp @@ -3,30 +3,25 @@ namespace my_object { #if defined _MSC_VER -#define MY_OBJECT_VC + #define MY_OBJECT_VC #elif defined __GNUC__ -#define MY_OBJECT_GCC + #define MY_OBJECT_GCC #elif defined __clang__ -#define MY_OBJECT_CLANG + #define MY_OBJECT_CLANG #endif -#if !defined(_NDEBUG) -#define MY_OBJECT_DEBUG -#else -#endif // Debug || Not-Debug - #if defined MY_OBJECT_VC -#if defined MY_OBJECT_DLL -#if defined MY_OBJECT_BUILD -#define MY_OBJECT_API __declspec(dllexport) -#else -#define MY_OBJECT_API __declspec(dllexport) -#endif // MY_OBJECT_BUILD - Building the Library vs. Using the Library -#else -#define MY_OBJECT_API -#endif // Building a DLL vs. Static Library -#else -#define MY_OBJECT_API -#endif + #if defined MY_OBJECT_DLL + #if defined MY_OBJECT_BUILD + #define MY_OBJECT_API __declspec(dllexport) + #else + #define MY_OBJECT_API __declspec(dllexport) + #endif // MY_OBJECT_BUILD - Building the Library vs. Using the Library + #else + #define MY_OBJECT_API + #endif // Building a DLL vs. Static Library +#else // g++ / clang++ + #define MY_OBJECT_API __attribute__ ((visibility ("default"))) +#endif // MY_OBJECT_BUILD -} // my_object +} // namespace my_object diff --git a/examples/stack_aligned_function.cpp b/examples/stack_aligned_function.cpp new file mode 100644 index 00000000..1148e2ea --- /dev/null +++ b/examples/stack_aligned_function.cpp @@ -0,0 +1,34 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include + +int main(int, char*[]) { + sol::state lua; + lua.script("function func (a, b) return (a + b) * 2 end"); + + sol::reference func_ref = lua["func"]; + + // for some reason, you need to use the low-level API + func_ref.push(); // function on stack now + + // maybe this is in a lua_CFunction you bind, + // or maybe you're trying to work with a pre-existing system + // maybe you've used a custom lua_load call, or you're working + // with state_view's load(lua_Reader, ...) call... + // here's a little bit of how you can work with the stack + lua_State* L = lua.lua_state(); + sol::stack_aligned_function func(L, -1); + lua_pushinteger(L, 5); // argument 1, using plain API + lua_pushinteger(L, 6); // argument 2 + + // take 2 arguments from the top, + // and use "stack_aligned_function" to call + int result = func(sol::stack_count(2)); + + // make sure everything is clean + assert(result == 22); + assert(lua.stack_top() == 0); // stack is empty/balanced + + return 0; +} diff --git a/sol/forward.hpp b/sol/forward.hpp index 1ad635d5..7647f2ce 100644 --- a/sol/forward.hpp +++ b/sol/forward.hpp @@ -53,14 +53,14 @@ namespace sol { using stack_environment = basic_environment; template class basic_function; - template + template class basic_protected_function; using unsafe_function = basic_function; - using safe_function = basic_protected_function; + using safe_function = basic_protected_function; using stack_unsafe_function = basic_function; - using stack_safe_function = basic_protected_function; + using stack_safe_function = basic_protected_function; using stack_aligned_unsafe_function = basic_function; - using stack_aligned_safe_function = basic_protected_function; + using stack_aligned_safe_function = basic_protected_function; using protected_function = safe_function; using stack_protected_function = stack_safe_function; using stack_aligned_protected_function = stack_aligned_safe_function; @@ -73,6 +73,8 @@ namespace sol { using stack_function = stack_unsafe_function; using stack_aligned_function = stack_aligned_unsafe_function; #endif + using stack_aligned_stack_handler_function = basic_protected_function; + struct function_result; struct protected_function_result; using safe_function_result = protected_function_result; diff --git a/sol/function_result.hpp b/sol/function_result.hpp index 4cb6a87d..a22c1a45 100644 --- a/sol/function_result.hpp +++ b/sol/function_result.hpp @@ -46,9 +46,7 @@ namespace sol { // Must be manual, otherwise destructor will screw us // return count being 0 is enough to keep things clean // but will be thorough - o.L = nullptr; - o.index = 0; - o.returncount = 0; + o.abandon(); } function_result& operator=(function_result&& o) { L = o.L; @@ -57,9 +55,7 @@ namespace sol { // Must be manual, otherwise destructor will screw us // return count being 0 is enough to keep things clean // but will be thorough - o.L = nullptr; - o.index = 0; - o.returncount = 0; + o.abandon(); return *this; } @@ -79,7 +75,11 @@ namespace sol { lua_State* lua_state() const { return L; }; int stack_index() const { return index; }; int return_count() const { return returncount; }; - + void abandon() noexcept { + L = nullptr; + index = 0; + returncount = 0; + } ~function_result() { lua_pop(L, returncount); } diff --git a/sol/protected_function.hpp b/sol/protected_function.hpp index b5de9f0c..3b885ebc 100644 --- a/sol/protected_function.hpp +++ b/sol/protected_function.hpp @@ -36,63 +36,90 @@ namespace sol { return name; } + template struct handler { - const reference& target; + typedef std::is_base_of is_stack; + const target_t& target; int stackindex; - handler(const reference& target) : target(target), stackindex(0) { - if (target.valid()) { + + handler(std::false_type, const target_t& target) : target(target), stackindex(0) { + if (b) { stackindex = lua_gettop(target.lua_state()) + 1; target.push(); } } - bool valid() const { return stackindex != 0; } + + handler(std::true_type, const target_t& target) : target(target), stackindex(0) { + if (b) { + stackindex = target.stack_index(); + } + } + + handler(const target_t& target) : handler(is_stack(), target) {} + + bool valid() const noexcept { return b; } + ~handler() { - if (valid()) { + if (b && !is_stack::value) { lua_remove(target.lua_state(), stackindex); } } }; - } + } // detail - template + template class basic_protected_function : public base_t { public: - static reference get_default_handler(lua_State* L) { - if (L == nullptr) - return reference(lua_nil); + typedef std::is_base_of is_stack_handler; + + static handler_t get_default_handler(lua_State* L) { + if (is_stack_handler::value || L == nullptr) + return handler_t(L, lua_nil); lua_getglobal(L, detail::default_handler_name()); auto pp = stack::pop_n(L, 1); - return reference(L, -1); + return handler_t(L, -1); } - static void set_default_handler(const reference& ref) { - ref.push(); - lua_setglobal(ref.lua_state(), detail::default_handler_name()); + template + static void set_default_handler(const T& ref) { + if (ref.lua_state() == nullptr) { + return; + } + if (!ref.valid()) { + lua_pushnil(ref.lua_state()); + lua_setglobal(ref.lua_state(), detail::default_handler_name()); + } + else { + ref.push(); + lua_setglobal(ref.lua_state(), detail::default_handler_name()); + } } private: - call_status luacall(std::ptrdiff_t argcount, std::ptrdiff_t resultcount, detail::handler& h) const { + template + call_status luacall(std::ptrdiff_t argcount, std::ptrdiff_t resultcount, detail::handler& h) const { return static_cast(lua_pcallk(lua_state(), static_cast(argcount), static_cast(resultcount), h.stackindex, 0, nullptr)); } - template - auto invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { + template + auto invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { luacall(n, sizeof...(Ret), h); return stack::pop>(lua_state()); } - template - Ret invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { + template + Ret invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { luacall(n, 1, h); return stack::pop(lua_state()); } - template - void invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { + template + void invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { luacall(n, 0, h); } - protected_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n, detail::handler& h) const { + template + protected_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n, detail::handler& h) const { int stacksize = lua_gettop(lua_state()); int poststacksize = stacksize; int firstreturn = 1; @@ -141,7 +168,7 @@ namespace sol { public: using base_t::lua_state; - reference error_handler; + handler_t error_handler; basic_protected_function() = default; template , basic_protected_function>>, meta::neg>, std::is_base_of>> = meta::enabler> @@ -159,12 +186,12 @@ namespace sol { basic_protected_function& operator=(basic_protected_function&&) = default; basic_protected_function(const basic_function& b) : basic_protected_function(b, get_default_handler(b.lua_state())) {} basic_protected_function(basic_function&& b) : basic_protected_function(std::move(b), get_default_handler(b.lua_state())) {} - basic_protected_function(const basic_function& b, reference eh) : base_t(b), error_handler(std::move(eh)) {} - basic_protected_function(basic_function&& b, reference eh) : base_t(std::move(b)), error_handler(std::move(eh)) {} + basic_protected_function(const basic_function& b, handler_t eh) : base_t(b), error_handler(std::move(eh)) {} + basic_protected_function(basic_function&& b, handler_t eh) : base_t(std::move(b)), error_handler(std::move(eh)) {} basic_protected_function(const stack_reference& r) : basic_protected_function(r.lua_state(), r.stack_index(), get_default_handler(r.lua_state())) {} basic_protected_function(stack_reference&& r) : basic_protected_function(r.lua_state(), r.stack_index(), get_default_handler(r.lua_state())) {} - basic_protected_function(const stack_reference& r, reference eh) : basic_protected_function(r.lua_state(), r.stack_index(), std::move(eh)) {} - basic_protected_function(stack_reference&& r, reference eh) : basic_protected_function(r.lua_state(), r.stack_index(), std::move(eh)) {} + basic_protected_function(const stack_reference& r, handler_t eh) : basic_protected_function(r.lua_state(), r.stack_index(), std::move(eh)) {} + basic_protected_function(stack_reference&& r, handler_t eh) : basic_protected_function(r.lua_state(), r.stack_index(), std::move(eh)) {} template basic_protected_function(const proxy_base& p) : basic_protected_function(p.operator basic_function(), get_default_handler(p.lua_state())) {} template @@ -172,27 +199,27 @@ namespace sol { template >>, meta::neg>>> = meta::enabler> basic_protected_function(lua_State* L, T&& r) : basic_protected_function(L, std::forward(r), get_default_handler(L)) {} template >>, meta::neg>>> = meta::enabler> - basic_protected_function(lua_State* L, T&& r, reference eh) : basic_protected_function(L, sol::ref_index(r.registry_index()), std::move(eh)) {} + basic_protected_function(lua_State* L, T&& r, handler_t eh) : basic_protected_function(L, sol::ref_index(r.registry_index()), std::move(eh)) {} basic_protected_function(lua_State* L, int index = -1) : basic_protected_function(L, index, get_default_handler(L)) {} - basic_protected_function(lua_State* L, int index, reference eh) : base_t(L, index), error_handler(std::move(eh)) { + basic_protected_function(lua_State* L, int index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS stack::check(L, index, type_panic); #endif // Safety } basic_protected_function(lua_State* L, absolute_index index) : basic_protected_function(L, index, get_default_handler(L)) {} - basic_protected_function(lua_State* L, absolute_index index, reference eh) : base_t(L, index), error_handler(std::move(eh)) { + basic_protected_function(lua_State* L, absolute_index index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS stack::check(L, index, type_panic); #endif // Safety } basic_protected_function(lua_State* L, raw_index index) : basic_protected_function(L, index, get_default_handler(L)) {} - basic_protected_function(lua_State* L, raw_index index, reference eh) : base_t(L, index), error_handler(std::move(eh)) { + basic_protected_function(lua_State* L, raw_index index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS stack::check(L, index, type_panic); #endif // Safety } basic_protected_function(lua_State* L, ref_index index) : basic_protected_function(L, index, get_default_handler(L)) {} - basic_protected_function(lua_State* L, ref_index index, reference eh) : base_t(L, index), error_handler(std::move(eh)) { + basic_protected_function(lua_State* L, ref_index index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); stack::check(L, -1, type_panic); @@ -211,12 +238,45 @@ namespace sol { template decltype(auto) call(Args&&... args) const { - detail::handler h(error_handler); if (!aligned) { - base_t::push(); + // we do not expect the function to already be on the stack: push it + if (error_handler.valid()) { + detail::handler h(error_handler); + base_t::push(); + int pushcount = stack::multi_push_reference(lua_state(), std::forward(args)...); + return invoke(types(), std::make_index_sequence(), pushcount, h); + } + else { + detail::handler h(error_handler); + base_t::push(); + int pushcount = stack::multi_push_reference(lua_state(), std::forward(args)...); + return invoke(types(), std::make_index_sequence(), pushcount, h); + } + } + else { + // the function is already on the stack at the right location + if (error_handler.valid()) { + // the handler will be pushed onto the stack manually, + // since it's not already on the stack this means we need to push our own + // function on the stack too and swap things to be in-place + if (!is_stack_handler::value) { + // so, we need to remove the function at the top and then dump the handler out ourselves + base_t::push(); + } + detail::handler h(error_handler); + if (!is_stack_handler::value) { + lua_replace(lua_state(), -3); + h.stackindex = lua_absindex(lua_state(), -2); + } + int pushcount = stack::multi_push_reference(lua_state(), std::forward(args)...); + return invoke(types(), std::make_index_sequence(), pushcount, h); + } + else { + detail::handler h(error_handler); + int pushcount = stack::multi_push_reference(lua_state(), std::forward(args)...); + return invoke(types(), std::make_index_sequence(), pushcount, h); + } } - int pushcount = stack::multi_push_reference(lua_state(), std::forward(args)...); - return invoke(types(), std::make_index_sequence(), pushcount, h); } }; } // sol diff --git a/sol/protected_function_result.hpp b/sol/protected_function_result.hpp index d10217f5..0b793280 100644 --- a/sol/protected_function_result.hpp +++ b/sol/protected_function_result.hpp @@ -82,11 +82,7 @@ namespace sol { // Must be manual, otherwise destructor will screw us // return count being 0 is enough to keep things clean // but we will be thorough - o.L = nullptr; - o.index = 0; - o.returncount = 0; - o.popcount = 0; - o.err = call_status::runtime; + o.abandon(); } protected_function_result& operator=(protected_function_result&& o) noexcept { L = o.L; @@ -97,11 +93,7 @@ namespace sol { // Must be manual, otherwise destructor will screw us // return count being 0 is enough to keep things clean // but we will be thorough - o.L = nullptr; - o.index = 0; - o.returncount = 0; - o.popcount = 0; - o.err = call_status::runtime; + o.abandon(); return *this; } @@ -122,7 +114,13 @@ namespace sol { int stack_index() const noexcept { return index; }; int return_count() const noexcept { return returncount; }; int pop_count() const noexcept { return popcount; }; - + void abandon() noexcept { + L = nullptr; + index = 0; + returncount = 0; + popcount = 0; + err = call_status::runtime; + } ~protected_function_result() { stack::remove(L, index, popcount); } diff --git a/sol/reference.hpp b/sol/reference.hpp index d6adb4f3..25a809be 100644 --- a/sol/reference.hpp +++ b/sol/reference.hpp @@ -140,6 +140,7 @@ namespace sol { lua_rawgeti(L, LUA_REGISTRYINDEX, index.index); ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX); } + reference(lua_State* L, lua_nil_t) noexcept : luastate(L) {} ~reference() noexcept { deref(); diff --git a/sol/stack_reference.hpp b/sol/stack_reference.hpp index f26a53c5..97de8408 100644 --- a/sol/stack_reference.hpp +++ b/sol/stack_reference.hpp @@ -38,6 +38,7 @@ namespace sol { public: stack_reference() noexcept = default; stack_reference(lua_nil_t) noexcept : stack_reference() {}; + stack_reference(lua_State* L, lua_nil_t) noexcept : L(L), index(0) {} stack_reference(lua_State* L, int i) noexcept : L(L), index(lua_absindex(L, i)) {} stack_reference(lua_State* L, absolute_index i) noexcept : L(L), index(i) {} stack_reference(lua_State* L, raw_index i) noexcept : L(L), index(i) {} diff --git a/sol/state_view.hpp b/sol/state_view.hpp index d33c08aa..e3d9bb95 100644 --- a/sol/state_view.hpp +++ b/sol/state_view.hpp @@ -64,7 +64,7 @@ namespace sol { err += " error:"; if (t == type::string) { err += " "; - string_detail::string_shim serr = stack::get(L, pfr.stack_index()); + string_view serr = stack::get(L, pfr.stack_index()); err.append(serr.data(), serr.size()); } #ifdef SOL_NO_EXCEPTIONS @@ -253,7 +253,7 @@ namespace sol { return stack::pop(L); } - object require_script(const std::string& key, const string_detail::string_shim& code, bool create_global = true, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + object require_script(const std::string& key, const string_view& code, bool create_global = true, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return require_core(key, [this, &code, &chunkname, &mode]() {stack::script(L, code, chunkname, mode); }, create_global); } @@ -262,13 +262,12 @@ namespace sol { } template - protected_function_result do_string(const string_detail::string_shim& code, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + protected_function_result do_string(const string_view& code, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), chunkname.c_str(), to_string(mode).c_str())); if (x != load_status::ok) { - return protected_function_result(L, -1, 0, 1, static_cast(x)); + return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast(x)); } - protected_function pf(L, -1); - pf.pop(); + stack_aligned_protected_function pf(L, -1); set_environment(env, pf); return pf(); } @@ -277,36 +276,33 @@ namespace sol { protected_function_result do_file(const std::string& filename, const basic_environment& env, load_mode mode = load_mode::any) { load_status x = static_cast(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())); if (x != load_status::ok) { - return protected_function_result(L, -1, 0, 1, static_cast(x)); + return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast(x)); } - protected_function pf(L, -1); - pf.pop(); + stack_aligned_protected_function pf(L, -1); set_environment(env, pf); return pf(); } - protected_function_result do_string(const string_detail::string_shim& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + protected_function_result do_string(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), chunkname.c_str(), to_string(mode).c_str())); if (x != load_status::ok) { - return protected_function_result(L, -1, 0, 1, static_cast(x)); + return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast(x)); } - protected_function pf(L, -1); - pf.pop(); + stack_aligned_protected_function pf(L, -1); return pf(); } protected_function_result do_file(const std::string& filename, load_mode mode = load_mode::any) { load_status x = static_cast(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())); if (x != load_status::ok) { - return protected_function_result(L, -1, 0, 1, static_cast(x)); + return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast(x)); } - protected_function pf(L, -1); - pf.pop(); + stack_aligned_protected_function pf(L, -1); return pf(); } template >> = meta::enabler> - protected_function_result safe_script(const string_detail::string_shim& code, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + protected_function_result safe_script(const string_view& code, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { protected_function_result pfr = do_string(code, chunkname, mode); if (!pfr.valid()) { return on_error(L, std::move(pfr)); @@ -314,18 +310,27 @@ namespace sol { return pfr; } - template >> = meta::enabler> - protected_function_result safe_script_file(const std::string& filename, Fx&& on_error, load_mode mode = load_mode::any) { - protected_function_result pfr = do_file(filename, mode); + template + protected_function_result safe_script(const string_view& code, const basic_environment& env, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + protected_function_result pfr = do_string(code, env, chunkname, mode); if (!pfr.valid()) { return on_error(L, std::move(pfr)); } return pfr; } - template - protected_function_result safe_script(const string_detail::string_shim& code, const basic_environment& env, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { - protected_function_result pfr = do_string(code, env, chunkname, mode); + template + protected_function_result safe_script(const string_view& code, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return safe_script(code, env, script_default_on_error, chunkname, mode); + } + + protected_function_result safe_script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return safe_script(code, script_default_on_error, chunkname, mode); + } + + template >> = meta::enabler> + protected_function_result safe_script_file(const std::string& filename, Fx&& on_error, load_mode mode = load_mode::any) { + protected_function_result pfr = do_file(filename, mode); if (!pfr.valid()) { return on_error(L, std::move(pfr)); } @@ -341,23 +346,31 @@ namespace sol { return pfr; } - protected_function_result safe_script(const string_detail::string_shim& code, const environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { - return safe_script(code, env, sol::script_default_on_error, chunkname, mode); - } - - protected_function_result safe_script_file(const std::string& filename, const environment& env, load_mode mode = load_mode::any) { - return safe_script_file(filename, env, sol::script_default_on_error, mode); - } - - protected_function_result safe_script(const string_detail::string_shim& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { - return safe_script(code, sol::script_default_on_error, chunkname, mode); + template + protected_function_result safe_script_file(const std::string& filename, const basic_environment& env, load_mode mode = load_mode::any) { + return safe_script_file(filename, env, script_default_on_error, mode); } protected_function_result safe_script_file(const std::string& filename, load_mode mode = load_mode::any) { - return safe_script_file(filename, sol::script_default_on_error, mode); + return safe_script_file(filename, script_default_on_error, mode); } - function_result unsafe_script(const string_detail::string_shim& code, const std::string& name = detail::default_chunk_name(), load_mode mode = load_mode::any) { + template + function_result unsafe_script(const string_view& code, const sol::basic_environment& env, load_mode mode = load_mode::any) { + int index = lua_gettop(L); + if (luaL_loadbufferx(L, code.data(), code.size(), name.data(), to_string(mode).c_str())) { + lua_error(L); + } + set_environment(env, stack_reference(L, raw_index(index + 1))); + if (lua_call(L, 0, LUA_MULTRET)) { + lua_error(L); + } + int postindex = lua_gettop(L); + int returns = postindex - index; + return function_result(L, (std::max)(postindex - (returns - 1), 1), returns); + } + + function_result unsafe_script(const string_view& code, const std::string& name = detail::default_chunk_name(), load_mode mode = load_mode::any) { int index = lua_gettop(L); stack::script(L, code, name, mode); int postindex = lua_gettop(L); @@ -365,6 +378,21 @@ namespace sol { return function_result(L, (std::max)(postindex - (returns - 1), 1), returns); } + template + function_result unsafe_script_file(const std::string& filename, const sol::basic_environment& env, load_mode mode = load_mode::any) { + int index = lua_gettop(L); + if (luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())) { + lua_error(L); + } + set_environment(env, stack_reference(L, raw_index(index + 1))); + if (lua_call(L, 0, LUA_MULTRET)) { + lua_error(L); + } + int postindex = lua_gettop(L); + int returns = postindex - index; + return function_result(L, (std::max)(postindex - (returns - 1), 1), returns); + } + function_result unsafe_script_file(const std::string& filename, load_mode mode = load_mode::any) { int index = lua_gettop(L); stack::script_file(L, filename, mode); @@ -374,7 +402,7 @@ namespace sol { } template >> = meta::enabler> - protected_function_result script(const string_detail::string_shim& code, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + protected_function_result script(const string_view& code, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return safe_script(code, std::forward(on_error), chunkname, mode); } @@ -384,7 +412,7 @@ namespace sol { } template - protected_function_result script(const string_detail::string_shim& code, const basic_environment& env, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + protected_function_result script(const string_view& code, const basic_environment& env, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return safe_script(code, env, std::forward(on_error), chunkname, mode); } @@ -393,15 +421,15 @@ namespace sol { return safe_script_file(filename, env, std::forward(on_error), mode); } - protected_function_result script(const string_detail::string_shim& code, const environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { - return safe_script(code, env, sol::script_default_on_error, chunkname, mode); + protected_function_result script(const string_view& code, const environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return safe_script(code, env, script_default_on_error, chunkname, mode); } protected_function_result script_file(const std::string& filename, const environment& env, load_mode mode = load_mode::any) { - return safe_script_file(filename, env, sol::script_default_on_error, mode); + return safe_script_file(filename, env, script_default_on_error, mode); } - function_result script(const string_detail::string_shim& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + function_result script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return unsafe_script(code, chunkname, mode); } @@ -409,7 +437,7 @@ namespace sol { return unsafe_script_file(filename, mode); } - load_result load(const string_detail::string_shim& code, const std::string& name = detail::default_chunk_name(), load_mode mode = load_mode::any) { + load_result load(const string_view& code, const std::string& name = detail::default_chunk_name(), load_mode mode = load_mode::any) { load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), name.c_str(), to_string(mode).c_str())); return load_result(L, lua_absindex(L, -1), 1, 1, x); } @@ -452,6 +480,10 @@ namespace sol { return total_memory_used(lua_state()); } + int stack_top() const { + return stack::top(L); + } + void collect_garbage() { lua_gc(lua_state(), LUA_GCCOLLECT, 0); } diff --git a/sol/string_shim.hpp b/sol/string_shim.hpp index ed0a41be..d9731573 100644 --- a/sol/string_shim.hpp +++ b/sol/string_shim.hpp @@ -91,6 +91,8 @@ namespace sol { }; #endif // C++17 } + + typedef string_detail::string_shim string_view; } #endif // SOL_STRING_SHIM_HPP diff --git a/sol/types.hpp b/sol/types.hpp index 81694a50..69194ab2 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -868,8 +868,8 @@ namespace sol { template struct lua_type_of> : std::integral_constant {}; - template - struct lua_type_of> : std::integral_constant {}; + template + struct lua_type_of> : std::integral_constant {}; template <> struct lua_type_of : std::integral_constant {}; @@ -1063,8 +1063,8 @@ namespace sol { struct is_function : std::false_type {}; template struct is_function> : std::true_type {}; - template - struct is_function> : std::true_type{}; + template + struct is_function> : std::true_type{}; template struct is_lightuserdata : std::false_type {}; diff --git a/test_state.cpp b/test_state.cpp index ab22cd68..132a3f06 100644 --- a/test_state.cpp +++ b/test_state.cpp @@ -199,13 +199,13 @@ TEST_CASE("state/script returns", "make sure script returns are done properly") R"( local example = { - str = "this is a string", - num = 1234, + str = "this is a string", + num = 1234, - func = function(self) - print(self.str) + func = function(self) + print(self.str) return "fstr" - end + end } return example;