mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
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:
parent
c523c67f9c
commit
fb276676d1
1
.gitignore
vendored
1
.gitignore
vendored
@ -81,3 +81,4 @@ temp.bad_syntax.lua
|
||||
temp.good.lua
|
||||
catch_mock.hpp
|
||||
main_aux.cpp
|
||||
main.hpp
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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 },
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user