From 433e34cd9b7429b3395a7a1b5e135f45d75b3108 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sat, 26 Nov 2016 13:58:06 -0500 Subject: [PATCH] as_args and threading API cleanliness. --- docs/source/api/as_args.rst | 46 +++++++++++++++++++ docs/source/api/thread.rst | 15 ++++++- docs/source/conf.py | 2 +- single/sol/sol.hpp | 90 ++++++++++++++++++++++++++++++++++++- sol/as_args.hpp | 52 +++++++++++++++++++++ sol/object.hpp | 1 + sol/thread.hpp | 57 +++++++++++++++++++++++ test_containers.cpp | 32 +++++++++++++ 8 files changed, 291 insertions(+), 4 deletions(-) create mode 100644 docs/source/api/as_args.rst create mode 100644 sol/as_args.hpp diff --git a/docs/source/api/as_args.rst b/docs/source/api/as_args.rst new file mode 100644 index 00000000..9d202776 --- /dev/null +++ b/docs/source/api/as_args.rst @@ -0,0 +1,46 @@ +as_args +======= +turn an iterable argument into multiple arguments +------------------------------------------------- + +.. code-block:: cpp + + template + as_args_t { ... }; + + template + as_args_t as_args( T&& ); + + +``sol::as_args`` is a function that that takes an iterable and turns it into multiple arguments to a function call. It forwards its arguments, and is meant to be used as shown below: + +.. code-block:: cpp + :caption: as_args.c++ + + #define SOL_CHECK_ARGUMENTS + #include + + #include + #include + + int main(int argc, const char* argv[]) { + + sol::state lua; + lua.open_libraries(); + + lua.script("function f (a, b, c, d) print(a, b, c, d) end"); + + sol::function f = lua["f"]; + + std::vector v2{ 3, 4 }; + f(1, 2, sol::as_args(v2)); + + std::set v4{ 3, 1, 2, 4 }; + f(sol::as_args(v4)); + + int v3[] = { 2, 3, 4 }; + f(1, sol::as_args(v3)); + + return 0; + } + diff --git a/docs/source/api/thread.rst b/docs/source/api/thread.rst index 1a713f97..e5372ccd 100644 --- a/docs/source/api/thread.rst +++ b/docs/source/api/thread.rst @@ -9,15 +9,21 @@ a separate state that can contain and run functions ``sol::thread`` is a separate runnable part of the Lua VM that can be used to execute work separately from the main thread, such as with :doc:`coroutines`. To take a table or a coroutine and run it specifically on the ``sol::thread`` you either pulled out of lua or created, just get that function through the :ref:`state of the thread` +.. note:: + + A CPU thread is not always equivalent to a new thread in Lua: ``std::this_thread::get_id()`` can be the same for 2 callbacks that have 2 distinct Lua threads. In order to know which thread a callback was called in, hook into :doc:`sol::this_state` from your Lua callback and then construct a ``sol::thread``, passing in the ``sol::this_state`` for both the first and last arguments. Then examine the results of the status and ``is_...`` calls below. + members ------- .. code-block:: cpp :caption: constructor: thread + thread(stack_reference r); thread(lua_State* L, int index = -1); + thread(lua_State* L, lua_State* actual_thread); -Takes a thread from the Lua stack at the specified index and allows a person to use all of the abstractions therein. +Takes a thread from the Lua stack at the specified index and allows a person to use all of the abstractions therein. It can also take an actual thread state to make a thread from that as well. .. code-block:: cpp :caption: function: view into thread_state()'s state @@ -42,6 +48,13 @@ This function retrieves the ``lua_State*`` that represents the thread. Retrieves the :doc:`thread status` that describes the current state of the thread. +.. code-block:: cpp + :caption: main thread status + + bool is_main_thread () const; + +Checks to see if the thread is the main Lua thread. + .. code-block:: cpp :caption: function: thread creation :name: thread-create diff --git a/docs/source/conf.py b/docs/source/conf.py index 54e51630..25105d44 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -61,7 +61,7 @@ author = 'ThePhD' # The short X.Y version. version = '2.15' # The full version, including alpha/beta/rc tags. -release = '2.15.2' +release = '2.15.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/single/sol/sol.hpp b/single/sol/sol.hpp index ffc559c2..e51687a8 100644 --- a/single/sol/sol.hpp +++ b/single/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 2016-11-26 08:32:10.873025 UTC -// This header was generated with sol v2.15.2 (revision 289ded3) +// Generated 2016-11-26 18:57:33.418806 UTC +// This header was generated with sol v2.15.2 (revision 7b63057) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -7041,6 +7041,35 @@ namespace sol { // end of sol/stack.hpp +// beginning of sol/as_args.hpp + +namespace sol { + template + struct to_args_t { + T src; + }; + + template + auto as_args(Source&& source) { + return to_args_t{ std::forward(source) }; + } + + namespace stack { + template + struct pusher> { + int push(lua_State* L, const to_args_t& e) { + int p = 0; + for (const auto& i : e.src) { + p += stack::push(L, i); + } + return p; + } + }; + } +} // sol + +// end of sol/as_args.hpp + // beginning of sol/variadic_args.hpp // beginning of sol/stack_proxy.hpp @@ -12690,6 +12719,49 @@ namespace sol { // beginning of sol/thread.hpp namespace sol { + struct lua_thread_state { + lua_State* L; + operator lua_State* () const { + return L; + } + lua_State* operator-> () const { + return L; + } + }; + + namespace stack { + + template <> + struct pusher { + int push(lua_State*, lua_thread_state lts) { + lua_pushthread(lts.L); + return 1; + } + }; + + template <> + struct getter { + lua_thread_state get(lua_State* L, int index = -1) { + lua_thread_state lts{ lua_tothread(L, index) }; + return lts; + } + }; + + template <> + struct check_getter { + template + optional get(lua_State* L, int index, Handler&& handler) { + lua_thread_state lts{ lua_tothread(L, index) }; + if (lts.L == nullptr) { + handler(L, index, type::thread, type_of(L, index)); + return nullopt; + } + return lts; + } + }; + + } + class thread : public reference { public: thread() noexcept = default; @@ -12704,11 +12776,25 @@ namespace sol { type_assert(L, index, type::thread); #endif // Safety } + thread(lua_State* L, lua_State* actualthread) : thread(L, lua_thread_state{ actualthread }) {} + thread(lua_State* L, sol::this_state actualthread) : thread(L, lua_thread_state{ actualthread.L }) {} + thread(lua_State* L, lua_thread_state actualthread) : reference(L, -stack::push(L, actualthread)) { +#ifdef SOL_CHECK_ARGUMENTS + type_assert(L, -1, type::thread); +#endif // Safety + lua_pop(L, 1); + } state_view state() const { return state_view(this->thread_state()); } + bool is_main_thread() const { + int ismainthread = lua_pushthread(this->thread_state()); + lua_pop(this->thread_state(), 1); + return ismainthread == 1; + } + lua_State* thread_state() const { auto pp = stack::push_pop(*this); lua_State* lthread = lua_tothread(lua_state(), -1); diff --git a/sol/as_args.hpp b/sol/as_args.hpp new file mode 100644 index 00000000..2b5092cc --- /dev/null +++ b/sol/as_args.hpp @@ -0,0 +1,52 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_TO_ARGS_HPP +#define SOL_TO_ARGS_HPP + +#include "stack.hpp" + +namespace sol { + template + struct to_args_t { + T src; + }; + + template + auto as_args(Source&& source) { + return to_args_t{ std::forward(source) }; + } + + namespace stack { + template + struct pusher> { + int push(lua_State* L, const to_args_t& e) { + int p = 0; + for (const auto& i : e.src) { + p += stack::push(L, i); + } + return p; + } + }; + } +} // sol + +#endif // SOL_TO_ARGS_HPP diff --git a/sol/object.hpp b/sol/object.hpp index fafa0b68..7a27275f 100644 --- a/sol/object.hpp +++ b/sol/object.hpp @@ -25,6 +25,7 @@ #include "reference.hpp" #include "stack.hpp" #include "userdata.hpp" +#include "as_args.hpp" #include "variadic_args.hpp" #include "optional.hpp" diff --git a/sol/thread.hpp b/sol/thread.hpp index d4834dee..9ac5736e 100644 --- a/sol/thread.hpp +++ b/sol/thread.hpp @@ -26,6 +26,49 @@ #include "stack.hpp" namespace sol { + struct lua_thread_state { + lua_State* L; + operator lua_State* () const { + return L; + } + lua_State* operator-> () const { + return L; + } + }; + + namespace stack { + + template <> + struct pusher { + int push(lua_State*, lua_thread_state lts) { + lua_pushthread(lts.L); + return 1; + } + }; + + template <> + struct getter { + lua_thread_state get(lua_State* L, int index = -1) { + lua_thread_state lts{ lua_tothread(L, index) }; + return lts; + } + }; + + template <> + struct check_getter { + template + optional get(lua_State* L, int index, Handler&& handler) { + lua_thread_state lts{ lua_tothread(L, index) }; + if (lts.L == nullptr) { + handler(L, index, type::thread, type_of(L, index)); + return nullopt; + } + return lts; + } + }; + + } + class thread : public reference { public: thread() noexcept = default; @@ -40,11 +83,25 @@ namespace sol { type_assert(L, index, type::thread); #endif // Safety } + thread(lua_State* L, lua_State* actualthread) : thread(L, lua_thread_state{ actualthread }) {} + thread(lua_State* L, sol::this_state actualthread) : thread(L, lua_thread_state{ actualthread.L }) {} + thread(lua_State* L, lua_thread_state actualthread) : reference(L, -stack::push(L, actualthread)) { +#ifdef SOL_CHECK_ARGUMENTS + type_assert(L, -1, type::thread); +#endif // Safety + lua_pop(L, 1); + } state_view state() const { return state_view(this->thread_state()); } + bool is_main_thread() const { + int ismainthread = lua_pushthread(this->thread_state()); + lua_pop(this->thread_state(), 1); + return ismainthread == 1; + } + lua_State* thread_state() const { auto pp = stack::push_pop(*this); lua_State* lthread = lua_tothread(lua_state(), -1); diff --git a/test_containers.cpp b/test_containers.cpp index acb35814..224c7cce 100644 --- a/test_containers.cpp +++ b/test_containers.cpp @@ -458,3 +458,35 @@ a.readonly_seq = value; )"); REQUIRE_FALSE(result.valid()); } + +TEST_CASE("containers/to_args", "Test that the to_args abstractions works") { + sol::state lua; + lua.open_libraries(); + + lua.script("function f (a, b, c, d) print(a, b, c, d) return a, b, c, d end"); + + sol::function f = lua["f"]; + int a, b, c, d; + + std::vector v2{ 3, 4 }; + sol::tie(a, b, c, d) = f(1, 2, sol::as_args(v2)); + REQUIRE(a == 1); + REQUIRE(b == 2); + REQUIRE(c == 3); + REQUIRE(d == 4); + + std::set v4{ 7, 6, 8, 5 }; + sol::tie(a, b, c, d) = f(sol::as_args(v4)); + REQUIRE(a == 5); + REQUIRE(b == 6); + REQUIRE(c == 7); + REQUIRE(d == 8); + + int v3[] = { 10, 11, 12 }; + sol::tie(a, b, c, d) = f(9, sol::as_args(v3)); + REQUIRE(a == 9); + REQUIRE(b == 10); + REQUIRE(c == 11); + REQUIRE(d == 12); + +}