mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
add more documentation, add clear() to bytecode, include additional examples and better data
This commit is contained in:
parent
09f5e862b8
commit
cb3acaa97b
|
@ -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()
|
||||
|
||||
|
|
|
@ -3,61 +3,37 @@ coroutine
|
|||
*resumable/yielding functions from Lua*
|
||||
|
||||
|
||||
A ``coroutine`` is a :doc:`reference<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<state>` that was used to create it (see :doc:`thread<thread>` for an example on how to get a coroutine that runs on a thread separate from your usual "main" :doc:`lua_State<state>`).
|
||||
A ``coroutine`` is a :doc:`reference<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<state>` that was used to create it (see :doc:`thread<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<state>`).
|
||||
|
||||
The ``coroutine`` object is entirely similar to the :doc:`protected_function<protected_function>` object, with additional member functions to check if a coroutine has yielded (:doc:`call_status::yielded<types>`) and is thus runnable again, whether it has completed (:ref:`call_status::ok<call-status>`) and thus cannot yield anymore values, or whether it has suffered an error (see :ref:`status()<status>` and :ref:`call_status<call-status>`'s error codes).
|
||||
The ``coroutine`` object is entirely similar to the :doc:`protected_function<protected_function>` object, with additional member functions to check if a coroutine has yielded (:doc:`call_status::yielded<types>`) and is thus runnable again, whether it has completed (:ref:`call_status::ok<call-status>`) and thus cannot yield anymore values, or whether it has suffered an error (see :ref:`status()<thread-status>`'s and :ref:`call_status<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<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<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``:
|
||||
|
||||
|
|
|
@ -38,12 +38,12 @@ free functions
|
|||
template <typename T>
|
||||
void set_environment( const environment& env, const T& target );
|
||||
|
||||
See :ref:`environment::set_on<environment-set-on>`.
|
||||
See :ref:`environment::set_on<sol-environment-set_on>`.
|
||||
|
||||
|
||||
.. code-block:: cpp
|
||||
:caption: function: get_environment
|
||||
:name: sol-environment-set_environment
|
||||
:name: sol-environment-get_environment
|
||||
|
||||
template <typename E = reference, typename T>
|
||||
basic_environment<E> get_environment( const T& target );
|
||||
|
|
|
@ -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<api/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
|
||||
|
|
51
examples/source/docs/coroutine_main.cpp
Normal file
51
examples/source/docs/coroutine_main.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#define SOL_ALL_SAFTIES_ON 1
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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;
|
||||
}
|
36
examples/source/docs/coroutine_thread.cpp
Normal file
36
examples/source/docs/coroutine_thread.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#define SOL_ALL_SAFTIES_ON 1
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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;
|
||||
}
|
134
examples/source/docs/std_thread.cpp
Normal file
134
examples/source/docs/std_thread.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
#define SOL_ALL_SAFETIES_ON 1
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
#include <variant>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
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<double, std::vector<double>> 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<std::mutex> 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<double, std::vector<double>> 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<std::mutex> 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<std::mutex> 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<std::remove_reference_t<decltype(returned_data)>>;
|
||||
if constexpr (std::is_same_v<option_type, double>) {
|
||||
std::cout << "received a double: " << returned_data << "\n";
|
||||
}
|
||||
else if constexpr (std::is_same_v<option_type, std::vector<double>>) {
|
||||
std::cout << "received a std::vector<double>: { ";
|
||||
for (std::size_t i = 0; i < returned_data.size(); ++i) {
|
||||
std::cout << returned_data[i];
|
||||
if (i != static_cast<std::size_t>(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;
|
||||
}
|
|
@ -5,23 +5,22 @@
|
|||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
int main(int, char* []) {
|
||||
std::cout << "=== optional with iteration ===" << std::endl;
|
||||
|
||||
struct thing {
|
||||
struct thing {
|
||||
int a = 20;
|
||||
|
||||
thing() = default;
|
||||
thing(int a) : a(a) {}
|
||||
};
|
||||
thing(int a) : a(a) {
|
||||
}
|
||||
};
|
||||
|
||||
struct super_thing : thing {
|
||||
struct super_thing : thing {
|
||||
int b = 40;
|
||||
};
|
||||
};
|
||||
|
||||
struct unrelated {
|
||||
struct unrelated {};
|
||||
|
||||
};
|
||||
int main(int, char* []) {
|
||||
std::cout << "=== optional with iteration ===" << std::endl;
|
||||
|
||||
sol::state lua;
|
||||
|
||||
|
@ -29,20 +28,14 @@ int main(int, char* []) {
|
|||
// to prevent derived class "super_thing"
|
||||
// from being picked up and cast to its base
|
||||
// class
|
||||
lua.new_usertype<super_thing>("super_thing",
|
||||
sol::base_classes, sol::bases<thing>()
|
||||
);
|
||||
lua.new_usertype<super_thing>("super_thing", sol::base_classes, sol::bases<thing>());
|
||||
|
||||
// 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<std::reference_wrapper<thing>> things;
|
||||
|
@ -66,8 +59,7 @@ int main(int, char* []) {
|
|||
case sol::type::table: {
|
||||
sol::table inner = value.as<sol::table>();
|
||||
f(f, inner);
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
case sol::type::userdata: {
|
||||
// This allows us to check if a userdata is
|
||||
// a specific class type
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<const char*>(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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<const char*>(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;
|
||||
|
|
|
@ -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<int> v{ 1, 2, 3 };
|
||||
lua.set_function("f", [&]() -> std::vector<int>& {
|
||||
auto f = [&]() -> std::vector<int>& {
|
||||
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>("bark",
|
||||
"something", [](const bark& b) {
|
||||
INFO("It works: " << b.at(24));
|
||||
},
|
||||
"size", &bark::size, "at", sol::resolve<const int&>(&bark::at), "clear", &bark::clear);
|
||||
"something",
|
||||
[](const bark& b) { INFO("It works: " << b.at(24)); },
|
||||
"size",
|
||||
&bark::size,
|
||||
"at",
|
||||
sol::resolve<const int&>(&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>("B",
|
||||
"a_list", &B::a_list);
|
||||
lua.new_usertype<B>("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>("options_type",
|
||||
"output_help", &options::output_help);
|
||||
lua.new_usertype<options>("options_type", "output_help", &options::output_help);
|
||||
|
||||
lua.new_usertype<machine>("machine_type",
|
||||
"new", sol::no_constructor,
|
||||
"opt", [](machine& m) { return &m.opt; },
|
||||
"copy_opt", [](machine& m) { return m.opt; });
|
||||
lua.new_usertype<machine>(
|
||||
"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<bar> seq;
|
||||
};
|
||||
|
||||
lua.new_usertype<foo>(
|
||||
"foo",
|
||||
"seq", &foo::seq, // this one works
|
||||
"readonly_seq", sol::readonly(&foo::seq) // this one does not work
|
||||
lua.new_usertype<foo>("foo",
|
||||
"seq",
|
||||
&foo::seq, // this one works
|
||||
"readonly_seq",
|
||||
sol::readonly(&foo::seq) // this one does not work
|
||||
);
|
||||
lua["value"] = std::list<bar>{ {}, {}, {} };
|
||||
|
||||
|
@ -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<bar>& seqrefx = lua["x"];
|
||||
std::list<bar>& seqrefy = lua["y"];
|
||||
|
@ -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<int> 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<non_copyable> 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>("test",
|
||||
"b", sol::readonly(&test::b));
|
||||
lua.new_usertype<test>("test", "b", sol::readonly(&test::b));
|
||||
|
||||
lua["v"] = std::vector<non_copyable>{};
|
||||
|
||||
|
@ -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 {
|
||||
|
|
|
@ -137,12 +137,9 @@ 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<sol::table>("os").set_function("fun",
|
||||
[]() {
|
||||
lua.get<sol::table>("os").set_function("fun", []() {
|
||||
INFO("stateless lambda()");
|
||||
return "test";
|
||||
});
|
||||
|
@ -165,8 +162,7 @@ TEST_CASE("tables/function variables", "Check if tables and function calls work
|
|||
|
||||
// stateful lambda: non-convertible, cannot be optimised
|
||||
int breakit = 50;
|
||||
lua.get<sol::table>("os").set_function("fun",
|
||||
[&breakit]() {
|
||||
lua.get<sol::table>("os").set_function("fun", [&breakit]() {
|
||||
INFO("stateful lambda() with breakit:" << breakit);
|
||||
return "test";
|
||||
});
|
||||
|
@ -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<double>("a");
|
||||
REQUIRE(la == 2.5);
|
||||
|
@ -241,9 +238,13 @@ TEST_CASE("tables/optional move", "ensure pushing a sol::optional<T> 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>{ move_only{ 0x4D } };
|
||||
sol_state["requires_move"] = sol::optional<move_only>(move_only(0x4D));
|
||||
REQUIRE(sol_state["requires_move"].get<move_only>().secret_code == 0x4D);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user