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 temp.good.lua
catch_mock.hpp catch_mock.hpp
main_aux.cpp 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. 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 .. code-block:: cpp
:caption: running code safely :caption: running code safely
@ -89,14 +89,19 @@ To handle errors when using the second overload, provide a callable function/obj
int main () { int main () {
sol::state lua; 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"); 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) { 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 // 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, // the user can do whatever they like here, including throw. Otherwise...
// they need to return the protected_function_result 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; 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 .. code-block:: cpp
:caption: function: get / traversing get :caption: function: get / traversing get
:name: get-value
template<typename... Args, typename... Keys> template<typename... Args, typename... Keys>
decltype(auto) get(Keys&&... keys) const; 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. 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 .. code-block:: cpp
:caption: function: set / traversing set :caption: function: set / traversing set
:name: set-value :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;``. 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:: .. 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>`. 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,8 +154,12 @@ This class of functions creates a new :doc:`simple usertype<simple_usertype>` wi
template<bool read_only = true, typename... Args> template<bool read_only = true, typename... Args>
basic_table_core& new_enum(const std::string& name, Args&&... 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. 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: .. _set_usertype:
@ -176,7 +210,7 @@ A functional ``for_each`` loop that calls the desired function. The passed in fu
template<typename T> template<typename T>
proxy<const table&, T> operator[](T&& key) const; 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 .. code-block:: cpp
:caption: function: create a table with defaults :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 .. code-block:: cpp
:caption: type checking convenience functions :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); 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:: .. 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: .. _container-detection:
@ -145,10 +145,15 @@ Below are the many container operations and their override points for ``containe
| | | | | - works only in Lua 5.2+ | | | | | | - 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) | | | | | | - 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:: .. 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: .. _container-classifications:

View File

@ -246,6 +246,19 @@ namespace sol {
static const bool value = sizeof(test<T>(0)) == sizeof(char); 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> template <typename T>
struct has_traits_add_test { struct has_traits_add_test {
private: private:
@ -289,6 +302,9 @@ namespace sol {
template <typename T> template <typename T>
using has_traits_pairs = meta::boolean<has_traits_pairs_test<T>::value>; 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> template <typename T>
using has_traits_add = meta::boolean<has_traits_add_test<T>::value>; 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()); 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&) { 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()); 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; return lua_nil;
@ -457,8 +477,9 @@ namespace sol {
struct iter { struct iter {
T& source; T& source;
iterator it; 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) { static auto& get_src(lua_State* L) {
@ -939,6 +960,7 @@ namespace sol {
erase_has(has_erase<T>(), L, self, key); erase_has(has_erase<T>(), L, self, key);
} }
template <bool ip>
static int next_associative(std::true_type, lua_State* L) { static int next_associative(std::true_type, lua_State* L) {
iter& i = stack::get<user<iter>>(L, 1); iter& i = stack::get<user<iter>>(L, 1);
auto& source = i.source; auto& source = i.source;
@ -947,20 +969,28 @@ namespace sol {
return 0; return 0;
} }
int p; 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<push_type>(L, detail::deref(it->second)); p += stack::stack_detail::push_reference<push_type>(L, detail::deref(it->second));
std::advance(it, 1); std::advance(it, 1);
return p; return p;
} }
template <bool ip>
static int pairs_associative(std::true_type, lua_State* L) { static int pairs_associative(std::true_type, lua_State* L) {
auto& src = get_src(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<user<iter>>(L, src, deferred_traits::begin(L, src));
stack::push(L, sol::lua_nil); stack::push(L, sol::lua_nil);
return 3; return 3;
} }
template <bool>
static int next_associative(std::false_type, lua_State* L) { static int next_associative(std::false_type, lua_State* L) {
iter& i = stack::get<user<iter>>(L, 1); iter& i = stack::get<user<iter>>(L, 1);
auto& source = i.source; auto& source = i.source;
@ -976,16 +1006,18 @@ namespace sol {
return p; return p;
} }
template <bool ip>
static int pairs_associative(std::false_type, lua_State* L) { static int pairs_associative(std::false_type, lua_State* L) {
auto& src = get_src(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<user<iter>>(L, src, deferred_traits::begin(L, src));
stack::push(L, 0); stack::push(L, 0);
return 3; return 3;
} }
template <bool ip>
static int next(lua_State* L) { static int next(lua_State* L) {
return next_associative(is_associative(), L); return next_associative<ip>(is_associative(), L);
} }
public: public:
@ -1065,7 +1097,11 @@ namespace sol {
} }
static int pairs(lua_State* L) { 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); 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) { static int real_size_traits(std::true_type, lua_State* L) {
return traits::size(L); return traits::size(L);
} }
@ -230,6 +242,10 @@ namespace sol {
return detail::typed_static_trampoline<decltype(&real_pairs_call), (&real_pairs_call)>(L); 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) { static int get_call(lua_State*L) {
return detail::typed_static_trampoline<decltype(&real_get_call), (&real_get_call)>(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 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 = { { static const std::array<luaL_Reg, 16> reg = { {
{ "__pairs", &meta_cumt::pairs_call }, { "__pairs", &meta_cumt::pairs_call },
{ "__ipairs", &meta_cumt::pairs_call }, { "__ipairs", &meta_cumt::ipairs_call },
{ "__len", &meta_cumt::length_call }, { "__len", &meta_cumt::length_call },
{ "__index", &meta_cumt::index_call }, { "__index", &meta_cumt::index_call },
{ "__newindex", &meta_cumt::new_index_call }, { "__newindex", &meta_cumt::new_index_call },

View File

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

View File

@ -36,6 +36,8 @@
namespace sol { namespace sol {
namespace detail { namespace detail {
using typical_chunk_name_t = char[32];
inline const std::string& default_chunk_name() { inline const std::string& default_chunk_name() {
static const std::string name = ""; static const std::string name = "";
return 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) { 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); 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)) { if (luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) {
lua_error(L); lua_error(L);

View File

@ -277,7 +277,7 @@ namespace sol {
template <typename E> 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) { 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); 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())); 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) { 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) { 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); 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())); 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) { if (x != load_status::ok) {
@ -375,7 +375,7 @@ namespace sol {
template <typename E> 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) { 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); const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname);
int index = lua_gettop(L); int index = lua_gettop(L);
if (luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())) { if (luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())) {
@ -467,7 +467,7 @@ namespace sol {
} }
#endif #endif
load_result load(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { 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); 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())); 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); 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) { 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); const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname);
#if SOL_LUA_VERSION > 501 #if SOL_LUA_VERSION > 501
load_status x = static_cast<load_status>(lua_load(L, reader, data, chunknametarget, to_string(mode).c_str())); 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; 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> template <typename Fx>
void for_each(Fx&& fx) { void for_each(Fx&& fx) {
global.for_each(std::forward<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 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)) { -> decltype(stack::pop<std::tuple<Ret0, Ret1, Ret...>>(nullptr)) {
typedef decltype(stack::pop<std::tuple<Ret0, Ret1, Ret...>>(nullptr)) Tup; typedef decltype(stack::pop<std::tuple<Ret0, Ret1, Ret...>>(nullptr)) Tup;
return 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, raw, 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, raw, 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, 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 { 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) { 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); 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>(pairs),
detail::forward_get<I * 2 + 1>(pairs), detail::forward_get<I * 2 + 1>(pairs),
lua_gettop(base_t::lua_state()) lua_gettop(base_t::lua_state())
), 0)... }); ), 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 { 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()); 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 { decltype(auto) traverse_get_deep(Key&& key, Keys&&... keys) 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 traverse_get_deep<false, T>(std::forward<Keys>(keys)...); 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 { decltype(auto) traverse_get_deep_optional(int& popcount, Key&& key) const {
typedef decltype(stack::get<T>(base_t::lua_state())) R; 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; popcount += p.levels;
if (!p.success) if (!p.success)
return R(nullopt); return R(nullopt);
return stack::get<T>(base_t::lua_state()); 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 { 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())); 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; popcount += p.levels;
if (!p.success) if (!p.success)
return T(nullopt); 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 { decltype(auto) traverse_get_optional(std::false_type, Keys&&... keys) const {
detail::clean<sizeof...(Keys)> c(base_t::lua_state()); 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 { decltype(auto) traverse_get_optional(std::true_type, Keys&&... keys) const {
int popcount = 0; int popcount = 0;
detail::ref_clean c(base_t::lua_state(), popcount); 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 { 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 { void traverse_set_deep(Key&& key, Keys&&... keys) 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));
traverse_set_deep<false>(std::forward<Keys>(keys)...); traverse_set_deep<false, raw>(std::forward<Keys>(keys)...);
} }
basic_table_core(lua_State* L, detail::global_tag t) noexcept : base_t(L, t) { } 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 { decltype(auto) get(Keys&&... keys) const {
static_assert(sizeof...(Keys) == sizeof...(Ret), "number of keys and number of return types do not match"); 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); 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> template<typename T, typename Key>
@ -252,20 +252,66 @@ namespace sol {
template <typename T, typename... Keys> template <typename T, typename... Keys>
decltype(auto) traverse_get(Keys&&... keys) const { decltype(auto) traverse_get(Keys&&... keys) const {
auto pp = stack::push_pop<is_global<Keys...>::value>(*this); 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> template <typename... Keys>
basic_table_core& traverse_set(Keys&&... keys) { basic_table_core& traverse_set(Keys&&... keys) {
auto pp = stack::push_pop<is_global<Keys...>::value>(*this); 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)); 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; return *this;
} }
template<typename... Args> template<typename... Args>
basic_table_core& set(Args&&... 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; return *this;
} }
@ -338,20 +384,40 @@ namespace sol {
} }
template<bool read_only = true, typename... Args> 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) { if (read_only) {
table idx = create_with(std::forward<Args>(args)...);
table x = create_with( table x = create_with(
meta_function::new_index, detail::fail_on_newindex, meta_function::new_index, detail::fail_on_newindex,
meta_function::index, idx meta_function::index, target
); );
table target = create_named(name); table shim = create_named(name, metatable_key, x);
target[metatable_key] = x; return shim;
} }
else { 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> template<typename Fx>
@ -482,11 +548,7 @@ namespace sol {
template <typename Name, typename... Args> template <typename Name, typename... Args>
table create_named(Name&& name, Args&&... 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); 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)...); return create(std::forward<Name>(name), narr, (sizeof...(Args) / 2) - narr, std::forward<Args>(args)...);
}
~basic_table_core() {
} }
}; };
} // sol } // sol

View File

@ -617,6 +617,7 @@ namespace sol {
bitwise_or, bitwise_or,
bitwise_xor, bitwise_xor,
pairs, pairs,
ipairs,
next, next,
type, type,
type_info, type_info,
@ -624,8 +625,8 @@ namespace sol {
typedef meta_function meta_method; typedef meta_function meta_method;
inline const std::array<std::string, 31>& meta_function_names() { inline const std::array<std::string, 32>& meta_function_names() {
static const std::array<std::string, 31> names = { { static const std::array<std::string, 32> names = { {
"new", "new",
"__index", "__index",
"__newindex", "__newindex",
@ -656,6 +657,7 @@ namespace sol {
"__bxor", "__bxor",
"__pairs", "__pairs",
"__ipairs",
"__next", "__next",
"__type", "__type",
"__typeinfo" "__typeinfo"

View File

@ -394,7 +394,7 @@ namespace sol {
void* baseclasscheck; void* baseclasscheck;
void* baseclasscast; void* baseclasscast;
bool secondarymeta; 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> 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 { lua_CFunction make_func() const {

View File

@ -121,22 +121,42 @@ TEST_CASE("tables/new_enum", "Making sure enums can be put in and gotten out as
right right
}; };
sol::state lua; SECTION("variadics") {
lua.open_libraries(sol::lib::base); sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_enum( "direction", lua.new_enum("direction",
"up", direction::up, "up", direction::up,
"down", direction::down, "down", direction::down,
"left", direction::left, "left", direction::left,
"right", direction::right "right", direction::right
); );
direction d = lua["direction"]["left"]; direction d = lua["direction"]["left"];
REQUIRE(d == direction::left); REQUIRE(d == direction::left);
auto result = lua.safe_script("direction.left = 50", sol::script_pass_on_error); auto result = lua.safe_script("direction.left = 50", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid()); REQUIRE_FALSE(result.valid());
d = lua["direction"]["left"]; d = lua["direction"]["left"];
REQUIRE(d == 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") { 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::state lua;
sol::table t = lua.create_table(sz, 0); 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); std::iota(bigvec.begin(), bigvec.end(), 1);
for (std::size_t i = 0; i < bigvec.size(); ++i) { for (std::size_t i = 0; i < bigvec.size(); ++i) {
t.add(bigvec[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<double>("a");
REQUIRE(la == 2.5);
}
TEST_CASE("tables/boolean keys", "make sure boolean keys don't get caught up in `is_integral` specializations") { TEST_CASE("tables/boolean keys", "make sure boolean keys don't get caught up in `is_integral` specializations") {
sol::state lua; sol::state lua;
lua.open_libraries(sol::lib::base); lua.open_libraries(sol::lib::base);