From fb276676d179fc3ce1f09235eb820837d621dd06 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Wed, 6 Sep 2017 15:09:51 -0400 Subject: [PATCH] create and test `raw_set` and `raw_get` add `new_enum` overload for initializer lists add ipairs overload for 5.2 users --- .gitignore | 1 + docs/source/api/state.rst | 15 ++- docs/source/api/table.rst | 38 ++++++- docs/source/api/types.rst | 6 +- docs/source/containers.rst | 9 +- sol/container_traits.hpp | 48 +++++++-- sol/container_usertype_metatable.hpp | 18 +++- sol/simple_usertype_metatable.hpp | 2 +- sol/stack.hpp | 4 +- sol/state_view.hpp | 16 ++- sol/table_core.hpp | 146 +++++++++++++++++++-------- sol/types.hpp | 6 +- sol/usertype_metatable.hpp | 2 +- tests/test_tables.cpp | 64 +++++++++--- 14 files changed, 289 insertions(+), 86 deletions(-) diff --git a/.gitignore b/.gitignore index 63f7ea37..d881776b 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,4 @@ temp.bad_syntax.lua temp.good.lua catch_mock.hpp main_aux.cpp +main.hpp diff --git a/docs/source/api/state.rst b/docs/source/api/state.rst index 4571d12f..0d22ce96 100644 --- a/docs/source/api/state.rst +++ b/docs/source/api/state.rst @@ -81,7 +81,7 @@ These functions run the desired blob of either code that is in a string, or code 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. ``sol::script_default_on_error`` and ``sol::script_pass_on_error`` are 2 functions that will either generate a traceback error to return / throw (if throwing is allowed); or, pass the error on through and return it to the user (respectively). Then, handle the errors any way you like: +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. ``sol::script_default_on_error`` and ``sol::script_pass_on_error`` are 2 functions provided by sol that will either generate a traceback error to return / throw (if throwing is allowed); or, pass the error on through and return it to the user (respectively). An example of having your: .. code-block:: cpp :caption: running code safely @@ -89,14 +89,19 @@ 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 + // uses sol::script_default_on_error, which either panics or throws, + // depending on your configuration and compiler settings auto result1 = lua.safe_script("bad.code"); + + // a custom handler that you write yourself + // is only called when an error happens with loading or running the script 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 + // the user can do whatever they like here, including throw. Otherwise... + sol::error err = pfr; + std::cout << err.what() << std::endl; - // You can also just return it, and let the call-site handle the error if necessary. + // ... they need to return the protected_function_result return pfr; }); } diff --git a/docs/source/api/table.rst b/docs/source/api/table.rst index e9d6a226..48258075 100644 --- a/docs/source/api/table.rst +++ b/docs/source/api/table.rst @@ -31,6 +31,7 @@ The first takes a table from the Lua stack at the specified index and allows a p .. code-block:: cpp :caption: function: get / traversing get + :name: get-value template decltype(auto) get(Keys&&... keys) const; @@ -49,6 +50,23 @@ These functions retrieve items from the table. The first one (``get``) can pull If the keys within nested queries try to traverse into a table that doesn't exist, the second lookup into the nil-returned variable and belong will cause a panic to be fired by the lua C API. If you need to check for keys, check with ``auto x = table.get>( std::tie("a", "b", "c" ) );``, and then use the :doc:`optional` interface to check for errors. As a short-hand, easy method for returning a default if a value doesn't exist, you can use ``get_or`` instead. +.. code-block:: cpp + :caption: function: raw get / traversing raw get + :name: raw-get-value + + template + decltype(auto) raw_get(Keys&&... keys) const; + + template + decltype(auto) traverse_raw_get(Keys&&... keys) const; + + template + decltype(auto) raw_get_or(Key&& key, T&& otherwise) const; + + template + decltype(auto) raw_get_or(Key&& key, D&& otherwise) const; + + .. code-block:: cpp :caption: function: set / traversing set :name: set-value @@ -61,6 +79,18 @@ If the keys within nested queries try to traverse into a table that doesn't exis These functions set items into the table. The first one (``set``) can set *multiple* values, in the form ``key_a, value_a, key_b, value_b, ...``. It is similar to ``table[key_a] = value_a; table[key_b] = value_b, ...``. The second one (``traverse_set``) sets a *single* value, using all but the last argument as keys to do another lookup into the value retrieved prior to it. It is equivalent to ``table[key_a][key_b][...] = value;``. +.. code-block:: cpp + :caption: function: raw set / traversing raw set + :name: raw-set-value + + template + table& raw_set(Args&&... args); + + template + table& traverse_raw_set(Args&&... args); + +Similar to :ref:`set`, but it does so "raw" (ignoring metamethods on the table's metatable). + .. note:: Value semantics are applied to all set operations. If you do not ``std::ref( obj )`` or specifically make a pointer with ``std::addressof( obj )`` or ``&obj``, it will copy / move. This is different from how :doc:`sol::function` behaves with its call operator. Also note that this does not detect callables by default: see the :ref:`note here`. @@ -124,8 +154,12 @@ This class of functions creates a new :doc:`simple usertype` wi template basic_table_core& new_enum(const std::string& name, Args&&... args); + template + basic_table_core& new_enum(const std::string& name, std::initializer_list> items); -Use this function to create an enumeration type in Lua. By default, the enum will be made read-only, which creates a tiny performance hit to make the values stored in this table behave exactly like a read-only enumeration in C++. If you plan on changing the enum values in Lua, set the ``read_only`` template parameter in your ``new_enum`` call to false. The arguments are expected to come in ``key, value, key, value, ...`` list. +Use this function to create an enumeration type in Lua. By default, the enum will be made read-only, which creates a tiny performance hit to make the values stored in this table behave exactly like a read-only enumeration in C++. If you plan on changing the enum values in Lua, set the ``read_only`` template parameter in your ``new_enum`` call to false. The arguments are expected to come in ``key, value, key, value, ...`` list. + +If you use the second overload, you will create a (runtime) ``std::initializer_list``. This will avoid compiler overhead for excessively large enumerations. For this overload, hoever, you must pass the enumeration name as a template parameter first, and then the ``read_only`` parameter, like ``lua.new_enum( "my_enum", { {"a", my_enum:: a}, { "b", my_enum::b } } );``. .. _set_usertype: @@ -176,7 +210,7 @@ A functional ``for_each`` loop that calls the desired function. The passed in fu template proxy operator[](T&& key) const; -Generates a :doc:`proxy` that is templated on the table type and the key type. Enables lookup of items and their implicit conversion to a desired type. +Generates a :doc:`proxy` that is templated on the table type and the key type. Enables lookup of items and their implicit conversion to a desired type. Lookup is done lazily. .. code-block:: cpp :caption: function: create a table with defaults diff --git a/docs/source/api/types.rst b/docs/source/api/types.rst index 264e8a5d..82ebe470 100644 --- a/docs/source/api/types.rst +++ b/docs/source/api/types.rst @@ -162,9 +162,11 @@ These functions get the type of a C++ type ``T``; or the type at the specified i .. code-block:: cpp :caption: type checking convenience functions - int type_panic(lua_State* L, int index, type expected, type actual); + int type_panic_string(lua_State* L, int index, type expected, type actual, const std::string& message); - int no_panic(lua_State*, int, type, type) noexcept; + int type_panic_c_str(lua_State* L, int index, type expected, type actual, const char* message); + + int no_panic(lua_State*, int, type, type, const char*) noexcept; void type_error(lua_State* L, int expected, int actual); diff --git a/docs/source/containers.rst b/docs/source/containers.rst index 2859f851..906879ba 100644 --- a/docs/source/containers.rst +++ b/docs/source/containers.rst @@ -20,7 +20,7 @@ Containers are objects that are meant to be inspected and iterated and whose job .. note:: - Please note that c-style arrays must be added to Lua using ``lua["my_arr"] = &my_c_array;`` or ``lua["my_arr"] = std::ref(my_c_array);`` to be bestowed these properties. No, a plain ``T*`` pointer is **not** considered an array. This is important because ``lua["my_string"] = "some string";`` is also typed as an array (``const char[n]``) and thusly we can only use ``std::reference_wrapper``\s or pointers to arrays to work for us. + Please note that c-style arrays must be added to Lua using ``lua["my_arr"] = &my_c_array;`` or ``lua["my_arr"] = std::ref(my_c_array);`` to be bestowed these properties. No, a plain ``T*`` pointer is **not** considered an array. This is important because ``lua["my_string"] = "some string";`` is also typed as an array (``const char[n]``) and thusly we can only use ``std::reference_wrapper``\s or pointers to the actual array types to work for this purpose. .. _container-detection: @@ -145,10 +145,15 @@ Below are the many container operations and their override points for ``containe | | | | | - works only in Lua 5.2+ | | | | | | - calling ``pairs( c )`` in Lua 5.1 / LuaJIT will crash with assertion failure (Lua expects ``c`` to be a table) | +-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ipairs | | ``static int ipairs(lua_State*);`` | 1 self | - implement if advanced user only that understands caveats | +| | | | | - override begin and end instead and leave this to default implementation if you do not know what ``__ipairs`` is for or how to implement it and the ``next`` function | +| | | | | - works only in Lua 5.2, deprecated in Lua 5.3 (but might still be called in compatibiltiy modes) | +| | | | | - calling ``ipairs( c )`` in Lua 5.1 / LuaJIT will crash with assertion failure (Lua expects ``c`` to be a table) | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ .. note:: - If your type does not adequately support ``begin()`` and ``end()`` and you cannot override it, use the ``sol::is_container`` trait override plus a custom implementation of ``pairs`` on your usertype to get it to work as you please. Note that a type not having proper ``begin()`` and ``end()`` will not work if you try to forcefully serialize it as a table (this means avoid using :doc:`sol::as_table` and :doc:`sol::nested`, otherwise you will have compiler errors). Just set it or get it directly, as shown in the examples, to work with the C++ containers. + If your type does not adequately support ``begin()`` and ``end()`` and you cannot override it, use the ``sol::is_container`` trait override along with a custom implementation of ``pairs`` on your usertype to get it to work as you want it to. Note that a type not having proper ``begin()`` and ``end()`` will not work if you try to forcefully serialize it as a table (this means avoid using :doc:`sol::as_table` and :doc:`sol::nested`, otherwise you will have compiler errors). Just set it or get it directly, as shown in the examples, to work with the C++ containers. .. _container-classifications: diff --git a/sol/container_traits.hpp b/sol/container_traits.hpp index 72c1979d..aaf0df7d 100644 --- a/sol/container_traits.hpp +++ b/sol/container_traits.hpp @@ -246,6 +246,19 @@ namespace sol { static const bool value = sizeof(test(0)) == sizeof(char); }; + template + struct has_traits_ipairs_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(&C::ipairs)); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + template struct has_traits_add_test { private: @@ -289,6 +302,9 @@ namespace sol { template using has_traits_pairs = meta::boolean::value>; + template + using has_traits_ipairs = meta::boolean::value>; + template using has_traits_add = meta::boolean::value>; @@ -405,6 +421,10 @@ namespace sol { return luaL_error(L, "sol: cannot call '__pairs' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } + static int ipairs(lua_State* L) { + return luaL_error(L, "sol: cannot call '__ipairs' on type '%s': it is not recognized as a container", detail::demangle().c_str()); + } + static iterator begin(lua_State* L, T&) { luaL_error(L, "sol: cannot call 'being' on type '%s': it is not recognized as a container", detail::demangle().c_str()); return lua_nil; @@ -457,8 +477,9 @@ namespace sol { struct iter { T& source; iterator it; + std::size_t i; - iter(T& source, iterator it) : source(source), it(std::move(it)) {} + iter(T& source, iterator it) : source(source), it(std::move(it)), i(0) {} }; static auto& get_src(lua_State* L) { @@ -939,6 +960,7 @@ namespace sol { erase_has(has_erase(), L, self, key); } + template static int next_associative(std::true_type, lua_State* L) { iter& i = stack::get>(L, 1); auto& source = i.source; @@ -947,20 +969,28 @@ namespace sol { return 0; } int p; - p = stack::push_reference(L, it->first); + if (ip) { + p = stack::push_reference(L, it->first); + } + else { + ++i.i; + p = stack::push_reference(L, i.i); + } p += stack::stack_detail::push_reference(L, detail::deref(it->second)); std::advance(it, 1); return p; } + template static int pairs_associative(std::true_type, lua_State* L) { auto& src = get_src(L); - stack::push(L, next); + stack::push(L, next); stack::push>(L, src, deferred_traits::begin(L, src)); stack::push(L, sol::lua_nil); return 3; } + template static int next_associative(std::false_type, lua_State* L) { iter& i = stack::get>(L, 1); auto& source = i.source; @@ -976,16 +1006,18 @@ namespace sol { return p; } + template static int pairs_associative(std::false_type, lua_State* L) { auto& src = get_src(L); - stack::push(L, next); + stack::push(L, next); stack::push>(L, src, deferred_traits::begin(L, src)); stack::push(L, 0); return 3; } + template static int next(lua_State* L) { - return next_associative(is_associative(), L); + return next_associative(is_associative(), L); } public: @@ -1065,7 +1097,11 @@ namespace sol { } static int pairs(lua_State* L) { - return pairs_associative(is_associative(), L); + return pairs_associative(is_associative(), L); + } + + static int ipairs(lua_State* L) { + return pairs_associative(is_associative(), L); } }; diff --git a/sol/container_usertype_metatable.hpp b/sol/container_usertype_metatable.hpp index 1fcc20d8..80c2c8fc 100644 --- a/sol/container_usertype_metatable.hpp +++ b/sol/container_usertype_metatable.hpp @@ -114,6 +114,18 @@ namespace sol { return real_pairs_traits(container_detail::has_traits_pairs(), L); } + static int real_ipairs_traits(std::true_type, lua_State* L) { + return traits::ipairs(L); + } + + static int real_ipairs_traits(std::false_type, lua_State* L) { + return default_traits::ipairs(L); + } + + static int real_ipairs_call(lua_State* L) { + return real_ipairs_traits(container_detail::has_traits_ipairs(), L); + } + static int real_size_traits(std::true_type, lua_State* L) { return traits::size(L); } @@ -230,6 +242,10 @@ namespace sol { return detail::typed_static_trampoline(L); } + static int ipairs_call(lua_State*L) { + return detail::typed_static_trampoline(L); + } + static int get_call(lua_State*L) { return detail::typed_static_trampoline(L); } @@ -263,7 +279,7 @@ namespace sol { static const char* metakey = is_shim ? &usertype_traits>>::metatable()[0] : &usertype_traits::metatable()[0]; static const std::array reg = { { { "__pairs", &meta_cumt::pairs_call }, - { "__ipairs", &meta_cumt::pairs_call }, + { "__ipairs", &meta_cumt::ipairs_call }, { "__len", &meta_cumt::length_call }, { "__index", &meta_cumt::index_call }, { "__newindex", &meta_cumt::new_index_call }, diff --git a/sol/simple_usertype_metatable.hpp b/sol/simple_usertype_metatable.hpp index 0a4cba46..27a1d46c 100644 --- a/sol/simple_usertype_metatable.hpp +++ b/sol/simple_usertype_metatable.hpp @@ -184,7 +184,7 @@ namespace sol { void* baseclasscast; bool mustindex; bool secondarymeta; - std::array properties; + std::array properties; template void insert(N&& n, object&& o) { diff --git a/sol/stack.hpp b/sol/stack.hpp index 18af8a22..3b2fe08a 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -36,6 +36,8 @@ namespace sol { namespace detail { + using typical_chunk_name_t = char[32]; + inline const std::string& default_chunk_name() { static const std::string name = ""; return name; @@ -211,7 +213,7 @@ namespace sol { } inline void script(lua_State* L, const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { - char basechunkname[17] = {}; + detail::typical_chunk_name_t basechunkname = {}; const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); if (luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) { lua_error(L); diff --git a/sol/state_view.hpp b/sol/state_view.hpp index e0fff2b5..fd90425c 100644 --- a/sol/state_view.hpp +++ b/sol/state_view.hpp @@ -277,7 +277,7 @@ namespace sol { template 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) { - char basechunkname[17] = {}; + detail::typical_chunk_name_t basechunkname = {}; const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())); if (x != load_status::ok) { @@ -300,7 +300,7 @@ namespace sol { } protected_function_result do_string(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { - char basechunkname[17] = {}; + detail::typical_chunk_name_t basechunkname = {}; const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())); if (x != load_status::ok) { @@ -375,7 +375,7 @@ namespace sol { template function_result unsafe_script(const string_view& code, const sol::basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { - char basechunkname[17] = {}; + detail::typical_chunk_name_t basechunkname = {}; const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); int index = lua_gettop(L); if (luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())) { @@ -467,7 +467,7 @@ namespace sol { } #endif load_result load(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { - char basechunkname[17] = {}; + detail::typical_chunk_name_t basechunkname = {}; const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())); return load_result(L, absolute_index(L, -1), 1, 1, x); @@ -483,7 +483,7 @@ namespace sol { } load_result load(lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { - char basechunkname[17] = {}; + detail::typical_chunk_name_t basechunkname = {}; const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname); #if SOL_LUA_VERSION > 501 load_status x = static_cast(lua_load(L, reader, data, chunknametarget, to_string(mode).c_str())); @@ -638,6 +638,12 @@ namespace sol { return *this; } + template + state_view& new_enum(const std::string& name, std::initializer_list> items) { + global.new_enum(name, std::move(items)); + return *this; + } + template void for_each(Fx&& fx) { global.for_each(std::forward(fx)); diff --git a/sol/table_core.hpp b/sol/table_core.hpp index 2077bf35..712a9964 100644 --- a/sol/table_core.hpp +++ b/sol/table_core.hpp @@ -76,85 +76,85 @@ namespace sol { } } - template + template auto tuple_get(types, std::index_sequence<0, 1, I...>, Keys&& keys) const -> decltype(stack::pop>(nullptr)) { typedef decltype(stack::pop>(nullptr)) Tup; return Tup( - traverse_get_optional(meta::is_optional>(), detail::forward_get<0>(keys)), - traverse_get_optional(meta::is_optional>(), detail::forward_get<1>(keys)), - traverse_get_optional(meta::is_optional>(), detail::forward_get(keys))... + traverse_get_optional(meta::is_optional>(), detail::forward_get<0>(keys)), + traverse_get_optional(meta::is_optional>(), detail::forward_get<1>(keys)), + traverse_get_optional(meta::is_optional>(), detail::forward_get(keys))... ); } - template + template decltype(auto) tuple_get(types, std::index_sequence, Keys&& keys) const { - return traverse_get_optional(meta::is_optional>(), detail::forward_get(keys)); + return traverse_get_optional(meta::is_optional>(), detail::forward_get(keys)); } - template + template void tuple_set(std::index_sequence, Pairs&& pairs) { auto pp = stack::push_pop(pairs))...>::value)>(*this); - void(detail::swallow{ (stack::set_field(base_t::lua_state(), + void(detail::swallow{ (stack::set_field(base_t::lua_state(), detail::forward_get(pairs), detail::forward_get(pairs), lua_gettop(base_t::lua_state()) ), 0)... }); } - template + template decltype(auto) traverse_get_deep(Key&& key) const { - stack::get_field(base_t::lua_state(), std::forward(key)); + stack::get_field(base_t::lua_state(), std::forward(key)); return stack::get(base_t::lua_state()); } - template + template decltype(auto) traverse_get_deep(Key&& key, Keys&&... keys) const { - stack::get_field(base_t::lua_state(), std::forward(key)); - return traverse_get_deep(std::forward(keys)...); + stack::get_field(base_t::lua_state(), std::forward(key)); + return traverse_get_deep(std::forward(keys)...); } - template + template decltype(auto) traverse_get_deep_optional(int& popcount, Key&& key) const { typedef decltype(stack::get(base_t::lua_state())) R; - auto p = stack::probe_get_field(base_t::lua_state(), std::forward(key), lua_gettop(base_t::lua_state())); + auto p = stack::probe_get_field(base_t::lua_state(), std::forward(key), lua_gettop(base_t::lua_state())); popcount += p.levels; if (!p.success) return R(nullopt); return stack::get(base_t::lua_state()); } - template + template decltype(auto) traverse_get_deep_optional(int& popcount, Key&& key, Keys&&... keys) const { auto p = I > 0 ? stack::probe_get_field(base_t::lua_state(), std::forward(key), -1) : stack::probe_get_field(base_t::lua_state(), std::forward(key), lua_gettop(base_t::lua_state())); popcount += p.levels; if (!p.success) return T(nullopt); - return traverse_get_deep_optional(popcount, std::forward(keys)...); + return traverse_get_deep_optional(popcount, std::forward(keys)...); } - template + template decltype(auto) traverse_get_optional(std::false_type, Keys&&... keys) const { detail::clean c(base_t::lua_state()); - return traverse_get_deep(std::forward(keys)...); + return traverse_get_deep(std::forward(keys)...); } - template + template decltype(auto) traverse_get_optional(std::true_type, Keys&&... keys) const { int popcount = 0; detail::ref_clean c(base_t::lua_state(), popcount); - return traverse_get_deep_optional(popcount, std::forward(keys)...); + return traverse_get_deep_optional(popcount, std::forward(keys)...); } - template + template void traverse_set_deep(Key&& key, Value&& value) const { - stack::set_field(base_t::lua_state(), std::forward(key), std::forward(value)); + stack::set_field(base_t::lua_state(), std::forward(key), std::forward(value)); } - template + template void traverse_set_deep(Key&& key, Keys&&... keys) const { - stack::get_field(base_t::lua_state(), std::forward(key)); - traverse_set_deep(std::forward(keys)...); + stack::get_field(base_t::lua_state(), std::forward(key)); + traverse_set_deep(std::forward(keys)...); } basic_table_core(lua_State* L, detail::global_tag t) noexcept : base_t(L, t) { } @@ -227,7 +227,7 @@ namespace sol { decltype(auto) get(Keys&&... keys) const { static_assert(sizeof...(Keys) == sizeof...(Ret), "number of keys and number of return types do not match"); auto pp = stack::push_pop::value>(*this); - return tuple_get(types(), std::make_index_sequence(), std::forward_as_tuple(std::forward(keys)...)); + return tuple_get(types(), std::make_index_sequence(), std::forward_as_tuple(std::forward(keys)...)); } template @@ -252,20 +252,66 @@ namespace sol { template decltype(auto) traverse_get(Keys&&... keys) const { auto pp = stack::push_pop::value>(*this); - return traverse_get_optional(meta::is_optional>(), std::forward(keys)...); + return traverse_get_optional(meta::is_optional>(), std::forward(keys)...); } template basic_table_core& traverse_set(Keys&&... keys) { auto pp = stack::push_pop::value>(*this); auto pn = stack::pop_n(base_t::lua_state(), static_cast(sizeof...(Keys)-2)); - traverse_set_deep(std::forward(keys)...); + traverse_set_deep(std::forward(keys)...); return *this; } template basic_table_core& set(Args&&... args) { - tuple_set(std::make_index_sequence(), std::forward_as_tuple(std::forward(args)...)); + tuple_set(std::make_index_sequence(), std::forward_as_tuple(std::forward(args)...)); + return *this; + } + + template + decltype(auto) raw_get(Keys&&... keys) const { + static_assert(sizeof...(Keys) == sizeof...(Ret), "number of keys and number of return types do not match"); + auto pp = stack::push_pop::value>(*this); + return tuple_get(types(), std::make_index_sequence(), std::forward_as_tuple(std::forward(keys)...)); + } + + template + decltype(auto) raw_get_or(Key&& key, T&& otherwise) const { + typedef decltype(raw_get("")) U; + optional option = raw_get>(std::forward(key)); + if (option) { + return static_cast(option.value()); + } + return static_cast(std::forward(otherwise)); + } + + template + decltype(auto) raw_get_or(Key&& key, D&& otherwise) const { + optional option = raw_get>(std::forward(key)); + if (option) { + return static_cast(option.value()); + } + return static_cast(std::forward(otherwise)); + } + + template + decltype(auto) traverse_raw_get(Keys&&... keys) const { + auto pp = stack::push_pop::value>(*this); + return traverse_get_optional(meta::is_optional>(), std::forward(keys)...); + } + + template + basic_table_core& traverse_raw_set(Keys&&... keys) { + auto pp = stack::push_pop::value>(*this); + auto pn = stack::pop_n(base_t::lua_state(), static_cast(sizeof...(Keys)-2)); + traverse_set_deep(std::forward(keys)...); + return *this; + } + + template + basic_table_core& raw_set(Args&&... args) { + tuple_set(std::make_index_sequence(), std::forward_as_tuple(std::forward(args)...)); return *this; } @@ -338,20 +384,40 @@ namespace sol { } template - basic_table_core& new_enum(const std::string& name, Args&&... args) { + table new_enum(const string_view& name, Args&&... args) { + table target = create_with(std::forward(args)...); if (read_only) { - table idx = create_with(std::forward(args)...); table x = create_with( meta_function::new_index, detail::fail_on_newindex, - meta_function::index, idx + meta_function::index, target ); - table target = create_named(name); - target[metatable_key] = x; + table shim = create_named(name, metatable_key, x); + return shim; } else { - create_named(name, std::forward(args)...); + set(name, target); + return target; + } + } + + template + table new_enum(const string_view& name, std::initializer_list> items) { + table target = create(items.size(), 0); + for (const auto& kvp : items) { + target.set(kvp.first, kvp.second); + } + if (read_only) { + table x = create_with( + meta_function::new_index, detail::fail_on_newindex, + meta_function::index, target + ); + table shim = create_named(name, metatable_key, x); + return shim; + } + else { + set(name, target); + return target; } - return *this; } template @@ -482,11 +548,7 @@ namespace sol { template table create_named(Name&& name, Args&&... args) { static const int narr = static_cast(meta::count_2_for_pack::value); - return create(std::forward(name), narr, sizeof...(Args) / 2 - narr, std::forward(args)...); - } - - ~basic_table_core() { - + return create(std::forward(name), narr, (sizeof...(Args) / 2) - narr, std::forward(args)...); } }; } // sol diff --git a/sol/types.hpp b/sol/types.hpp index c06cb915..ee85477f 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -617,6 +617,7 @@ namespace sol { bitwise_or, bitwise_xor, pairs, + ipairs, next, type, type_info, @@ -624,8 +625,8 @@ namespace sol { typedef meta_function meta_method; - inline const std::array& meta_function_names() { - static const std::array names = { { + inline const std::array& meta_function_names() { + static const std::array names = { { "new", "__index", "__newindex", @@ -656,6 +657,7 @@ namespace sol { "__bxor", "__pairs", + "__ipairs", "__next", "__type", "__typeinfo" diff --git a/sol/usertype_metatable.hpp b/sol/usertype_metatable.hpp index 00cef1e3..2564843e 100644 --- a/sol/usertype_metatable.hpp +++ b/sol/usertype_metatable.hpp @@ -394,7 +394,7 @@ namespace sol { void* baseclasscheck; void* baseclasscast; bool secondarymeta; - std::array properties; + std::array properties; template >> = meta::enabler> lua_CFunction make_func() const { diff --git a/tests/test_tables.cpp b/tests/test_tables.cpp index c11baae5..6d31fa7c 100644 --- a/tests/test_tables.cpp +++ b/tests/test_tables.cpp @@ -121,22 +121,42 @@ TEST_CASE("tables/new_enum", "Making sure enums can be put in and gotten out as right }; - sol::state lua; - lua.open_libraries(sol::lib::base); + SECTION("variadics") { + sol::state lua; + lua.open_libraries(sol::lib::base); - lua.new_enum( "direction", - "up", direction::up, - "down", direction::down, - "left", direction::left, - "right", direction::right - ); + lua.new_enum("direction", + "up", direction::up, + "down", direction::down, + "left", direction::left, + "right", direction::right + ); - direction d = lua["direction"]["left"]; - REQUIRE(d == direction::left); - auto result = lua.safe_script("direction.left = 50", sol::script_pass_on_error); - REQUIRE_FALSE(result.valid()); - d = lua["direction"]["left"]; - REQUIRE(d == direction::left); + direction d = lua["direction"]["left"]; + REQUIRE(d == direction::left); + auto result = lua.safe_script("direction.left = 50", sol::script_pass_on_error); + REQUIRE_FALSE(result.valid()); + d = lua["direction"]["left"]; + REQUIRE(d == direction::left); + } + SECTION("initializer_list") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_enum("direction", { + { "up", direction::up }, + { "down", direction::down }, + { "left", direction::left }, + { "right", direction::right } + } ); + + direction d = lua["direction"]["left"]; + REQUIRE(d == direction::left); + auto result = lua.safe_script("direction.left = 50", sol::script_pass_on_error); + REQUIRE_FALSE(result.valid()); + d = lua["direction"]["left"]; + REQUIRE(d == direction::left); + } } TEST_CASE("tables/for_each", "Testing the use of for_each to get values from a lua table") { @@ -536,9 +556,9 @@ TEST_CASE("tables/add", "Basic test to make sure the 'add' feature works") { sol::state lua; sol::table t = lua.create_table(sz, 0); - std::vector bigvec( sz ); + std::vector bigvec(sz); std::iota(bigvec.begin(), bigvec.end(), 1); - + for (std::size_t i = 0; i < bigvec.size(); ++i) { t.add(bigvec[i]); } @@ -548,6 +568,18 @@ 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.raw_set("a", 2.5); + double la = t.raw_get("a"); + REQUIRE(la == 2.5); +} + TEST_CASE("tables/boolean keys", "make sure boolean keys don't get caught up in `is_integral` specializations") { sol::state lua; lua.open_libraries(sol::lib::base);