From 4094ba3e7a564931f1d2334e37cffe528e9323a9 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Thu, 30 Mar 2017 14:11:43 -0400 Subject: [PATCH] New feature: `nested` getter for complex nested tables, plus docs and example --- docs/source/api/nested.rst | 16 +++++ examples/containers_as_table.cpp | 49 +++++++++++--- single/sol/sol.hpp | 109 ++++++++++++++++++++++++++++--- sol/stack_check.hpp | 2 +- sol/stack_get.hpp | 71 ++++++++++++++++++-- sol/types.hpp | 32 +++++++++ 6 files changed, 255 insertions(+), 24 deletions(-) create mode 100644 docs/source/api/nested.rst diff --git a/docs/source/api/nested.rst b/docs/source/api/nested.rst new file mode 100644 index 00000000..960e4cfa --- /dev/null +++ b/docs/source/api/nested.rst @@ -0,0 +1,16 @@ +nested +====== + +.. code-block:: cpp + + template + struct nested { + T source; + }; + + +``sol::nested<...>`` is a template class similar to :doc:`sol::as_table`, but with the caveat that every :doc:`container type` within the ``sol::nested`` type will be retrieved as a table from lua. This is helpful when you need to receive C++-style vectors, lists, and maps nested within each other: all of them will be deserialized from lua using table properties rather than anything else. + +The `example`_ provides a very in-depth look at both ``sol::as_table`` and ``sol::nested``, and how the two are equivalent. + +.. _example: https://github.com/ThePhD/sol2/blob/develop/examples/containers_as_table.cpp \ No newline at end of file diff --git a/examples/containers_as_table.cpp b/examples/containers_as_table.cpp index ad6857bf..4c50c4d3 100644 --- a/examples/containers_as_table.cpp +++ b/examples/containers_as_table.cpp @@ -6,21 +6,51 @@ #include #include -// Nota bene the signature here -// Every container-type that's a table must be wrapped in `sol::as_table_t` -// it's verbose, so feel free to use typedefs ot make it easy on you -void demo (sol::as_table_t>>> src) { + +// nested allows serialization of maps with vectors inside, and vice-versa +// all from a nested structure of Lua tables +// it has less control over which pieces are considered tables in Lua, +// and which ones are considered userdata, but it covers a good 90% of cases +// where someone wants to handle a nested table +void demo(sol::nested>> src) { + std::cout << "demo, sol::nested<...>" << std::endl; const auto& listmap = src.source; assert(listmap.size() == 2); for (const auto& kvp : listmap) { - const std::vector& strings = kvp.second.source; + const std::vector& strings = kvp.second; assert(strings.size() == 3); - std::cout << kvp.first << " = "; + std::cout << "\t" << kvp.first << " = "; for (const auto& s : strings) { std::cout << "'" << s << "'" << " "; } std::cout << std::endl; } + std::cout << std::endl; +} + +// This second demo is equivalent to the first +// Nota bene the signature here +// Every container-type that's meant to be +// a table must be wrapped in `sol::as_table_t` +// it's verbose, so feel free to use typedefs to make it easy on you +// you can mix which parts are considered tables from Lua, and which parts +// are considered other kinds of types, such as userdata and the like +void demo_explicit (sol::as_table_t>>> src) { + std::cout << "demo, explicit sol::as_table_t<...>" << std::endl; + // Have to access the "source" member variable for as_table_t + const auto& listmap = src.source; + assert(listmap.size() == 2); + for (const auto& kvp : listmap) { + // Have to access the internal "source" for the inner as_table_t, as well + const std::vector& strings = kvp.second.source; + assert(strings.size() == 3); + std::cout << "\t" << kvp.first << " = "; + for (const auto& s : strings) { + std::cout << "'" << s << "'" << " "; + } + std::cout << std::endl; + } + std::cout << std::endl; } int main(int, char**) { @@ -29,12 +59,15 @@ int main(int, char**) { sol::state lua; // bind the function lua.set_function("f", &demo); + lua.set_function("g", &demo_explicit); // Call it with a table that has string sequences set to distinct keys lua.script(R"( -f({ +t = { key1 = {'hello', 'there', 'world'}, key2 = {'bark', 'borf', 'woof'} -}) +} +f(t) +g(t) )"); std::cout << std::endl; diff --git a/single/sol/sol.hpp b/single/sol/sol.hpp index 93636c05..3e4c42c5 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 2017-03-30 08:20:24.093445 UTC -// This header was generated with sol v2.16.0 (revision 4f72880) +// Generated 2017-03-30 18:11:26.562402 UTC +// This header was generated with sol v2.16.0 (revision 92d31b3) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -3286,6 +3286,18 @@ namespace sol { } }; + template + struct nested { + T source; + + template + nested(Args&&... args) : source(std::forward(args)...) {} + + operator std::add_lvalue_reference_t() { + return source; + } + }; + template as_table_t as_table(T&& container) { return as_table_t(std::forward(container)); @@ -3674,6 +3686,18 @@ namespace sol { template struct is_container : std::false_type {}; + template <> + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + template struct is_container>::value>> : std::true_type {}; @@ -3810,6 +3834,14 @@ namespace sol { inline type type_of() { return lua_type_of>::value; } + + namespace detail { + template + struct lua_type_of, std::enable_if_t<::sol::is_container::value>> : std::integral_constant {}; + + template + struct lua_type_of, std::enable_if_t::value>> : lua_type_of {}; + } // detail } // sol // end of sol/types.hpp @@ -5131,7 +5163,7 @@ namespace sol { return true; } if (t != type::userdata) { - handler(L, index, type::function, t); + handler(L, index, type::table, t); return false; } return true; @@ -5330,6 +5362,11 @@ namespace sol { struct getter, std::enable_if_t>::value>> { static T get(lua_State* L, int relindex, record& tracking) { typedef typename T::value_type V; + return get(types(), L, relindex, tracking); + } + + template + static T get(types, lua_State* L, int relindex, record& tracking) { tracking.use(1); int index = lua_absindex(L, relindex); @@ -5385,10 +5422,15 @@ namespace sol { typedef typename T::value_type P; typedef typename P::first_type K; typedef typename P::second_type V; + return get(types(), L, index, tracking); + } + + template + static T get(types, lua_State* L, int relindex, record& tracking) { tracking.use(1); T associative; - index = lua_absindex(L, index); + int index = lua_absindex(L, relindex); lua_pushnil(L); while (lua_next(L, index) != 0) { decltype(auto) key = stack::check_get(L, -2); @@ -5403,6 +5445,40 @@ namespace sol { } }; + template + struct getter, std::enable_if_t::value>> { + static T get(lua_State* L, int index, record& tracking) { + getter g; + // VC++ has a bad warning here: shut it up + (void)g; + return g.get(L, index, tracking); + } + }; + + template + struct getter, std::enable_if_t, meta::neg>>>::value>> { + static T get(lua_State* L, int index, record& tracking) { + typedef typename T::value_type V; + getter> g; + // VC++ has a bad warning here: shut it up + (void)g; + return g.get(types>(), L, index, tracking); + } + }; + + template + struct getter, std::enable_if_t, meta::has_key_value_pair>>::value>> { + static T get(lua_State* L, int index, record& tracking) { + typedef typename T::value_type P; + typedef typename P::first_type K; + typedef typename P::second_type V; + getter> g; + // VC++ has a bad warning here: shut it up + (void)g; + return g.get(types>(), L, index, tracking); + } + }; + template struct getter::value || std::is_base_of::value>> { static T get(lua_State* L, int index, record& tracking) { @@ -5716,35 +5792,50 @@ namespace sol { tracking.use(1); return nullptr; } - return getter>::get_no_lua_nil(L, index, tracking); + getter> g; + // Avoid VC++ warning + (void)g; + return g.get_no_lua_nil(L, index, tracking); } }; template struct getter> { static T* get(lua_State* L, int index, record& tracking) { - return getter>::get_no_lua_nil(L, index, tracking); + getter> g; + // Avoid VC++ warning + (void)g; + return g.get_no_lua_nil(L, index, tracking); } }; template struct getter { static T& get(lua_State* L, int index, record& tracking) { - return getter>::get(L, index, tracking); + getter> g; + // Avoid VC++ warning + (void)g; + return g.get(L, index, tracking); } }; template struct getter> { static T& get(lua_State* L, int index, record& tracking) { - return getter{}.get(L, index, tracking); + getter g; + // Avoid VC++ warning + (void)g; + return g.get(L, index, tracking); } }; template struct getter { static T* get(lua_State* L, int index, record& tracking) { - return getter>::get(L, index, tracking); + getter> g; + // Avoid VC++ warning + (void)g; + return g.get(L, index, tracking); } }; diff --git a/sol/stack_check.hpp b/sol/stack_check.hpp index 82739c83..771331b3 100644 --- a/sol/stack_check.hpp +++ b/sol/stack_check.hpp @@ -259,7 +259,7 @@ namespace sol { return true; } if (t != type::userdata) { - handler(L, index, type::function, t); + handler(L, index, type::table, t); return false; } return true; diff --git a/sol/stack_get.hpp b/sol/stack_get.hpp index ca7e4d4c..2b736ec7 100644 --- a/sol/stack_get.hpp +++ b/sol/stack_get.hpp @@ -81,6 +81,11 @@ namespace sol { struct getter, std::enable_if_t>::value>> { static T get(lua_State* L, int relindex, record& tracking) { typedef typename T::value_type V; + return get(types(), L, relindex, tracking); + } + + template + static T get(types, lua_State* L, int relindex, record& tracking) { tracking.use(1); int index = lua_absindex(L, relindex); @@ -136,10 +141,15 @@ namespace sol { typedef typename T::value_type P; typedef typename P::first_type K; typedef typename P::second_type V; + return get(types(), L, index, tracking); + } + + template + static T get(types, lua_State* L, int relindex, record& tracking) { tracking.use(1); T associative; - index = lua_absindex(L, index); + int index = lua_absindex(L, relindex); lua_pushnil(L); while (lua_next(L, index) != 0) { decltype(auto) key = stack::check_get(L, -2); @@ -154,6 +164,40 @@ namespace sol { } }; + template + struct getter, std::enable_if_t::value>> { + static T get(lua_State* L, int index, record& tracking) { + getter g; + // VC++ has a bad warning here: shut it up + (void)g; + return g.get(L, index, tracking); + } + }; + + template + struct getter, std::enable_if_t, meta::neg>>>::value>> { + static T get(lua_State* L, int index, record& tracking) { + typedef typename T::value_type V; + getter> g; + // VC++ has a bad warning here: shut it up + (void)g; + return g.get(types>(), L, index, tracking); + } + }; + + template + struct getter, std::enable_if_t, meta::has_key_value_pair>>::value>> { + static T get(lua_State* L, int index, record& tracking) { + typedef typename T::value_type P; + typedef typename P::first_type K; + typedef typename P::second_type V; + getter> g; + // VC++ has a bad warning here: shut it up + (void)g; + return g.get(types>(), L, index, tracking); + } + }; + template struct getter::value || std::is_base_of::value>> { static T get(lua_State* L, int index, record& tracking) { @@ -467,35 +511,50 @@ namespace sol { tracking.use(1); return nullptr; } - return getter>::get_no_lua_nil(L, index, tracking); + getter> g; + // Avoid VC++ warning + (void)g; + return g.get_no_lua_nil(L, index, tracking); } }; template struct getter> { static T* get(lua_State* L, int index, record& tracking) { - return getter>::get_no_lua_nil(L, index, tracking); + getter> g; + // Avoid VC++ warning + (void)g; + return g.get_no_lua_nil(L, index, tracking); } }; template struct getter { static T& get(lua_State* L, int index, record& tracking) { - return getter>::get(L, index, tracking); + getter> g; + // Avoid VC++ warning + (void)g; + return g.get(L, index, tracking); } }; template struct getter> { static T& get(lua_State* L, int index, record& tracking) { - return getter{}.get(L, index, tracking); + getter g; + // Avoid VC++ warning + (void)g; + return g.get(L, index, tracking); } }; template struct getter { static T* get(lua_State* L, int index, record& tracking) { - return getter>::get(L, index, tracking); + getter> g; + // Avoid VC++ warning + (void)g; + return g.get(L, index, tracking); } }; diff --git a/sol/types.hpp b/sol/types.hpp index 76b8437c..92a61d52 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -307,6 +307,18 @@ namespace sol { } }; + template + struct nested { + T source; + + template + nested(Args&&... args) : source(std::forward(args)...) {} + + operator std::add_lvalue_reference_t() { + return source; + } + }; + template as_table_t as_table(T&& container) { return as_table_t(std::forward(container)); @@ -695,6 +707,18 @@ namespace sol { template struct is_container : std::false_type {}; + template <> + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + template struct is_container>::value>> : std::true_type {}; @@ -831,6 +855,14 @@ namespace sol { inline type type_of() { return lua_type_of>::value; } + + namespace detail { + template + struct lua_type_of, std::enable_if_t<::sol::is_container::value>> : std::integral_constant {}; + + template + struct lua_type_of, std::enable_if_t::value>> : lua_type_of {}; + } // detail } // sol #endif // SOL_TYPES_HPP