From cb3acaa97b9879ffaa91c98fb6db7c6b91b748c6 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Thu, 15 Aug 2019 01:26:52 -0400 Subject: [PATCH] add more documentation, add clear() to bytecode, include additional examples and better data --- docs/CMakeLists.txt | 4 +- docs/source/api/coroutine.rst | 88 +++++-------- docs/source/api/environment.rst | 4 +- docs/source/threading.rst | 6 +- examples/source/docs/coroutine_main.cpp | 51 ++++++++ examples/source/docs/coroutine_thread.cpp | 36 ++++++ examples/source/docs/std_thread.cpp | 134 ++++++++++++++++++++ examples/source/optional_with_iteration.cpp | 49 +++---- include/sol/bytecode.hpp | 8 +- single/include/sol/forward.hpp | 4 +- single/include/sol/sol.hpp | 12 +- tests/runtime_tests/source/containers.cpp | 77 ++++++----- tests/runtime_tests/source/tables.cpp | 37 +++--- 13 files changed, 356 insertions(+), 154 deletions(-) create mode 100644 examples/source/docs/coroutine_main.cpp create mode 100644 examples/source/docs/coroutine_thread.cpp create mode 100644 examples/source/docs/std_thread.cpp diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 57011ffe..cc280513 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -24,9 +24,9 @@ # # # Required minimum version statement cmake_minimum_required(VERSION 3.5.0) -find_package(PythonInterp 3) +find_package(Python3 COMPONENTS Interpreter) -if (NOT PYTHONINTERP_FOUND) +if (NOT Python3_Interpreter_FOUND) message(FATAL_ERROR "sol2 documentation cannot be generated as python 3 has not been found: install or set the python 3 interpreter for the docs to find it and be sure to pip install sphinx") endif() diff --git a/docs/source/api/coroutine.rst b/docs/source/api/coroutine.rst index b6f5302e..f64d715c 100644 --- a/docs/source/api/coroutine.rst +++ b/docs/source/api/coroutine.rst @@ -3,61 +3,37 @@ coroutine *resumable/yielding functions from Lua* -A ``coroutine`` is a :doc:`reference` to a function in Lua that can be called multiple times to yield a specific result. It is run on the :doc:`lua_State` that was used to create it (see :doc:`thread` for an example on how to get a coroutine that runs on a thread separate from your usual "main" :doc:`lua_State`). +A ``coroutine`` is a :doc:`reference` to a function in Lua that can be called multiple times to yield a specific result. It is a cooperative function. It is run on the :doc:`lua_State` that was used to create it (see :doc:`thread` for an example on how to get a coroutine that runs on a stack space separate from your usual "main" stack space :doc:`lua_State`). -The ``coroutine`` object is entirely similar to the :doc:`protected_function` object, with additional member functions to check if a coroutine has yielded (:doc:`call_status::yielded`) and is thus runnable again, whether it has completed (:ref:`call_status::ok`) and thus cannot yield anymore values, or whether it has suffered an error (see :ref:`status()` and :ref:`call_status`'s error codes). +The ``coroutine`` object is entirely similar to the :doc:`protected_function` object, with additional member functions to check if a coroutine has yielded (:doc:`call_status::yielded`) and is thus runnable again, whether it has completed (:ref:`call_status::ok`) and thus cannot yield anymore values, or whether it has suffered an error (see :ref:`status()`'s and :ref:`call_status`'s error codes). For example, you can work with a coroutine like this: -.. code-block:: lua +.. literalinclude:: ../../../examples/source/docs/coroutine_main.cpp :caption: co.lua :name: co-lua + :lines: 8-15 + :linenos: - function loop() - while counter ~= 30 - do - coroutine.yield(counter); - counter = counter + 1; - end - return counter - end +This is a function that yields. We set the ``counter`` value in C++, and then use the coroutine to get a few values: -This is a function that yields: +.. literalinclude:: ../../../examples/source/docs/coroutine_main.cpp + :caption: coroutine_main.cpp + :name: coroutine_main + :lines: 1-6,18-19,21,25- + :linenos: -.. code-block:: cpp - :caption: main.cpp - :name: yield-main +Note that this code doesn't check for errors: to do so, you can call the function and assign it as ``auto result = loop_coroutine();``, then check ``result.valid()`` as is the case with :doc:`protected_function`. - sol::state lua; - lua.open_libraries(sol::lib::base, sol::lib::coroutine); - lua.script_file("co.lua"); - sol::coroutine cr = lua["loop"]; +Finally, you can run this coroutine on another stack space (NOT a different computer thread: Lua uses the term 'thread' a bit strangely, as we follow its usage of the term, but it is NOT a separate thread) by doing the following: - for (int counter = 0; // start from 0 - counter < 10 && cr; // we want 10 values, and we only want to run if the coroutine "cr" is valid - // Alternative: counter < 10 && cr.valid() - ++counter) { - // Call the coroutine, does the computation and then suspends - int value = cr(); - } - -Note that this code doesn't check for errors: to do so, you can call the function and assign it as ``auto result = cr();``, then check ``result.valid()`` as is the case with :doc:`protected_function`. Finally, you can run this coroutine on another thread by doing the following: - -.. code-block:: cpp - :caption: main_with_thread.cpp +.. literalinclude:: ../../../examples/source/docs/coroutine_thread.cpp + :caption: coroutine_thread.cpp :name: yield-main-thread + :lines: 1-6,18-19,21,25- + :linenos: - sol::state lua; - lua.open_libraries(sol::lib::base, sol::lib::coroutine); - lua.script_file("co.lua"); - sol::thread runner = sol::thread::create(lua.lua_state()); - sol::state_view runnerstate = runner.state(); - sol::coroutine cr = runnerstate["loop"]; - for (int counter = 0; counter < 10 && cr; ++counter) { - // Call the coroutine, does the computation and then suspends - int value = cr(); - } The following are the members of ``sol::coroutine``: @@ -73,44 +49,44 @@ members Grabs the coroutine at the specified index given a ``lua_State*``. .. code-block:: cpp - :caption: returning the coroutine's status + :caption: returning the coroutine's status :name: sol-coroutine-status - call_status status() const noexcept; + call_status status() const noexcept; Returns the status of a coroutine. .. code-block:: cpp - :caption: checks for an error + :caption: checks for an error :name: sol-coroutine-error - bool error() const noexcept; + bool error() const noexcept; Checks if an error occured when the coroutine was run. .. _runnable: .. code-block:: cpp - :caption: runnable and explicit operator bool + :caption: runnable and explicit operator bool :name: sol-coroutine-runnable - bool runnable () const noexcept; - explicit operator bool() const noexcept; + bool runnable () const noexcept; + explicit operator bool() const noexcept; These functions allow you to check if a coroutine can still be called (has more values to yield and has not errored). If you have a coroutine object ``coroutine my_co = /*...*/``, you can either check ``runnable()`` or do ``if ( my_co ) { /* use coroutine */ }``. .. code-block:: cpp - :caption: calling a coroutine + :caption: calling a coroutine :name: sol-coroutine-operator-call - template - protected_function_result operator()( Args&&... args ); + template + protected_function_result operator()( Args&&... args ); - template - decltype(auto) call( Args&&... args ); + template + decltype(auto) call( Args&&... args ); - template - decltype(auto) operator()( types, Args&&... args ); + template + decltype(auto) operator()( types, Args&&... args ); -Calls the coroutine. The second ``operator()`` lets you specify the templated return types using the ``my_co(sol::types, ...)`` syntax. Check ``status()`` afterwards for more information about the success of the run or just check the coroutine object in an ifs tatement, as shown :ref:`above`. \ No newline at end of file +Calls the coroutine. The second ``operator()`` lets you specify the templated return types using the ``my_co(sol::types, ...)`` syntax. Check ``status()`` afterwards for more information about the success of the run or just check the coroutine object in an ifs tatement, as shown :ref:`above`. diff --git a/docs/source/api/environment.rst b/docs/source/api/environment.rst index 4ff2c874..712fb409 100644 --- a/docs/source/api/environment.rst +++ b/docs/source/api/environment.rst @@ -38,12 +38,12 @@ free functions template void set_environment( const environment& env, const T& target ); -See :ref:`environment::set_on`. +See :ref:`environment::set_on`. .. code-block:: cpp :caption: function: get_environment - :name: sol-environment-set_environment + :name: sol-environment-get_environment template basic_environment get_environment( const T& target ); diff --git a/docs/source/threading.rst b/docs/source/threading.rst index bbf4c0c4..598d582b 100644 --- a/docs/source/threading.rst +++ b/docs/source/threading.rst @@ -5,9 +5,9 @@ Lua has no thread safety. sol does not force thread safety bottlenecks anywhere. Assume any access or any call on Lua affects the whole ``sol::state``/``lua_State*`` (because it does, in a fair bit of cases). Therefore, every call to a state should be blocked off in C++ with some kind of access control (when you're working with multiple C++ threads). When you start hitting the same state from multiple threads, race conditions (data or instruction) can happen. -Individual Lua coroutines might be able to run on separate C++-created threads without tanking the state utterly, since each Lua coroutine has the capability to run on an independent Lua execution stack (Lua confusingly calls it a ``thread`` in the C API, but it really just means a separate execution stack) as well as some other associated bits and pieces that won't quite interfere with the global state. +To handle multithreaded environments, it is encouraged to either spawn a Lua state (``sol::state``) for each thread you are working with and keep inter-state communication to synchronized serialization points. This means that 3 C++ threads should have 3 Lua states, and access between them should be controlled using some kind of synchronized C++ mechanism (actual transfer between states must be done by serializing the value into C++ and then re-pushing it into the other state). -To handle multithreaded environments, it is encouraged to either spawn a Lua state (``sol::state``) for each thread you are working with and keep inter-state communication to synchronized serialization points. This means that 3 C++ threads should each have their own Lua state, and access between them should be controlled using some kind of synchronized C++ mechanism (actual transfer between states must be done by serializing the value into C++ and then re-pushing it into the other state). +`Here is an example of a processor that explicitly serializes`_ Lua returns into C++ values, and Lua functions into state-transferrable byte code to do work. Using coroutines and Lua's threads might also buy you some concurrency and parallelism (**unconfirmed and likely untrue, do not gamble on this**), but remember that Lua's 'threading' technique is ultimately cooperative and requires explicit yielding and resuming (simplified as function calls for :doc:`sol::coroutine`). @@ -37,3 +37,5 @@ Here's an example of explicit state transferring below: .. literalinclude:: ../../examples/source/docs/state_transfer.cpp :name: state-transfer :linenos: + +.. _Here is an example of a processor that explicitly serializes: https://github.com/ThePhD/sol2/blob/develop/examples/source/docs/std_thread.cpp diff --git a/examples/source/docs/coroutine_main.cpp b/examples/source/docs/coroutine_main.cpp new file mode 100644 index 00000000..8913887e --- /dev/null +++ b/examples/source/docs/coroutine_main.cpp @@ -0,0 +1,51 @@ +#define SOL_ALL_SAFTIES_ON 1 +#include + +#include + +int main() { + const auto& co_lua_script = R"( +function loop() + while counter ~= 30 + do + coroutine.yield(counter); + counter = counter + 1; + end + return counter +end +)"; + + sol::state lua; + lua.open_libraries(sol::lib::base, sol::lib::coroutine); + /* + lua.script_file("co.lua"); + we load string directly rather than use a file + */ + lua.script(co_lua_script); + sol::coroutine loop_coroutine = lua["loop"]; + // set counter variable in C++ + // (can set it to something else to + // have loop_coroutine() yield different values) + lua["counter"] = 20; + + // example of using and re-using coroutine + // you do not have to use coroutines in a loop, + // this is just the example + + // we start from 0; + // we want 10 values, and we only want to + // run if the coroutine "loop_coroutine" is valid + for (int counter = 0; counter < 10 && loop_coroutine; ++counter) { + // Alternative: counter < 10 && cr.valid() + + // Call the coroutine, does the computation and then suspends + // once it returns, we get the value back from the return + // and then can use it + // we can either leave the coroutine like that can come to it later, + // or loop back around + int value = loop_coroutine(); + std::cout << "In C++: " << value << std::endl; + } + + return 0; +} diff --git a/examples/source/docs/coroutine_thread.cpp b/examples/source/docs/coroutine_thread.cpp new file mode 100644 index 00000000..8113b90b --- /dev/null +++ b/examples/source/docs/coroutine_thread.cpp @@ -0,0 +1,36 @@ +#define SOL_ALL_SAFTIES_ON 1 +#include + +#include + +int main() { + const auto& co_lua_script = R"( +function loop() + while counter ~= 30 + do + coroutine.yield(counter); + counter = counter + 1; + end + return counter +end +)"; + + sol::state lua; + lua.open_libraries(sol::lib::base, sol::lib::coroutine); + /* + lua.script_file("co.lua"); + we load string directly rather than use a file + */ + lua.script(co_lua_script); + sol::thread runner = sol::thread::create(lua.lua_state()); + sol::state_view runnerstate = runner.state(); + sol::coroutine loop_coroutine = lua["loop"]; + lua["counter"] = 20; + + for (int counter = 0; counter < 10 && loop_coroutine; ++counter) { + // Call the coroutine, does the computation and then suspends + int value = loop_coroutine(); + } + + return 0; +} diff --git a/examples/source/docs/std_thread.cpp b/examples/source/docs/std_thread.cpp new file mode 100644 index 00000000..c6101692 --- /dev/null +++ b/examples/source/docs/std_thread.cpp @@ -0,0 +1,134 @@ +#define SOL_ALL_SAFETIES_ON 1 +#include + +#include +#include +#include +#include +#include +#include + +struct worker_data { + std::mutex until_ready_mutex; + std::condition_variable until_ready_condition; + bool is_ready = false; + bool is_processed = false; + sol::state worker_lua; + sol::bytecode payload; + std::variant> return_payload; + + worker_data() { + worker_lua.open_libraries(sol::lib::base); + } +}; + +void worker_thread(worker_data& data) { + for (std::uint64_t loops = 0; true; ++loops) { + // Wait until main() sends data + std::unique_lock data_lock(data.until_ready_mutex); + data.until_ready_condition.wait(data_lock, [&data] { return data.is_ready; }); + + if (data.payload.size() == 0) { + // signaling we are done + return; + } + + // just for easier typing + sol::state& lua = data.worker_lua; + + // we own the lock now, do the work + std::variant> result = lua.safe_script(data.payload.as_string_view()); + + // store returning payload, + // clear current payload + data.return_payload = std::move(result); + data.payload.clear(); + + // Send result back to main + std::cout << "worker_thread data processing is completed: signaling & unlocking\n"; + data.is_processed = true; + data.is_ready = false; + data_lock.unlock(); + data.until_ready_condition.notify_one(); + } +} + +int main() { + + // main lua state + sol::state lua; + lua.open_libraries(sol::lib::base); + + // set up functions, etc. etc. + lua.script("function f () return 4.5 end"); + lua.script("function g () return { 1.1, 2.2, 3.3 } end"); + + // kick off worker + worker_data data; + std::thread worker(worker_thread, std::ref(data)); + + // main Lua state + bool done_working = false; + for (std::uint64_t loops = 0; !done_working; ++loops) { + // finished working? send nothing + // even loop? use f + // otherwise, use g + if (loops >= 3) { + data.payload.clear(); + done_working = true; + } + else if ((loops % 2) == 0) { + sol::function target = lua["f"]; + data.payload = target.dump(); + } + else { + sol::function target = lua["g"]; + data.payload = target.dump(); + } + + // send data to the worker thread + { + std::lock_guard lk(data.until_ready_mutex); + data.is_ready = true; + std::cout << "function serialized: sending to worker thread to execute on Lua state...\n"; + } + data.until_ready_condition.notify_one(); + + if (done_working) { + break; + } + // wait for the worker + { + std::unique_lock lock_waiting_for_worker(data.until_ready_mutex); + data.until_ready_condition.wait(lock_waiting_for_worker, [&data] { return data.is_processed; }); + data.is_processed = false; + } + auto data_processor = [](auto& returned_data) { + using option_type = std::remove_cv_t>; + if constexpr (std::is_same_v) { + std::cout << "received a double: " << returned_data << "\n"; + } + else if constexpr (std::is_same_v>) { + std::cout << "received a std::vector: { "; + for (std::size_t i = 0; i < returned_data.size(); ++i) { + std::cout << returned_data[i]; + if (i != static_cast(returned_data.size() - 1)) { + std::cout << ", "; + } + } + std::cout << " }\n"; + } + else { + std::cerr << "OH MY GOD YOU FORGOT TO HANDLE A TYPE OF DATA FROM A WORKER ABORT ABORT ABORT\n"; + std::abort(); + } + }; + std::visit(data_processor, data.return_payload); + } + + // join and wait for workers to come back + worker.join(); + + // workers are back, exit program + return 0; +} diff --git a/examples/source/optional_with_iteration.cpp b/examples/source/optional_with_iteration.cpp index abf6885c..74765e39 100644 --- a/examples/source/optional_with_iteration.cpp +++ b/examples/source/optional_with_iteration.cpp @@ -5,44 +5,37 @@ #include #include +struct thing { + int a = 20; + + thing() = default; + thing(int a) : a(a) { + } +}; + +struct super_thing : thing { + int b = 40; +}; + +struct unrelated {}; + int main(int, char* []) { std::cout << "=== optional with iteration ===" << std::endl; - struct thing { - int a = 20; - - thing() = default; - thing(int a) : a(a) {} - }; - - struct super_thing : thing { - int b = 40; - }; - - struct unrelated { - - }; - sol::state lua; // Comment out the new_usertype call // to prevent derived class "super_thing" // from being picked up and cast to its base // class - lua.new_usertype("super_thing", - sol::base_classes, sol::bases() - ); + lua.new_usertype("super_thing", sol::base_classes, sol::bases()); // Make a few things lua["t1"] = thing{}; lua["t2"] = super_thing{}; lua["t3"] = unrelated{}; // And a table - lua["container"] = lua.create_table_with( - 0, thing{50}, - 1, unrelated{}, - 4, super_thing{} - ); + lua["container"] = lua.create_table_with(0, thing{ 50 }, 1, unrelated{}, 4, super_thing{}); std::vector> things; @@ -50,7 +43,7 @@ int main(int, char* []) { // We use some lambda techniques and pass the function itself itself so we can recurse, // but a regular function would work too! auto fx = [&things](auto& f, auto& tbl) -> void { - // You can iterate through a table: it has + // You can iterate through a table: it has // begin() and end() // like standard containers for (auto key_value_pair : tbl) { @@ -66,10 +59,9 @@ int main(int, char* []) { case sol::type::table: { sol::table inner = value.as(); f(f, inner); - } - break; + } break; case sol::type::userdata: { - // This allows us to check if a userdata is + // This allows us to check if a userdata is // a specific class type sol::optional maybe_thing = value.as>(); if (maybe_thing) { @@ -83,8 +75,7 @@ int main(int, char* []) { std::cout << "thing.a ==" << the_thing.a << std::endl; things.push_back(the_thing); } - } - break; + } break; default: break; } diff --git a/include/sol/bytecode.hpp b/include/sol/bytecode.hpp index cf85371a..ab413fe8 100644 --- a/include/sol/bytecode.hpp +++ b/include/sol/bytecode.hpp @@ -75,11 +75,13 @@ namespace sol { using base_t::rend; - using base_t::swap; using base_t::get_allocator; + using base_t::swap; + using base_t::clear; using base_t::emplace; using base_t::emplace_back; + using base_t::erase; using base_t::insert; using base_t::pop_back; using base_t::push_back; @@ -87,7 +89,7 @@ namespace sol { using base_t::resize; using base_t::shrink_to_fit; - string_view as_string_view () const { + string_view as_string_view() const { return string_view(reinterpret_cast(this->data()), this->size()); } }; @@ -100,7 +102,7 @@ namespace sol { try { bc.insert(bc.cend(), p_code, p_code + memory_size); } - catch ( ... ) { + catch (...) { return -1; } return 0; diff --git a/single/include/sol/forward.hpp b/single/include/sol/forward.hpp index 9b5a9510..56e35f50 100644 --- a/single/include/sol/forward.hpp +++ b/single/include/sol/forward.hpp @@ -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 2019-08-12 18:06:54.421648 UTC -// This header was generated with sol v3.0.3 (revision 5799084) +// Generated 2019-08-15 05:26:10.792424 UTC +// This header was generated with sol v3.0.3 (revision 09f5e86) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_FORWARD_HPP diff --git a/single/include/sol/sol.hpp b/single/include/sol/sol.hpp index 282de47c..fb6ba5d1 100644 --- a/single/include/sol/sol.hpp +++ b/single/include/sol/sol.hpp @@ -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 2019-08-12 18:06:53.939935 UTC -// This header was generated with sol v3.0.3 (revision 5799084) +// Generated 2019-08-15 05:26:10.095950 UTC +// This header was generated with sol v3.0.3 (revision 09f5e86) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -3507,11 +3507,13 @@ namespace sol { using base_t::rbegin; using base_t::rend; - using base_t::swap; using base_t::get_allocator; + using base_t::swap; + using base_t::clear; using base_t::emplace; using base_t::emplace_back; + using base_t::erase; using base_t::insert; using base_t::pop_back; using base_t::push_back; @@ -3519,7 +3521,7 @@ namespace sol { using base_t::resize; using base_t::shrink_to_fit; - string_view as_string_view () const { + string_view as_string_view() const { return string_view(reinterpret_cast(this->data()), this->size()); } }; @@ -3532,7 +3534,7 @@ namespace sol { try { bc.insert(bc.cend(), p_code, p_code + memory_size); } - catch ( ... ) { + catch (...) { return -1; } return 0; diff --git a/tests/runtime_tests/source/containers.cpp b/tests/runtime_tests/source/containers.cpp index 10db59fd..e03ed3f5 100644 --- a/tests/runtime_tests/source/containers.cpp +++ b/tests/runtime_tests/source/containers.cpp @@ -1,4 +1,4 @@ -// sol3 +// sol3 // The MIT License (MIT) @@ -40,9 +40,11 @@ TEST_CASE("containers/returns", "make sure that even references to vectors are being serialized as tables") { sol::state lua; std::vector v{ 1, 2, 3 }; - lua.set_function("f", [&]() -> std::vector& { + auto f = [&]() -> std::vector& { + REQUIRE(v.size() == 3); return v; - }); + }; + lua.set_function("f", f); auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error); REQUIRE(result1.valid()); sol::object x = lua["x"]; @@ -64,10 +66,14 @@ TEST_CASE("containers/custom usertype", "make sure container usertype metatables sol::state lua; lua.open_libraries(); lua.new_usertype("bark", - "something", [](const bark& b) { - INFO("It works: " << b.at(24)); - }, - "size", &bark::size, "at", sol::resolve(&bark::at), "clear", &bark::clear); + "something", + [](const bark& b) { INFO("It works: " << b.at(24)); }, + "size", + &bark::size, + "at", + sol::resolve(&bark::at), + "clear", + &bark::clear); bark obj{ { 24, 50 } }; lua.set("a", &obj); { @@ -175,7 +181,8 @@ func = function(vecs) print(i, ":", vec.x, vec.y, vec.z) end end -)", sol::script_pass_on_error); +)", + sol::script_pass_on_error); REQUIRE(result0.valid()); sol::protected_function f(lua["func"]); @@ -185,12 +192,12 @@ end REQUIRE(pfr2.valid()); } -TEST_CASE("containers/usertype transparency", "Make sure containers pass their arguments through transparently and push the results as references, not new values") { +TEST_CASE( + "containers/usertype transparency", "Make sure containers pass their arguments through transparently and push the results as references, not new values") { class A { public: int a; - A(int b = 2) - : a(b){}; + A(int b = 2) : a(b){}; void func() { } @@ -208,13 +215,13 @@ TEST_CASE("containers/usertype transparency", "Make sure containers pass their a }; sol::state lua; - lua.new_usertype("B", - "a_list", &B::a_list); + lua.new_usertype("B", "a_list", &B::a_list); auto result = lua.safe_script(R"( b = B.new() a_ref = b.a_list[2] -)", sol::script_pass_on_error); +)", + sol::script_pass_on_error); REQUIRE(result.valid()); B& b = lua["b"]; @@ -265,13 +272,10 @@ TEST_CASE("containers/is container", "make sure the is_container trait behaves p sol::state lua; lua.open_libraries(); - lua.new_usertype("options_type", - "output_help", &options::output_help); + lua.new_usertype("options_type", "output_help", &options::output_help); - lua.new_usertype("machine_type", - "new", sol::no_constructor, - "opt", [](machine& m) { return &m.opt; }, - "copy_opt", [](machine& m) { return m.opt; }); + lua.new_usertype( + "machine_type", "new", sol::no_constructor, "opt", [](machine& m) { return &m.opt; }, "copy_opt", [](machine& m) { return m.opt; }); { machine m; @@ -279,7 +283,8 @@ TEST_CASE("containers/is container", "make sure the is_container trait behaves p auto result0 = lua.safe_script(R"( machine:opt():output_help() - )", sol::script_pass_on_error); + )", + sol::script_pass_on_error); REQUIRE(result0.valid()); REQUIRE(options::last == &m.opt); @@ -300,10 +305,11 @@ TEST_CASE("containers/readonly", "make sure readonly members are stored appropri std::list seq; }; - lua.new_usertype( - "foo", - "seq", &foo::seq, // this one works - "readonly_seq", sol::readonly(&foo::seq) // this one does not work + lua.new_usertype("foo", + "seq", + &foo::seq, // this one works + "readonly_seq", + sol::readonly(&foo::seq) // this one does not work ); lua["value"] = std::list{ {}, {}, {} }; @@ -312,7 +318,8 @@ a = foo.new() x = a.seq a.seq = value y = a.readonly_seq -)", sol::script_pass_on_error); +)", + sol::script_pass_on_error); REQUIRE(result0.valid()); std::list& seqrefx = lua["x"]; std::list& seqrefy = lua["y"]; @@ -357,9 +364,9 @@ TEST_CASE("containers/to_args", "Test that the to_args abstractions works") { TEST_CASE("containers/append idiom", "ensure the append-idiom works as intended") { sol::state lua; lua.open_libraries(sol::lib::base); - + auto result1 = lua.safe_script( - R"( + R"( function f_fill(vec) print("#vec in lua: " .. #vec) for k = 1, #vec do @@ -373,7 +380,8 @@ function f_append(vec) vec[#vec + 1] = -54 print("#vec in lua: " .. #vec) end -)", sol::script_pass_on_error); +)", + sol::script_pass_on_error); REQUIRE(result1.valid()); std::vector fill_cmp{ 1, 2, 3 }; @@ -397,8 +405,7 @@ TEST_CASE("containers/non_copyable", "make sure non-copyable types in containers struct test { std::vector b; - test() - : b() { + test() : b() { } test(test&&) = default; test& operator=(test&&) = default; @@ -408,8 +415,7 @@ TEST_CASE("containers/non_copyable", "make sure non-copyable types in containers SECTION("normal") { sol::state lua; - lua.new_usertype("test", - "b", sol::readonly(&test::b)); + lua.new_usertype("test", "b", sol::readonly(&test::b)); lua["v"] = std::vector{}; @@ -487,7 +493,8 @@ TEST_CASE("containers/pairs", "test how well pairs work with the underlying syst TEST_CASE("containers/pointer types", "check that containers with unique usertypes and pointers or something") { struct base_t { virtual int get() const = 0; - virtual ~base_t(){} + virtual ~base_t() { + } }; struct derived_1_t : base_t { @@ -516,7 +523,7 @@ TEST_CASE("containers/pointer types", "check that containers with unique usertyp v2.push_back(&d2); lua["c1"] = std::move(v1); lua["c2"] = &v2; - + auto result1 = lua.safe_script("b1 = c1[1]", sol::script_pass_on_error); REQUIRE(result1.valid()); base_t* b1 = lua["b1"]; diff --git a/tests/runtime_tests/source/tables.cpp b/tests/runtime_tests/source/tables.cpp index b91ac6e7..a6b45cc4 100644 --- a/tests/runtime_tests/source/tables.cpp +++ b/tests/runtime_tests/source/tables.cpp @@ -1,4 +1,4 @@ -// sol3 +// sol3 // The MIT License (MIT) @@ -137,15 +137,12 @@ TEST_CASE("tables/create-with-local", "Check if creating a table is kosher") { TEST_CASE("tables/function variables", "Check if tables and function calls work as intended") { sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::os); - auto run_script = [](sol::state& lua) -> void { - lua.safe_script("assert(os.fun() == \"test\")"); - }; + auto run_script = [](sol::state& lua) -> void { lua.safe_script("assert(os.fun() == \"test\")"); }; - lua.get("os").set_function("fun", - []() { - INFO("stateless lambda()"); - return "test"; - }); + lua.get("os").set_function("fun", []() { + INFO("stateless lambda()"); + return "test"; + }); REQUIRE_NOTHROW(run_script(lua)); lua.get("os").set_function("fun", &free_function); @@ -165,11 +162,10 @@ TEST_CASE("tables/function variables", "Check if tables and function calls work // stateful lambda: non-convertible, cannot be optimised int breakit = 50; - lua.get("os").set_function("fun", - [&breakit]() { - INFO("stateful lambda() with breakit:" << breakit); - return "test"; - }); + lua.get("os").set_function("fun", [&breakit]() { + INFO("stateful lambda() with breakit:" << breakit); + return "test"; + }); REQUIRE_NOTHROW(run_script(lua)); // r-value, cannot optimise @@ -203,9 +199,10 @@ TEST_CASE("tables/add", "Basic test to make sure the 'add' feature works") { TEST_CASE("tables/raw set and raw get", "ensure raw setting and getting works through metatables") { sol::state lua; sol::table t = lua.create_table(); - t[sol::metatable_key] = lua.create_table_with( - sol::meta_function::new_index, [](lua_State* L) { return luaL_error(L, "nay"); }, - sol::meta_function::index, [](lua_State* L) { return luaL_error(L, "nay"); }); + t[sol::metatable_key] = lua.create_table_with(sol::meta_function::new_index, + [](lua_State* L) { return luaL_error(L, "nay"); }, + sol::meta_function::index, + [](lua_State* L) { return luaL_error(L, "nay"); }); t.raw_set("a", 2.5); double la = t.raw_get("a"); REQUIRE(la == 2.5); @@ -241,9 +238,13 @@ TEST_CASE("tables/optional move", "ensure pushing a sol::optional rvalue corr sol::state sol_state; struct move_only { int secret_code; + move_only(int sc) : secret_code(sc) {} + move_only(const move_only&) = delete; move_only(move_only&&) = default; + move_only& operator=(const move_only&) = delete; + move_only& operator=(move_only&&) = default; }; - sol_state["requires_move"] = sol::optional{ move_only{ 0x4D } }; + sol_state["requires_move"] = sol::optional(move_only(0x4D)); REQUIRE(sol_state["requires_move"].get().secret_code == 0x4D); }