create and test raw_set and raw_get

add `new_enum` overload for initializer lists
add ipairs overload for 5.2 users
This commit is contained in:
ThePhD 2017-09-06 15:09:51 -04:00
parent c523c67f9c
commit fb276676d1
14 changed files with 289 additions and 86 deletions

1
.gitignore vendored
View File

@ -81,3 +81,4 @@ temp.bad_syntax.lua
temp.good.lua
catch_mock.hpp
main_aux.cpp
main.hpp

View File

@ -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<function-result>`/:ref:`sol::protected_function_result<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;
});
}

View File

@ -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<typename... Args, typename... Keys>
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<sol::optional<int>>( std::tie("a", "b", "c" ) );``, and then use the :doc:`optional<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<typename... Args, typename... Keys>
decltype(auto) raw_get(Keys&&... keys) const;
template<typename T, typename... Keys>
decltype(auto) traverse_raw_get(Keys&&... keys) const;
template<typename T, typename Key>
decltype(auto) raw_get_or(Key&& key, T&& otherwise) const;
template<typename T, typename Key, typename D>
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<typename... Args>
table& raw_set(Args&&... args);
template<typename... Args>
table& traverse_raw_set(Args&&... args);
Similar to :ref:`set<set-value>`, 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<function>` behaves with its call operator. Also note that this does not detect callables by default: see the :ref:`note here<binding-callable-objects>`.
@ -124,9 +154,13 @@ This class of functions creates a new :doc:`simple usertype<simple_usertype>` wi
template<bool read_only = true, typename... Args>
basic_table_core& new_enum(const std::string& name, Args&&... args);
template<typename T, bool read_only = true>
basic_table_core& new_enum(const std::string& name, std::initializer_list<std::pair<string_view, T>> 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.
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>( "my_enum", { {"a", my_enum:: a}, { "b", my_enum::b } } );``.
.. _set_usertype:
.. code-block:: cpp
@ -176,7 +210,7 @@ A functional ``for_each`` loop that calls the desired function. The passed in fu
template<typename T>
proxy<const table&, T> operator[](T&& key) const;
Generates a :doc:`proxy<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<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

View File

@ -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);

View File

@ -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<api/as_table>` and :doc:`sol::nested<api/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<api/as_table>` and :doc:`sol::nested<api/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:

View File

@ -246,6 +246,19 @@ namespace sol {
static const bool value = sizeof(test<T>(0)) == sizeof(char);
};
template <typename T>
struct has_traits_ipairs_test {
private:
typedef std::array<char, 1> one;
typedef std::array<char, 2> two;
template <typename C> static one test(decltype(&C::ipairs));
template <typename C> static two test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(char);
};
template <typename T>
struct has_traits_add_test {
private:
@ -289,6 +302,9 @@ namespace sol {
template <typename T>
using has_traits_pairs = meta::boolean<has_traits_pairs_test<T>::value>;
template <typename T>
using has_traits_ipairs = meta::boolean<has_traits_ipairs_test<T>::value>;
template <typename T>
using has_traits_add = meta::boolean<has_traits_add_test<T>::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<T>().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<T>().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<T>().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<T>(), L, self, key);
}
template <bool ip>
static int next_associative(std::true_type, lua_State* L) {
iter& i = stack::get<user<iter>>(L, 1);
auto& source = i.source;
@ -947,20 +969,28 @@ namespace sol {
return 0;
}
int p;
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<push_type>(L, detail::deref(it->second));
std::advance(it, 1);
return p;
}
template <bool ip>
static int pairs_associative(std::true_type, lua_State* L) {
auto& src = get_src(L);
stack::push(L, next);
stack::push(L, next<ip>);
stack::push<user<iter>>(L, src, deferred_traits::begin(L, src));
stack::push(L, sol::lua_nil);
return 3;
}
template <bool>
static int next_associative(std::false_type, lua_State* L) {
iter& i = stack::get<user<iter>>(L, 1);
auto& source = i.source;
@ -976,16 +1006,18 @@ namespace sol {
return p;
}
template <bool ip>
static int pairs_associative(std::false_type, lua_State* L) {
auto& src = get_src(L);
stack::push(L, next);
stack::push(L, next<ip>);
stack::push<user<iter>>(L, src, deferred_traits::begin(L, src));
stack::push(L, 0);
return 3;
}
template <bool ip>
static int next(lua_State* L) {
return next_associative(is_associative(), L);
return next_associative<ip>(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<false>(is_associative(), L);
}
static int ipairs(lua_State* L) {
return pairs_associative<true>(is_associative(), L);
}
};

View File

@ -114,6 +114,18 @@ namespace sol {
return real_pairs_traits(container_detail::has_traits_pairs<traits>(), 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<traits>(), 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<decltype(&real_pairs_call), (&real_pairs_call)>(L);
}
static int ipairs_call(lua_State*L) {
return detail::typed_static_trampoline<decltype(&real_ipairs_call), (&real_ipairs_call)>(L);
}
static int get_call(lua_State*L) {
return detail::typed_static_trampoline<decltype(&real_get_call), (&real_get_call)>(L);
}
@ -263,7 +279,7 @@ namespace sol {
static const char* metakey = is_shim ? &usertype_traits<as_container_t<std::remove_pointer_t<T>>>::metatable()[0] : &usertype_traits<T>::metatable()[0];
static const std::array<luaL_Reg, 16> 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 },

View File

@ -184,7 +184,7 @@ namespace sol {
void* baseclasscast;
bool mustindex;
bool secondarymeta;
std::array<bool, 31> properties;
std::array<bool, 32> properties;
template <typename N>
void insert(N&& n, object&& o) {

View File

@ -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);

View File

@ -277,7 +277,7 @@ namespace sol {
template <typename E>
protected_function_result do_string(const string_view& code, const basic_environment<E>& 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<load_status>(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<load_status>(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 <typename E>
function_result unsafe_script(const string_view& code, const sol::basic_environment<E>& 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<load_status>(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<load_status>(lua_load(L, reader, data, chunknametarget, to_string(mode).c_str()));
@ -638,6 +638,12 @@ namespace sol {
return *this;
}
template<typename T, bool read_only = true>
state_view& new_enum(const std::string& name, std::initializer_list<std::pair<string_view, T>> items) {
global.new_enum<T, read_only>(name, std::move(items));
return *this;
}
template <typename Fx>
void for_each(Fx&& fx) {
global.for_each(std::forward<Fx>(fx));

View File

@ -76,85 +76,85 @@ namespace sol {
}
}
template<typename Ret0, typename Ret1, typename... Ret, std::size_t... I, typename Keys>
template<bool raw, typename Ret0, typename Ret1, typename... Ret, std::size_t... I, typename Keys>
auto tuple_get(types<Ret0, Ret1, Ret...>, std::index_sequence<0, 1, I...>, Keys&& keys) const
-> decltype(stack::pop<std::tuple<Ret0, Ret1, Ret...>>(nullptr)) {
typedef decltype(stack::pop<std::tuple<Ret0, Ret1, Ret...>>(nullptr)) Tup;
return Tup(
traverse_get_optional<top_level, Ret0>(meta::is_optional<meta::unqualified_t<Ret0>>(), detail::forward_get<0>(keys)),
traverse_get_optional<top_level, Ret1>(meta::is_optional<meta::unqualified_t<Ret1>>(), detail::forward_get<1>(keys)),
traverse_get_optional<top_level, Ret>(meta::is_optional<meta::unqualified_t<Ret>>(), detail::forward_get<I>(keys))...
traverse_get_optional<top_level, raw, Ret0>(meta::is_optional<meta::unqualified_t<Ret0>>(), detail::forward_get<0>(keys)),
traverse_get_optional<top_level, raw, Ret1>(meta::is_optional<meta::unqualified_t<Ret1>>(), detail::forward_get<1>(keys)),
traverse_get_optional<top_level, raw, Ret>(meta::is_optional<meta::unqualified_t<Ret>>(), detail::forward_get<I>(keys))...
);
}
template<typename Ret, std::size_t I, typename Keys>
template<bool raw, typename Ret, std::size_t I, typename Keys>
decltype(auto) tuple_get(types<Ret>, std::index_sequence<I>, Keys&& keys) const {
return traverse_get_optional<top_level, Ret>(meta::is_optional<meta::unqualified_t<Ret>>(), detail::forward_get<I>(keys));
return traverse_get_optional<top_level, raw, Ret>(meta::is_optional<meta::unqualified_t<Ret>>(), detail::forward_get<I>(keys));
}
template<typename Pairs, std::size_t... I>
template<bool raw, typename Pairs, std::size_t... I>
void tuple_set(std::index_sequence<I...>, Pairs&& pairs) {
auto pp = stack::push_pop<top_level && (is_global<decltype(detail::forward_get<I * 2>(pairs))...>::value)>(*this);
void(detail::swallow{ (stack::set_field<top_level>(base_t::lua_state(),
void(detail::swallow{ (stack::set_field<top_level, raw>(base_t::lua_state(),
detail::forward_get<I * 2>(pairs),
detail::forward_get<I * 2 + 1>(pairs),
lua_gettop(base_t::lua_state())
), 0)... });
}
template <bool global, typename T, typename Key>
template <bool global, bool raw, typename T, typename Key>
decltype(auto) traverse_get_deep(Key&& key) const {
stack::get_field<global>(base_t::lua_state(), std::forward<Key>(key));
stack::get_field<global, raw>(base_t::lua_state(), std::forward<Key>(key));
return stack::get<T>(base_t::lua_state());
}
template <bool global, typename T, typename Key, typename... Keys>
template <bool global, bool raw, typename T, typename Key, typename... Keys>
decltype(auto) traverse_get_deep(Key&& key, Keys&&... keys) const {
stack::get_field<global>(base_t::lua_state(), std::forward<Key>(key));
return traverse_get_deep<false, T>(std::forward<Keys>(keys)...);
stack::get_field<global, raw>(base_t::lua_state(), std::forward<Key>(key));
return traverse_get_deep<false, raw, T>(std::forward<Keys>(keys)...);
}
template <bool global, typename T, std::size_t I, typename Key>
template <bool global, bool raw, typename T, std::size_t I, typename Key>
decltype(auto) traverse_get_deep_optional(int& popcount, Key&& key) const {
typedef decltype(stack::get<T>(base_t::lua_state())) R;
auto p = stack::probe_get_field<global>(base_t::lua_state(), std::forward<Key>(key), lua_gettop(base_t::lua_state()));
auto p = stack::probe_get_field<global, raw>(base_t::lua_state(), std::forward<Key>(key), lua_gettop(base_t::lua_state()));
popcount += p.levels;
if (!p.success)
return R(nullopt);
return stack::get<T>(base_t::lua_state());
}
template <bool global, typename T, std::size_t I, typename Key, typename... Keys>
template <bool global, bool raw, typename T, std::size_t I, typename Key, typename... Keys>
decltype(auto) traverse_get_deep_optional(int& popcount, Key&& key, Keys&&... keys) const {
auto p = I > 0 ? stack::probe_get_field<global>(base_t::lua_state(), std::forward<Key>(key), -1) : stack::probe_get_field<global>(base_t::lua_state(), std::forward<Key>(key), lua_gettop(base_t::lua_state()));
popcount += p.levels;
if (!p.success)
return T(nullopt);
return traverse_get_deep_optional<false, T, I + 1>(popcount, std::forward<Keys>(keys)...);
return traverse_get_deep_optional<false, raw, T, I + 1>(popcount, std::forward<Keys>(keys)...);
}
template <bool global, typename T, typename... Keys>
template <bool global, bool raw, typename T, typename... Keys>
decltype(auto) traverse_get_optional(std::false_type, Keys&&... keys) const {
detail::clean<sizeof...(Keys)> c(base_t::lua_state());
return traverse_get_deep<top_level, T>(std::forward<Keys>(keys)...);
return traverse_get_deep<global, raw, T>(std::forward<Keys>(keys)...);
}
template <bool global, typename T, typename... Keys>
template <bool global, bool raw, typename T, typename... Keys>
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<top_level, T, 0>(popcount, std::forward<Keys>(keys)...);
return traverse_get_deep_optional<global, raw, T, 0>(popcount, std::forward<Keys>(keys)...);
}
template <bool global, typename Key, typename Value>
template <bool global, bool raw, typename Key, typename Value>
void traverse_set_deep(Key&& key, Value&& value) const {
stack::set_field<global>(base_t::lua_state(), std::forward<Key>(key), std::forward<Value>(value));
stack::set_field<global, raw>(base_t::lua_state(), std::forward<Key>(key), std::forward<Value>(value));
}
template <bool global, typename Key, typename... Keys>
template <bool global, bool raw, typename Key, typename... Keys>
void traverse_set_deep(Key&& key, Keys&&... keys) const {
stack::get_field<global>(base_t::lua_state(), std::forward<Key>(key));
traverse_set_deep<false>(std::forward<Keys>(keys)...);
stack::get_field<global, raw>(base_t::lua_state(), std::forward<Key>(key));
traverse_set_deep<false, raw>(std::forward<Keys>(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<is_global<Keys...>::value>(*this);
return tuple_get(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), std::forward_as_tuple(std::forward<Keys>(keys)...));
return tuple_get<false>(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), std::forward_as_tuple(std::forward<Keys>(keys)...));
}
template<typename T, typename Key>
@ -252,20 +252,66 @@ namespace sol {
template <typename T, typename... Keys>
decltype(auto) traverse_get(Keys&&... keys) const {
auto pp = stack::push_pop<is_global<Keys...>::value>(*this);
return traverse_get_optional<top_level, T>(meta::is_optional<meta::unqualified_t<T>>(), std::forward<Keys>(keys)...);
return traverse_get_optional<false, top_level, T>(meta::is_optional<meta::unqualified_t<T>>(), std::forward<Keys>(keys)...);
}
template <typename... Keys>
basic_table_core& traverse_set(Keys&&... keys) {
auto pp = stack::push_pop<is_global<Keys...>::value>(*this);
auto pn = stack::pop_n(base_t::lua_state(), static_cast<int>(sizeof...(Keys)-2));
traverse_set_deep<top_level>(std::forward<Keys>(keys)...);
traverse_set_deep<false, top_level>(std::forward<Keys>(keys)...);
return *this;
}
template<typename... Args>
basic_table_core& set(Args&&... args) {
tuple_set(std::make_index_sequence<sizeof...(Args) / 2>(), std::forward_as_tuple(std::forward<Args>(args)...));
tuple_set<false>(std::make_index_sequence<sizeof...(Args) / 2>(), std::forward_as_tuple(std::forward<Args>(args)...));
return *this;
}
template<typename... Ret, typename... Keys>
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<is_global<Keys...>::value>(*this);
return tuple_get<false>(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), std::forward_as_tuple(std::forward<Keys>(keys)...));
}
template<typename T, typename Key>
decltype(auto) raw_get_or(Key&& key, T&& otherwise) const {
typedef decltype(raw_get<T>("")) U;
optional<U> option = raw_get<optional<U>>(std::forward<Key>(key));
if (option) {
return static_cast<U>(option.value());
}
return static_cast<U>(std::forward<T>(otherwise));
}
template<typename T, typename Key, typename D>
decltype(auto) raw_get_or(Key&& key, D&& otherwise) const {
optional<T> option = raw_get<optional<T>>(std::forward<Key>(key));
if (option) {
return static_cast<T>(option.value());
}
return static_cast<T>(std::forward<D>(otherwise));
}
template <typename T, typename... Keys>
decltype(auto) traverse_raw_get(Keys&&... keys) const {
auto pp = stack::push_pop<is_global<Keys...>::value>(*this);
return traverse_get_optional<true, top_level, T>(meta::is_optional<meta::unqualified_t<T>>(), std::forward<Keys>(keys)...);
}
template <typename... Keys>
basic_table_core& traverse_raw_set(Keys&&... keys) {
auto pp = stack::push_pop<is_global<Keys...>::value>(*this);
auto pn = stack::pop_n(base_t::lua_state(), static_cast<int>(sizeof...(Keys)-2));
traverse_set_deep<true, top_level>(std::forward<Keys>(keys)...);
return *this;
}
template<typename... Args>
basic_table_core& raw_set(Args&&... args) {
tuple_set<true>(std::make_index_sequence<sizeof...(Args) / 2>(), std::forward_as_tuple(std::forward<Args>(args)...));
return *this;
}
@ -338,20 +384,40 @@ namespace sol {
}
template<bool read_only = true, typename... Args>
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>(args)...);
if (read_only) {
table idx = create_with(std::forward<Args>(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>(args)...);
set(name, target);
return target;
}
}
template<typename T, bool read_only = true>
table new_enum(const string_view& name, std::initializer_list<std::pair<string_view, T>> 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<typename Fx>
@ -482,11 +548,7 @@ namespace sol {
template <typename Name, typename... Args>
table create_named(Name&& name, Args&&... args) {
static const int narr = static_cast<int>(meta::count_2_for_pack<std::is_integral, Args...>::value);
return create(std::forward<Name>(name), narr, sizeof...(Args) / 2 - narr, std::forward<Args>(args)...);
}
~basic_table_core() {
return create(std::forward<Name>(name), narr, (sizeof...(Args) / 2) - narr, std::forward<Args>(args)...);
}
};
} // sol

View File

@ -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<std::string, 31>& meta_function_names() {
static const std::array<std::string, 31> names = { {
inline const std::array<std::string, 32>& meta_function_names() {
static const std::array<std::string, 32> names = { {
"new",
"__index",
"__newindex",
@ -656,6 +657,7 @@ namespace sol {
"__bxor",
"__pairs",
"__ipairs",
"__next",
"__type",
"__typeinfo"

View File

@ -394,7 +394,7 @@ namespace sol {
void* baseclasscheck;
void* baseclasscast;
bool secondarymeta;
std::array<bool, 31> properties;
std::array<bool, 32> properties;
template <std::size_t Idx, meta::enable<std::is_same<lua_CFunction, meta::unqualified_tuple_element<Idx + 1, RawTuple>>> = meta::enabler>
lua_CFunction make_func() const {

View File

@ -121,10 +121,11 @@ TEST_CASE("tables/new_enum", "Making sure enums can be put in and gotten out as
right
};
SECTION("variadics") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_enum( "direction",
lua.new_enum("direction",
"up", direction::up,
"down", direction::down,
"left", direction::left,
@ -137,6 +138,25 @@ TEST_CASE("tables/new_enum", "Making sure enums can be put in and gotten out as
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>("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,7 +556,7 @@ 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<int> bigvec( sz );
std::vector<int> bigvec(sz);
std::iota(bigvec.begin(), bigvec.end(), 1);
for (std::size_t i = 0; i < bigvec.size(); ++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<double>("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);