diff --git a/docs/source/api/as_args.rst b/docs/source/api/as_args.rst index fdb12f20..eb93a2e2 100644 --- a/docs/source/api/as_args.rst +++ b/docs/source/api/as_args.rst @@ -1,7 +1,7 @@ as_args ======= -turn an iterable argument into multiple arguments -------------------------------------------------- +*turn an iterable argument into multiple arguments* + .. code-block:: cpp diff --git a/docs/source/api/as_function.rst b/docs/source/api/as_function.rst index bb058ba5..e7be9364 100644 --- a/docs/source/api/as_function.rst +++ b/docs/source/api/as_function.rst @@ -1,7 +1,7 @@ as_function =========== -make sure an object is pushed as a function -------------------------------------------- +*make sure an object is pushed as a function* + .. code-block:: cpp diff --git a/docs/source/api/as_returns.rst b/docs/source/api/as_returns.rst index 1171ff19..a4693dea 100644 --- a/docs/source/api/as_returns.rst +++ b/docs/source/api/as_returns.rst @@ -1,7 +1,7 @@ as_returns ========== -turn an iterable argument into a multiple-return type ------------------------------------------------------ +*turn an iterable argument into a multiple-return type* + .. code-block:: cpp diff --git a/docs/source/api/as_table.rst b/docs/source/api/as_table.rst index b8190dd7..7367a078 100644 --- a/docs/source/api/as_table.rst +++ b/docs/source/api/as_table.rst @@ -1,7 +1,7 @@ as_table =========== -make sure an object is pushed as a table ----------------------------------------- +*make sure an object is pushed as a table* + .. code-block:: cpp diff --git a/docs/source/api/c_call.rst b/docs/source/api/c_call.rst index a9c26cdd..5c1e3ba0 100644 --- a/docs/source/api/c_call.rst +++ b/docs/source/api/c_call.rst @@ -1,7 +1,7 @@ c_call ====== -Templated type to transport functions through templates -------------------------------------------------------- +*templated type to transport functions through templates* + .. code-block:: cpp diff --git a/docs/source/api/compatibility.rst b/docs/source/api/compatibility.rst index 3d06213f..6187b046 100644 --- a/docs/source/api/compatibility.rst +++ b/docs/source/api/compatibility.rst @@ -1,7 +1,7 @@ compatibility.hpp ================= -Lua 5.3/5.2 compatibility for Lua 5.1/LuaJIT --------------------------------------------- +*Lua 5.3/5.2 compatibility for Lua 5.1/LuaJIT* + This is a detail header used to maintain compatability with the 5.2 and 5.3+ APIs. It contains code from the MIT-Licensed `Lua code`_ in some places and also from the `lua-compat`_ repository by KeplerProject. diff --git a/docs/source/api/containers.rst b/docs/source/api/containers.rst index 2150d245..4460a3b3 100644 --- a/docs/source/api/containers.rst +++ b/docs/source/api/containers.rst @@ -1,11 +1,13 @@ containers ========== -for handling ``std::vector/map/set`` and others ------------------------------------------------ +*for handling ``std::vector/map/set`` and others* -Sol2 automatically converts containers (detected using the ``sol::is_container`` type trait, which simply looks for begin / end) to be a special kind of userdata with metatable on it. For Lua 5.2 and 5.3, this is extremely helpful as you can make typical containers behave like Lua tables without losing the actual container that they came from, as well as a small amount of indexing and other operations that behave properly given the table type. -If you need to deal with these things from Lua as tables, please consider :doc:`sol::as_table` and :doc:`sol::nested`. +Sol2 automatically converts containers (detected using the ``sol::is_container`` type trait, which simply looks for ``begin`` / ``end``) to be a special kind of userdata with metatable on it. For Lua 5.2 and 5.3, this is extremely helpful as you can make typical containers behave like Lua tables without losing the actual container that they came from, as well as a small amount of indexing and other operations that behave properly given the table type. + +An overview of these traits and additional information can be found :doc:`at the top level container page<../containers>`. + +If you need to deal with these things from and in Lua to be **actual**, true-blue, Lua tables, please consider :doc:`sol::as_table` and :doc:`sol::nested` for serialization and deserialization into and out of the VM with sol2 operations. a complete example @@ -65,42 +67,5 @@ Note that this will not work well in Lua 5.1, as it has explicit table checks an print(i, vec[i]) end -There are also other ways to iterate over key/values, but they can be difficult due to not having proper support in Lua 5.1. We recommend that you upgrade to Lua 5.2 or 5.3 if this is integral to your infrastructure. +There are also other ways to iterate over key/values, but they can be difficult AND cost your performance due to not having proper support in Lua 5.1. We recommend that you upgrade to Lua 5.2 or 5.3 if this is integral to your infrastructure. - -additional functions --------------------- - -Based on the type pushed, a few additional functions are added as "member functions" (``self`` functions called with ``obj:func()`` or ``obj.func(obj)`` syntax) within a Lua script: - -* ``my_container:clear()``: This will call the underlying containers ``clear`` function. -* ``my_container:add( key, value )`` or ``my_container:add( value )``: this will add to the end of the container, or if it is an associative or ordered container, simply put in an expected key-value pair into it. -* ``my_contaner:insert( where, value )`` or ``my_contaner:insert( key, value )``: similar to add, but it only takes two arguments. In the case of ``std::vector`` and the like, the first argument is a ``where`` integer index. The second argument is the value. For associative containers, a key and value argument are expected. -* ``my_container:find( value )``: This will call the underlying containers ``find`` function if it exists, or in case of associative containers, it will work just like an index call. This is meant to give a fast membership check for ``std::set`` and ``std::unordered_set`` containers. -* ``my_container:get( key )``: This function can return multiple values when the value type is a ``std::pair`` or ``std::tuple``, which is not the case for ``obj[key]``! This will call the underlying containers ``find`` function if it exists, index into a regular container, or in case of certain associative containers, it will work just like an index call. This is meant to give a fast membership check for ``std::set`` and ``std::unordered_set`` containers. - -.. _container-detection: - -too-eager container detection? ------------------------------- - - -If you have a type that has ``begin`` or ``end`` member functions but don't provide iterators, you can specialize ``sol::is_container`` to be ``std::false_type``, and that will treat the type as a regular usertype and push it as a regular userdata: - -.. code-block:: cpp - :caption: specialization.hpp - - struct not_container { - void begin() { - - } - - void end() { - - } - }; - - namespace sol { - template <> - struct is_container : std::false_type {}; - } \ No newline at end of file diff --git a/docs/source/api/coroutine.rst b/docs/source/api/coroutine.rst index 3d03da31..34624773 100644 --- a/docs/source/api/coroutine.rst +++ b/docs/source/api/coroutine.rst @@ -1,7 +1,7 @@ coroutine ========= -resumable/yielding functions from Lua -------------------------------------- +*resumable/yielding functions from Lua* + A ``coroutine`` is a :doc:`reference` to a function in Lua that can be called multiple times to yield a specific result. It is run on the :doc:`lua_State` that was used to create it (see :doc:`thread` for an example on how to get a coroutine that runs on a thread separate from your usual "main" :doc:`lua_State`). diff --git a/docs/source/api/environment.rst b/docs/source/api/environment.rst index 17149ba3..7b1072f1 100644 --- a/docs/source/api/environment.rst +++ b/docs/source/api/environment.rst @@ -1,7 +1,7 @@ environment =========== -encapsulation table for script sandboxing ------------------------------------------ +*encapsulation table for script sandboxing* + .. code-block:: cpp :caption: environment diff --git a/docs/source/api/error.rst b/docs/source/api/error.rst index 4bfe21e9..35557595 100644 --- a/docs/source/api/error.rst +++ b/docs/source/api/error.rst @@ -1,7 +1,7 @@ error ===== -the single error/exception type -------------------------------- +*the single error/exception type* + .. code-block:: cpp diff --git a/docs/source/api/function.rst b/docs/source/api/function.rst index fce41eec..1eb2a648 100644 --- a/docs/source/api/function.rst +++ b/docs/source/api/function.rst @@ -1,7 +1,7 @@ function ======== -calling functions bound to Lua ------------------------------- +*calling functions bound to Lua* + .. note:: diff --git a/docs/source/api/make_reference.rst b/docs/source/api/make_reference.rst index cd39b5a3..168912ec 100644 --- a/docs/source/api/make_reference.rst +++ b/docs/source/api/make_reference.rst @@ -1,7 +1,7 @@ make_object/make_reference ========================== -Create a value on the Lua stack and return it ---------------------------------------------- +*create a value in the lua registry / on the Lua stack and return it* + .. code-block:: cpp :caption: function: make_reference diff --git a/docs/source/api/metatable_key.rst b/docs/source/api/metatable_key.rst index 9a19ff8c..19367b01 100644 --- a/docs/source/api/metatable_key.rst +++ b/docs/source/api/metatable_key.rst @@ -1,7 +1,7 @@ metatable_key ============= -A key for setting and getting an object's metatable ---------------------------------------------------- +*a key for setting and getting an object's metatable* + .. code-block:: cpp diff --git a/docs/source/api/nested.rst b/docs/source/api/nested.rst index 960e4cfa..c18d5ea9 100644 --- a/docs/source/api/nested.rst +++ b/docs/source/api/nested.rst @@ -1,6 +1,7 @@ nested ====== + .. code-block:: cpp template diff --git a/docs/source/api/new_table.rst b/docs/source/api/new_table.rst index 8ce9fac0..2eb1ffb9 100644 --- a/docs/source/api/new_table.rst +++ b/docs/source/api/new_table.rst @@ -1,7 +1,7 @@ new_table ========= -a table creation hint to environment/table ------------------------------------------- +*a table creation hint to environment/table* + .. code-block:: cpp diff --git a/docs/source/api/object.rst b/docs/source/api/object.rst index f38f089c..f87c4f37 100644 --- a/docs/source/api/object.rst +++ b/docs/source/api/object.rst @@ -1,7 +1,7 @@ object ====== -general-purpose safety reference to an existing object ------------------------------------------------------- +*general-purpose safety reference to an existing object* + .. code-block:: cpp diff --git a/docs/source/api/overload.rst b/docs/source/api/overload.rst index f6319558..c97f951a 100644 --- a/docs/source/api/overload.rst +++ b/docs/source/api/overload.rst @@ -1,9 +1,19 @@ overload ======== -calling different functions based on argument number/type ---------------------------------------------------------- +*calling different functions based on argument number/type* -This function helps users make overloaded functions that can be called from Lua using 1 name but multiple arguments. It is meant to replace the spaghetti of code whre users mock this up by doing strange if statements and switches on what version of a function to call based on `luaL_check{number/udata/string}`_. + +.. code-block:: cpp + :caption: function: create overloaded set + :linenos: + + template + struct overloaded_set : std::tuple { /* ... */ }; + + template + overloaded_set overload( Args&&... args ); + +The actual class produced by ``sol::overload`` is essentially a type-wrapper around ``std::tuple`` that signals to the library that an overload is being created. The function helps users make overloaded functions that can be called from Lua using 1 name but multiple arguments. It is meant to replace the spaghetti of code whre users mock this up by doing strange if statements and switches on what version of a function to call based on `luaL_check{number/udata/string}`_. .. note:: @@ -77,20 +87,15 @@ Thusly, doing the following in Lua: bark(pup, 20) -- calls ultra_bark local nowherebark = bark() -- calls lambda which returns that string -The actual class produced by ``sol::overload`` is essentially a type-wrapper around ``std::tuple`` that signals to the library that an overload is being created: +.. note:: -.. code-block:: cpp - :caption: function: create overloaded set - :linenos: + Overloading is done on a first-come, first-serve system. This means if two overloads are compatible, workable overloads, it will choose the first one in the list. - template - struct overloaded_set : std::tuple { /* ... */ }; - - template - overloaded_set overload( Args&&... args ); +Note that because of this system, you can use :doc:`sol::variadic_args` to make a function that serves as a "fallback". Be sure that it is the last specified function in the listed functions for ``sol::overload( ... )``. `This example shows how`_. .. note:: Please keep in mind that doing this bears a runtime cost to find the proper overload. The cost scales directly not exactly with the number of overloads, but the number of functions that have the same argument count as each other (Sol will early-eliminate any functions that do not match the argument count). .. _luaL_check{number/udata/string}: http://www.Lua.org/manual/5.3/manual.html#luaL_checkinteger +.. _This example shows how: https://github.com/ThePhD/sol2/blob/develop/examples/overloading_with_fallback.cpp diff --git a/docs/source/api/property.rst b/docs/source/api/property.rst index e3ee02e2..e9a574c4 100644 --- a/docs/source/api/property.rst +++ b/docs/source/api/property.rst @@ -1,5 +1,6 @@ property ======== +*wrapper to specify read and write variable functionality using functions* .. code-block:: cpp diff --git a/docs/source/api/protect.rst b/docs/source/api/protect.rst index 4f14cb0d..112fa5f8 100644 --- a/docs/source/api/protect.rst +++ b/docs/source/api/protect.rst @@ -1,7 +1,7 @@ protect ======= -Routine to mark a function / variable as requiring safety ---------------------------------------------------------- +*routine to mark a function / variable as requiring safety* + .. code-block:: cpp diff --git a/docs/source/api/protected_function.rst b/docs/source/api/protected_function.rst index e9283d16..47d49062 100644 --- a/docs/source/api/protected_function.rst +++ b/docs/source/api/protected_function.rst @@ -1,7 +1,6 @@ protected_function ================== -Lua function calls that trap errors and provide error handling --------------------------------------------------------------- +*Lua function calls that trap errors and provide error handling* .. code-block:: cpp diff --git a/docs/source/api/proxy.rst b/docs/source/api/proxy.rst index 897dbbaf..0f778d33 100644 --- a/docs/source/api/proxy.rst +++ b/docs/source/api/proxy.rst @@ -1,9 +1,9 @@ proxy, (protected\_)function_result - proxy_base derivatives ============================================================ -``table[x]`` and ``function(...)`` conversion struct ----------------------------------------------------- +*``table[x]`` and ``function(...)`` conversion struct* -.. code-block:: c++ + +.. code-block:: cpp template struct proxy_base; diff --git a/docs/source/api/readonly.rst b/docs/source/api/readonly.rst index 1f47bec5..e01de48f 100644 --- a/docs/source/api/readonly.rst +++ b/docs/source/api/readonly.rst @@ -1,14 +1,13 @@ readonly ======== -routine to mark a member variable as read-only ----------------------------------------------- +*routine to mark a member variable as read-only* .. code-block:: cpp template auto readonly( T&& value ); -The goal of read-only is to protect a variable set on a usertype or a function. Simply wrap it around a member variable, e.g. ``sol::readonly( &my_class::my_member_variable )`` in the appropriate place to use it. If someone tries to set it, it will throw an error. +The goal of read-only is to protect a variable set on a usertype or a function. Simply wrap it around a member variable, e.g. ``sol::readonly( &my_class::my_member_variable )`` in the appropriate place to use it. If someone tries to set it, it will error their code. ``sol::readonly`` is especially important when you're working with types that do not have a copy constructor. Lua does not understand move semantics, and therefore setters to user-defined-types require a C++ copy constructor. Containers as member variables that contain types that are not copyable but movable -- e.g. ``std::vector`` amongst others -- also can erroneously state they are copyable but fail with compiler errors. If your type does not fit a container's definition of being copyable or is just not copyable in general and it is a member variable, please use ``sol::readonly``. diff --git a/docs/source/api/reference.rst b/docs/source/api/reference.rst index 43c163a7..6637bfb3 100644 --- a/docs/source/api/reference.rst +++ b/docs/source/api/reference.rst @@ -1,7 +1,7 @@ reference ========= -general purpose reference to Lua object in registry ---------------------------------------------------- +*general purpose reference to Lua object in registry* + .. code-block:: cpp :caption: reference @@ -10,11 +10,11 @@ general purpose reference to Lua object in registry This type keeps around a reference to something inside of Lua, whether that object was on the stack or already present as an object in the Lua Runtime. It places the object Lua registry and will keep it alive. -It is the backbone for all things that reference items on the stack and needs to keep them around beyond their appearance and lifetime on said Lua stack or need to be kept alive outside of a script beyond garbage collection times. Its progeny include :doc:`sol::coroutine`, :doc:`sol::function`, :doc:`sol::protected_function`, :doc:`sol::object`, :doc:`sol::table`/:doc:`sol::global_table
`, :doc:`sol::thread`, and :doc:`sol::userdata`, which are type-specific versions of ``sol::reference``. +It is the backbone for all things that reference items on the stack that need to be kept around beyond their appearance and lifetime on said Lua stack or need to be kept alive outside of a script beyond garbage collection times. Its progeny include :doc:`sol::coroutine`, :doc:`sol::function`, :doc:`sol::protected_function`, :doc:`sol::object`, :doc:`sol::table
`/:doc:`sol::global_table
`, :doc:`sol::thread`, and :doc:`sol::(light_)userdata`, which are type-specific versions of ``sol::reference``. Note that if you need to keep a reference to something inside of Lua, it is better to use ``sol::reference`` or :doc:`sol::object` to keep a reference to it and then use the ``obj.as()`` member function to retrieve what you need than to take a direct dependency on the memory by retrieving a pointer or reference to the userdata itself. This will ensure that if a script or the Lua Runtime is finished with an object, it will not be garbage collected. Do this only if you need long-term storage. -For all of these types, there's also a ``sol::stack_{x}`` version of them, such as ``sol::stack_table``. +For all of these types, there's also a ``sol::stack_{x}`` version of them, such as ``sol::stack_table``. They are useful for a small performance boost at the cost of not having a strong reference, which has implications for what happens when the item is moved off of the stack. See :doc:`sol::stack_reference` for more details. members diff --git a/docs/source/api/resolve.rst b/docs/source/api/resolve.rst index b1ceffd0..091204e3 100644 --- a/docs/source/api/resolve.rst +++ b/docs/source/api/resolve.rst @@ -1,7 +1,7 @@ resolve ======= -utility to pick overloaded C++ function calls ---------------------------------------------- +*utility to pick overloaded C++ function calls* + .. code-block:: cpp :caption: function: resolve C++ overload diff --git a/docs/source/api/simple_usertype.rst b/docs/source/api/simple_usertype.rst index f0a76978..643fdcb0 100644 --- a/docs/source/api/simple_usertype.rst +++ b/docs/source/api/simple_usertype.rst @@ -1,14 +1,17 @@ simple_usertype ================== -structures and classes from C++ made available to Lua code (simpler) --------------------------------------------------------------------- +*structures and classes from C++ made available to Lua code (simpler)* -This type is no different from :doc:`regular usertype`, but allows much of its work to be done at runtime instead of compile-time. You can reduce compilation times from a plain ``usertype`` when you have an exceedingly bulky registration listing. +This usertype is no difference from :doc:`regular usertype`, but allows much of its work to be done at runtime instead of compile-time. You can reduce compilation times from a plain ``usertype`` when you have an exceedingly bulky registration listing. -You can set functions incrementally to reduce compile-time burden with ``simple_usertype`` as well, as shown in `this example`_. This means both adding incrementally during registration. +You can set functions incrementally to reduce compile-time burden with ``simple_usertype`` as well, as shown in `this example`_. This means both adding incrementally during registration and even adding at runt=time. -You can add functions to both regular and simple usertypes afterwards by adding items to the metatable directly at runtime (e.g., with :doc:`metatable_key` or by accessing the named metatable yourself). +You can add functions to both regular and simple usertypes afterwards by adding items to the metatable directly at runtime (e.g., by accessing the named metatable yourself and setting functions on it). + +.. note:: + + You cannot add functions to an individual object. You can only add functions to the whole class / usertype. Some developers used ``simple_usertype`` in older versions to have variables automatically be functions. To achieve this behavior, wrap the desired variable into :doc:`sol::as_function`. diff --git a/docs/source/api/stack.rst b/docs/source/api/stack.rst index dce86242..0842ce4b 100644 --- a/docs/source/api/stack.rst +++ b/docs/source/api/stack.rst @@ -1,7 +1,7 @@ stack namespace =============== -the nitty-gritty core abstraction layer over Lua ------------------------------------------------- +*the nitty-gritty core abstraction layer over Lua* + .. code-block:: cpp @@ -9,6 +9,8 @@ the nitty-gritty core abstraction layer over Lua If you find that the higher level abstractions are not meeting your needs, you may want to delve into the ``stack`` namespace to try and get more out of Sol. ``stack.hpp`` and the ``stack`` namespace define several utilities to work with Lua, including pushing / popping utilities, getters, type checkers, Lua call helpers and more. This namespace is not thoroughly documented as the majority of its interface is mercurial and subject to change between releases to either heavily boost performance or improve the Sol :doc:`api`. +Working at this level of the stack can be enhanced by understanding how the `Lua stack works in general`_ and then supplementing it with the objects and items here. + There are, however, a few :ref:`template customization points` that you may use for your purposes and a handful of potentially handy functions. These may help if you're trying to slim down the code you have to write, or if you want to make your types behave differently throughout the Sol stack. Note that overriding the defaults **can** throw out many of the safety guarantees Sol provides: therefore, modify the :ref:`extension points` at your own discretion. structures @@ -31,9 +33,33 @@ When overriding the :doc:`customization points<../tutorial/customization>`, plea Note that customizations can also be put up on a separate page here, if individuals decide to make in-depth custom ones for their framework or other places. +.. code-block:: cpp + :caption: struct: probe + :name: stack-probe-struct + + struct probe { + bool success; + int levels; + + probe(bool s, int l); + operator bool() const; + }; + +This struct is used for showing whether or not a :ref:`probing get_field` was successful or not. + + members ------- +.. code-block:: cpp + :caption: function: call_lua + :name: stack-call-lua + + template + inline int call_lua(lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs); + +This function is helpful for when you bind to a raw C function but need sol's abstractions to save you the agony of setting up arguments and know how `calling C functions works`_. The ``start`` parameter tells the function where to start pulling arguments from. The parameter ``fx`` is what's supposed to be called. Extra arguments are passed to the function directly. There are intermediate versions of this (``sol::stack::call_into_lua`` and similar) for more advanced users, but they are not documented as they are subject to change to improve performance or adjust the API accordingly in later iterations of sol2. Use the more advanced versions at your own peril. + .. code-block:: cpp :caption: function: get :name: stack-get @@ -57,7 +83,10 @@ You may also retrieve an :doc:`sol::optional\` from this as well, t template bool check( lua_State* L, int index, Handler&& handler ) -Checks if the object at ``index`` is of type ``T``. If it is not, it will call the ``handler`` function with ``lua_State*``, ``int index``, ``type`` expected, and ``type`` actual as arguments. + template + bool check( lua_State* L, int index, Handler&& handler, record& tracking ) + +Checks if the object at ``index`` is of type ``T``. If it is not, it will call the ``handler`` function with ``lua_State*``, ``int index``, ``type`` expected, and ``type`` actual as arguments. If you do not pass your own handler, a ``no_panic`` handler will be passed. .. code-block:: cpp :caption: function: check_get @@ -111,11 +140,18 @@ These functinos behave similarly to the ones above, but they check for specific :caption: function: pop :name: stack-pop - // push T inferred from call site, pass args... through to extension point template - auto pop( lua_State* L, int index, ... ) + auto pop( lua_State* L ); +Pops an object off the stack. Will remove a fixed number of objects off the stack, generally determined by the ``sol::lua_size`` traits of the arguments supplied. Generally a simplicity function, used for convenience. +.. code-block:: cpp + :caption: function: top + :name: stack-top + + int top( lua_State* L ); + +Returns the number of values on the stack. .. code-block:: cpp :caption: function: set_field @@ -149,20 +185,6 @@ Gets the field referenced by the key ``k``, by pushing the key onto the stack, a This function leaves the retrieved value on the stack. -.. code-block:: cpp - :caption: struct: probe - :name: stack-probe-struct - - struct probe { - bool success; - int levels; - - probe(bool s, int l); - operator bool() const; - }; - -This struct is used for showing whether or not a :ref:`probing get_field` was successful or not. - .. _extension_points: objects (extension points) @@ -223,4 +245,6 @@ This is an SFINAE-friendly struct that is meant to expose static function ``push This is an SFINAE-friendly struct that is meant to expose static function ``check`` that returns the number of things pushed onto the stack. The default implementation simply checks whether the expected type passed in through the template is equal to the type of the object at the specified index in the Lua stack. The default implementation for types which are considered ``userdata`` go through a myriad of checks to support checking if a type is *actually* of type ``T`` or if its the base class of what it actually stored as a userdata in that index. Down-casting from a base class to a more derived type is, unfortunately, impossible to do. -.. _lua_CFunction: http://www.Lua.org/manual/5.3/manual.html#lua_CFunction \ No newline at end of file +.. _lua_CFunction: http://www.Lua.org/manual/5.3/manual.html#lua_CFunction +.. _Lua stack works in general: https://www.lua.org/pil/24.2.html +.. _calling C functions works: https://www.lua.org/pil/26.html diff --git a/docs/source/api/stack_reference.rst b/docs/source/api/stack_reference.rst index ebac5256..cb2ddbfe 100644 --- a/docs/source/api/stack_reference.rst +++ b/docs/source/api/stack_reference.rst @@ -1,10 +1,16 @@ stack_reference =============== -zero-overhead object on the stack ---------------------------------- +*zero-overhead object on the stack* + When you work with a :doc:`sol::reference`, the object gotten from the stack has a reference to it made in the registry, keeping it alive. If you want to work with the Lua stack directly without having any additional references made, ``sol::stack_reference`` is for you. Its API is identical to ``sol::reference`` in every way, except it contains a ``int stack_index()`` member function that allows you to retrieve the stack index. Note that this will not pin the object since a copy is not made in the registry, meaning things can be pulled out from under it, the stack can shrink under it, things can be added onto the stack before this object's position, and what ``sol::stack_reference`` will point to will change. Please know what the Lua stack is and have discipline while managing your Lua stack when working at this level. -All of the base types have ``stack`` versions of themselves, and the APIs are identical to their non-stack forms. This includes :doc:`sol::stack_table
`, :doc:`sol::stack_function`, :doc:`sol::stack_protected_function`, :doc:`sol::stack_(light\_)userdata` and :doc:`sol::stack_object`. \ No newline at end of file +All of the base types have ``stack`` versions of themselves, and the APIs are identical to their non-stack forms. This includes :doc:`sol::stack_table
`, :doc:`sol::stack_function`, :doc:`sol::stack_protected_function`, :doc:`sol::stack_(light\_)userdata` and :doc:`sol::stack_object`. There is a special case for ``sol::stack_function``, which has an extra type called ``sol::stack_aligned_function`` (and similar ``sol::stack_aligned_protected_function``). + + +stack_aligned_function +---------------------- + +This type is particular to working with the stack. It does not push the function object on the stack before pushing the arguments, assuming that the function present is already on the stack before going ahead and invoking the function it is targeted at. It is identical to :doc:`sol::function` and has a protected counterpart as well. If you are working with the stack and know there is a callable object in the right place (i.e., at the top of the Lua stack), use this abstraction to work with the very top of the stack and have it call your function while still having the easy-to-use Lua abstractions on top. diff --git a/docs/source/api/state.rst b/docs/source/api/state.rst index 1cc20a68..0e312510 100644 --- a/docs/source/api/state.rst +++ b/docs/source/api/state.rst @@ -1,7 +1,7 @@ state ===== -owning and non-owning state holders for registry and globals ------------------------------------------------------------- +*owning and non-owning state holders for registry and globals* + .. code-block:: cpp @@ -9,9 +9,9 @@ owning and non-owning state holders for registry and globals class state : state_view, std::unique_ptr; -The most important class here is ``state_view``. This structure takes a ``lua_State*`` that was already created and gives you simple, easy access to Lua's interfaces without taking ownership. ``state`` derives from ``state_view``, inheriting all of this functionality, but has the additional purpose of creating a fresh ``lua_State*`` and managing its lifetime for you in the default constructor. +The most important class here is ``state_view``. This structure takes a ``lua_State*`` that was already created and gives you simple, easy access to Lua's interfaces without taking ownership. ``state`` derives from ``state_view``, inheriting all of this functionality, but has the additional purpose of creating a fresh ``lua_State*`` and managing its lifetime for you in its constructors. -The majority of the members between ``state_view`` and :doc:`sol::table
` are identical, with added for this higher-level type. Therefore, all of the examples and notes in :doc:`sol::table
` apply here as well. +The majority of the members between ``state_view`` and :doc:`sol::table
` are identical, with a few added for this higher-level type. Therefore, all of the examples and notes in :doc:`sol::table
` apply here as well. enumerations ------------ @@ -111,8 +111,9 @@ Thanks to `Eric (EToreo) for the suggestion on this one`_! :caption: function: load / load_file :name: state-load-code - sol::load_result load(const std::string& code); - sol::load_result load_file(const std::string& filename); + sol::load_result load(lua_Reader reader, void* data, const std::string& chunk_name = "[string]", load_mode mode = load_mode::any); + sol::load_result load(const string_view& code, const std::string& chunk_name = "[string]", load_mode mode = load_mode::any); + sol::load_result load_file(const std::string& filename, load_mode mode = load_mode::any); These functions *load* the desired blob of either code that is in a string, or code that comes from a filename, on the ``lua_State*``. That blob will be turned into a Lua Function. It will not be run: it returns a ``load_result`` proxy that can be called to actually run the code, when you are ready. It can also be turned into a ``sol::function``, a ``sol::protected_function``, or some other abstraction that can serve to call the function. If it is called, it will run on the object's current ``lua_State*``: it is not isolated. If you need isolation, consider using :doc:`sol::environment`, creating a new state, or other Lua sandboxing techniques. diff --git a/docs/source/api/table.rst b/docs/source/api/table.rst index 3bee4c89..e9d6a226 100644 --- a/docs/source/api/table.rst +++ b/docs/source/api/table.rst @@ -1,7 +1,7 @@ table ===== -a representation of a Lua (meta)table -------------------------------------- +*a representation of a Lua (meta)table* + .. code-block:: cpp diff --git a/docs/source/api/this_environment.rst b/docs/source/api/this_environment.rst index 7a8dd6ea..9fd7c0d4 100644 --- a/docs/source/api/this_environment.rst +++ b/docs/source/api/this_environment.rst @@ -1,7 +1,7 @@ this_environment ================ -retrieving the environment of the calling function --------------------------------------------------- +*retrieving the environment of the calling function* + Sometimes in C++ it's useful to know where a Lua call is coming from and what :doc:`environment` it is from. The former is covered by Lua's Debug API, which is extensive and is not fully wrapped up by sol2. But, sol2 covers the latter in letting you get the environment of the calling script / function, if it has one. ``sol::this_environment`` is a *transparent argument* and does not need to be passed in Lua scripts or provided when using :doc:`sol::function`, similar to :doc:`sol::this_state`: diff --git a/docs/source/api/this_state.rst b/docs/source/api/this_state.rst index c4c6b3b7..0aa61404 100644 --- a/docs/source/api/this_state.rst +++ b/docs/source/api/this_state.rst @@ -1,7 +1,7 @@ this_state ========== -transparent state argument for the current state ------------------------------------------------- +*transparent state argument for the current state* + .. code-block:: cpp diff --git a/docs/source/api/thread.rst b/docs/source/api/thread.rst index b7349d97..d4a0b636 100644 --- a/docs/source/api/thread.rst +++ b/docs/source/api/thread.rst @@ -1,7 +1,6 @@ thread ====== -a separate state that can contain and run functions ---------------------------------------------------- +*a separate state that can contain and run functions* .. code-block:: cpp diff --git a/docs/source/api/tie.rst b/docs/source/api/tie.rst index 080c8a1c..ce542127 100644 --- a/docs/source/api/tie.rst +++ b/docs/source/api/tie.rst @@ -1,7 +1,7 @@ tie === -An improved version of ``std::tie`` ------------------------------------ +*improved version of ``std::tie``* + `std::tie()`_ does not work well with :doc:`sol::function`'s ``sol::function_result`` returns. Use ``sol::tie`` instead. Because they're both named `tie`, you'll need to be explicit when you use Sol's by naming it with the namespace (``sol::tie``), even with a ``using namespace sol;``. Here's an example: diff --git a/docs/source/api/types.rst b/docs/source/api/types.rst index a335880d..264e8a5d 100644 --- a/docs/source/api/types.rst +++ b/docs/source/api/types.rst @@ -1,7 +1,6 @@ types ===== -nil, lua_primitive type traits, and other fundamentals ------------------------------------------------------- +*nil, lua_primitive type traits, and other fundamentals* The ``types.hpp`` header contains various fundamentals and utilities of Sol. diff --git a/docs/source/api/unique_usertype_traits.rst b/docs/source/api/unique_usertype_traits.rst index 4568b113..e7ef54da 100644 --- a/docs/source/api/unique_usertype_traits.rst +++ b/docs/source/api/unique_usertype_traits.rst @@ -1,7 +1,7 @@ unique_usertype_traits ========================= -A trait for hooking special handles / pointers ----------------------------------------------- +*trait for hooking special handles / pointers* + .. code-block:: cpp :caption: unique_usertype diff --git a/docs/source/api/user.rst b/docs/source/api/user.rst index 9a0905d7..712f2d3c 100644 --- a/docs/source/api/user.rst +++ b/docs/source/api/user.rst @@ -1,7 +1,7 @@ light/user ================ -Utility class for the cheapest form of (light) userdata -------------------------------------------------------- +*utility class for the cheapest form of (light) userdata* + .. code-block:: cpp diff --git a/docs/source/api/userdata.rst b/docs/source/api/userdata.rst index ebf220d7..173e3e79 100644 --- a/docs/source/api/userdata.rst +++ b/docs/source/api/userdata.rst @@ -1,7 +1,6 @@ userdata ======== -reference to a userdata ------------------------ +*reference to a userdata* .. code-block:: cpp :caption: (light\_)userdata reference diff --git a/docs/source/api/usertype.rst b/docs/source/api/usertype.rst index 8960323f..cf8851a7 100644 --- a/docs/source/api/usertype.rst +++ b/docs/source/api/usertype.rst @@ -1,7 +1,7 @@ usertype =========== -structures and classes from C++ made available to Lua code ----------------------------------------------------------- +*structures and classes from C++ made available to Lua code* + *Note: ``T`` refers to the type being turned into a usertype.* @@ -230,6 +230,44 @@ usertype arguments - simple usertype - Should probably not be used directly. Use ``sol::table::new_usertype`` or ``sol::table::new_simple_usertype`` instead +runtime functions +----------------- + +You can add functions at runtime **to the whole class**. Set a name under the metatable name you bound using ``new_usertype``/``new_simple_usertype`` to an object. For example: + +.. code-block:: cpp + :linenos: + :caption: runtime_extension.cpp + :name: runtime-extension + + #define SOL_CHECK_ARGUMENTS 1 + #include + + struct object { + int value = 0; + }; + + int main (int, char*[]) { + + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_usertype( "object" ); + + // runtime additions: through the sol API + lua["object"]["func"] = [](object& o) { return o.value; }; + // runtime additions: through a lua script + lua.script("function object:print () print(self:func()) end"); + + // see it work + lua.script("local obj = object.new() \n obj:print()"); + } + + +.. note:: + + You cannot add functions to an individual object. You can only add functions to the whole class / usertype. + overloading ----------- diff --git a/docs/source/api/usertype_memory.rst b/docs/source/api/usertype_memory.rst index 4043cb27..d516ffb0 100644 --- a/docs/source/api/usertype_memory.rst +++ b/docs/source/api/usertype_memory.rst @@ -1,5 +1,6 @@ usertype memory =============== +*memory layout of usertypes* .. note:: diff --git a/docs/source/api/var.rst b/docs/source/api/var.rst index 9e9bcec5..762772cc 100644 --- a/docs/source/api/var.rst +++ b/docs/source/api/var.rst @@ -1,7 +1,7 @@ var === -For hooking up static / global variables to Lua usertypes ---------------------------------------------------------- +*For hooking up static / global variables to Lua usertypes* + The sole purpose of this tagging type is to work with :doc:`usertypes` to provide ``my_class.my_static_var`` access, and to also provide reference-based access as well. diff --git a/docs/source/api/variadic_args.rst b/docs/source/api/variadic_args.rst index 6db06767..8ff7dc4f 100644 --- a/docs/source/api/variadic_args.rst +++ b/docs/source/api/variadic_args.rst @@ -1,7 +1,6 @@ variadic_args ============= -transparent argument to deal with multiple parameters to a function -------------------------------------------------------------------- +*transparent argument to deal with multiple parameters to a function* .. code-block:: cpp diff --git a/docs/source/api/variadic_results.rst b/docs/source/api/variadic_results.rst index 1c542c9a..11fa73ab 100644 --- a/docs/source/api/variadic_results.rst +++ b/docs/source/api/variadic_results.rst @@ -1,7 +1,6 @@ variadic_results ================ -push multiple disparate arguments into lua ------------------------------------------- +*push multiple disparate arguments into lua* .. code-block:: cpp diff --git a/docs/source/containers.rst b/docs/source/containers.rst new file mode 100644 index 00000000..f94bd4e4 --- /dev/null +++ b/docs/source/containers.rst @@ -0,0 +1,169 @@ +containers +========== + +Containers are objects that are meant to be inspected and iterated and whose job is to typically provide storage to a collection of items. The ``std::`` library has several containers of varying types, and all of them have ``begin()`` and ``end()`` function which return iterators. C-style arrays are also containers, and sol2 will detect all of them for use and bestow them with special properties and functions. + +.. _container-c-array: + +.. 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. + + +.. _container-detection: + +container detection +------------------- + +containers are detected by the type trait ``sol::is_container``. If that turns out to be true, sol2 will attempt to push a userdata into Lua for the specified type ``T``, and bestow it with some of the functions and properties listed below. These functions and properties are provided by a template struct ``sol::container_traits``, which has a number of static Lua C functions bound to a safety metatable. If you want to override the behavior for a specific container, you must first specialize ``sol::is_container`` to drive from ``std::true_type``, then override the functions you want to change. Any function you do not override will call the default implementation or equivalent. The default implementation for unrecognized containers is simply errors. + +You can also specialize ``sol::is_container`` to turn off container detection, if you find it too eager for a type that just happens to have ``begin`` and ``end`` functions, like so: + +.. code-block:: cpp + :caption: not_container.hpp + + struct not_container { + void begin() { + + } + + void end() { + + } + }; + + namespace sol { + template <> + struct is_container : std::false_type {}; + } + +This will let the type be pushed as a regular userdata. + + +container overriding +-------------------- + +If you **want** it to participate as a table, use ``std::true_type`` instead of ``std::false_type`` from the :ref:`containter detection` example. and provide the appropriate ``iterator`` and ``value_type`` definitions on the type. Failure to do so will result in a container whose operations fail by default (or compilation will fail). + +If you need a type whose declaration and definition you do not have control over to be a container, then you must override the default behavior by specializing container traits, like so: + +.. code-block:: cpp + :caption: specializing.hpp + + struct not_my_type { ... }; + + namespace sol { + template <> + struct is_container : std::true_type {}; + + template <> + struct container_traits { + + ... + // see below for implemetation details + }; + } + + +The various operations provided by ``container_traits`` are expected to be like so, below. Ability to override them requires familiarity with the Lua stack and how it operates, as well as knowledge of Lua's :ref:`raw C functions`. You can read up on raw C functions by looking at the "Programming in Lua" book. The `online version's information`_ about the stack and how to return information is still relevant, and you can combine that by also using sol's low-level :doc:`stack API` to achieve whatever behavior you need. + +.. warning:: + + Exception handling **WILL** be provided around these particular raw C functions, so you do not need to worry about exceptions or errors bubbling through and handling that part. It is specifically handled for you in this specific instance, and **ONLY** in this specific instance. The raw note still applies to every other raw C function you make manually. + + +container operations +------------------------- + +Below are the many container operations and their override points for ``container_traits``. Please use these to understand how to use any part of the implementation. + ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| operation | lua syntax | container_traits | stack argument order | notes/caveats | +| | | extension point | | | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| set | ``c:set(key, value)`` | ``static int set(lua_State*);`` | 1 self | - if ``value`` is nil, it performs an erase in default implementation | +| | | | 2 key | - if this is a sequence container and it support insertion and ``key``,is an index equal to the size of the container,+ 1, it will insert at,the end of the container (this is a Lua idiom) | +| | | | 3 value | | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| index_set | ``c[key] = value`` | ``static int index_set(lua_State*);`` | 1 self | - default implementation calls "set" | +| | | | 2 key | - if this is a sequence container and it support insertion and ``key`` is an index equal to the size of the container + 1, it will insert at the end of the container (this is a Lua idiom) | +| | | | 3 value | | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| get | ``v = c:get(key)`` | ``static int get(lua_State*);`` | 1 self | - can return multiple values | +| | | | 2 key | - default implementation increments iterators linearly for non-random-access | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| index_get | ``v = c[key]`` | ``static int index_get(lua_State*);`` | 1 self | - can only return 1 value | +| | | | 2 key | - default implementation just calls "get" | +| | | | | - if ``key`` is a string and ``key`` is one of the other member functions, it will return that member function rather than perform a lookup / index get | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| find | ``c:find(target)`` | ``static int find(lua_State*);`` | 1 self | - ``target`` is a value for non-lookup containers (fixed containers, sequence containers, non-associative and non-ordered containers) | +| | | | 2 target | | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| erase | ``c:erase(target)`` | ``static int erase(lua_State*);`` | 1 self | - for sequence containers, ``target`` is an index to erase | +| | | | 2 target | - for lookup containers, ``target`` is the key type | +| | | | | - uses linear incrementation to spot for sequence containers that do not have random access iterators (``std::list``, ``std::forward_list``, and similar) | +| | | | | - invalidates iteration | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| insert | ``c:insert(target, value)`` | | 1 self | - for sequence containers, ``target`` is an index, otherwise it is the key type | +| | | | 2 target | - inserts into a container if possible at the specified location | +| | | | 3 key | | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| add | ``c:add(key, value)`` or ``c:add(value)`` | ``static int add(lua_State*);`` | 1 self | - 2nd argument (3rd on stack) is provided for associative containers to add | +| | | | 2 key/value | - ordered containers will insert into the appropriate spot, not necessarily at the end | +| | | | 3 value | | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| size | ``#c`` | ``static int size(lua_State*);`` | 1 self | - default implementation calls ``.size()`` if present | +| | | | | - otherwise, default implementation uses ``std::distance(begin(L, self), end(L, self))`` | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| clear | ``c:clear()`` | ``static int clear(lua_State*);`` | 1 self | - default implementation provides no fallback if there's no ``clear`` operation | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| begin | n/a | ``static int begin(lua_State*, T&);`` | n/a | - called by default implementation | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| end | n/a | ``static int end(lua_State*, T&);`` | n/a | - called by default implementation | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| pairs | | ``static int pairs(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 ``__pairs`` is for or how to implement it and the ``next`` function | +| | | | | - 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) | ++-----------+-------------------------------------------+---------------------------------------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +.. _container-classifications: + +container classifications +------------------------- + +When you serialize a container into sol2, the default container handler deals with the containers by inspecting various properties, functions, and typedefs on them. Here are the broad implications of containers sol2's defaults will recognize, and which already-known containers fall into their categories: + ++------------------------+----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------+ +| container type | requirements | known containers | notes/caveats | ++------------------------+----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------+ +| sequence | ``erase(iterator)`` | std::vector | - ``find`` operation is linear in size of list (searches all elements) | +| | ``push_back``/``insert(value_type)`` | std::deque | - std::forward_list has forward-only iterators: set/find is a linear operation | +| | | std::list | - std::forward_list uses "insert_after" idiom, requires special handling internally | +| | | std::forward_list | | ++------------------------+----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------+ +| fixed | lacking ``push_back``/``insert`` | std::array | - regular c-style arrays must be set with | +| | lacking ``erase`` | T[n] (fixed arrays) | ``std::ref( arr )`` or ``&arr`` | +| | | | to be used as a container type with sol2 | ++------------------------+----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------+ +| ordered | ``key_type`` typedef | std::set | - ``container[key] = stuff`` operation erases when ``stuff`` is nil, inserts/sets when not | +| | ``erase(key)`` | std::multi_set | - ``container.get(key)`` returns the key itself | +| | ``find(key)`` | | | +| | ``insert(key)`` | | | ++------------------------+----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------+ +| associative, ordered | ``key_type``, ``mapped_type`` typedefs | std::map | | +| | ``erase(key)`` | std::multi_map | | +| | ``find(key)`` | | | +| | ``insert({ key, value })`` | | | ++------------------------+----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------+ +| unordered | same as ordered | std::unordered_set | - ``container[key] = stuff`` operation erases when ``stuff`` is nil, inserts/sets when not | +| | | std::unordered_multiset | - ``container.get(key)`` returns the key itself | +| | | | - iteration not guaranteed to be in order of insertion, just like in C++ container | +| | | | | ++------------------------+----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------+ +| unordered, associative | same as ordered, associative | std::unordered_map | - iteration not guaranteed to be in order of insertion, just like in C++ container | +| | | std::unordered_multimap | | ++------------------------+----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------+ + + +.. _online version's information: https://www.lua.org/pil/26.html diff --git a/docs/source/errors.rst b/docs/source/errors.rst index c36c76e2..7bd7239c 100644 --- a/docs/source/errors.rst +++ b/docs/source/errors.rst @@ -20,6 +20,8 @@ A myriad of compiler errors can occur when something goes wrong. Here is some ba * Template depth errors may also be a problem on earlier versions of clang++ and g++. Use ``-ftemplate-depth`` compiler flag and specify really high number (something like 2048 or even double that amount) to let the compiler work freely. Also consider potentially using :doc:`simple usertypes` to save compilation speed. * If you have a move-only type, that type may need to be made ``readonly`` if it is bound as a member variable on a usertype or bound using ``state_view::set_function``. See :doc:`sol::readonly` for more details. * Assigning a ``std::string`` or a ``std::pair`` using ``operator=`` after it's been constructed can result in compiler errors when working with ``sol::function`` and its results. See `this issue for fixes to this behavior`_. +* Sometimes, using ``__stdcall`` in a 32-bit (x86) environment on VC++ can cause problems binding functions because of a compiler bug. Put the function in a ``std::function`` to make the compiler errors and other problems go away. Also see `this __stdcall issue report`_ for more details. + Linker Errors ------------- @@ -62,4 +64,5 @@ Iteration Tables may have other junk on them that makes iterating through their numeric part difficult when using a bland ``for-each`` loop, or when calling sol's ``for_each`` function. Use a numeric look to iterate through a table. Iteration does not iterate in any defined order also: see :ref:`this note in the table documentation for more explanation`. -.. _this issue for fixes to this behavior: https://github.com/ThePhD/sol2/issues/414#issuecomment-306839439 \ No newline at end of file +.. _this issue for fixes to this behavior: https://github.com/ThePhD/sol2/issues/414#issuecomment-306839439 +.. _this __stdcall issue report: https://github.com/ThePhD/sol2/issues/463 diff --git a/docs/source/features.rst b/docs/source/features.rst index dde933f6..e3bfb77c 100644 --- a/docs/source/features.rst +++ b/docs/source/features.rst @@ -1,7 +1,7 @@ features ======== -what does Sol (and other libraries) support? --------------------------------------------- +*what does Sol (and other libraries) support?* + The goal of Sol is to provide an incredibly clean API that provides high performance (comparable or better than the C it was written on) and extreme ease of use. That is, users should be able to say: "this works pretty much how I expected it to." diff --git a/docs/source/functions.rst b/docs/source/functions.rst index 9cc0e815..31281f2d 100644 --- a/docs/source/functions.rst +++ b/docs/source/functions.rst @@ -1,7 +1,6 @@ functions ========= -working with functions in sol2 ------------------------------- +*working with functions in sol2* There are a number of examples dealing with functions and how they can be bound to sol2: @@ -9,15 +8,18 @@ There are a number of examples dealing with functions and how they can be bound * For a quicker walkthrough that demonstrates almost everything, see `the examples`_ and the :doc:`the quick and dirty tutorial` * For a full explanation, :doc:`read the tutorial` and consult the subjects below * If you have bindings and set-ups that want to leverage the C API without sol2's interference, you can push a raw function, which has certain implications (noted :ref:`below`) -* You return multiple values by returning a `std::tuple` +* Return multiple values into Lua by: + - returning a ``std::tuple`` + - using :doc:`sol::variadic_results` +* :doc:`Overload function calls with different argument types and count on a single name` (first-bound, first-serve overloading) + - Note: because of this feature, automatic number to string conversion from Lua is not permitted for overloads and does not work when safeties are turned on + - Use C++ captures and lambdas to bind member functions tied to a single object / * You can work with **transparent arguments** that provide you with special information, such as - :doc:`sol::variadic_args`, for handling variable number of arguments at runtime - :doc:`sol::this_state`, for getting the current Lua state - :doc:`sol::this_environment`, for potentially retrieving the current Lua environment -* :doc:`Overload function calls on a single name`, discriminating by argument number and type (first-come, first-serve overloading) - - Note: because of this feature, automatic number to string conversion from Lua is not permitted * Control serialization of arguments and return types with :doc:`sol::nested`, :doc:`sol::as_table`, :doc:`sol::as_args` and :doc:`sol::as_function` -* Set environments for Lua functions and scrips with :doc:`sol::environment` +* Set environments for Lua functions and scripts with :doc:`sol::environment` .. _binding-callable-objects: @@ -25,9 +27,13 @@ There are a number of examples dealing with functions and how they can be bound working with callables/lambdas ------------------------------ +To be explicit about wanting a struct to be interpreted as a function, use ``mytable.set_function( key, func_value );``. You can be explicit about wanting a function as well by using the :doc:`sol::as_function<../api/as_function>` call, which will wrap and identify your type as a function. + .. note:: - Function objects ``obj`` -- a struct with a ``return_type operator()( ... )`` member defined on them, like all C++ lambdas -- are not interpreted as functions when you use ``set`` for ``mytable.set( key, value )`` and ``state.create_table(_with)( ... )``. This only happens automagically with ``mytable[key] = obj``. To be explicit about wanting a struct to be interpreted as a function, use ``mytable.set_function( key, func_value );``. You can be explicit about wanting a function as well by using the :doc:`sol::as_function<../api/as_function>` call, which will wrap and identify your type as a function. + Function objects ``obj`` -- a struct with a ``return_type operator()( ... )`` member defined on them, like all C++ lambdas -- are not interpreted as functions when you use ``set`` for ``mytable.set( key, value )`` and ``state.create_table(_with)( ... )``. This only happens automagically with ``mytable[key] = obj``. + + Note that this also applies to calling functions, for example: ``my_state["table"]["sort"]( some_table, sorting_object );``. .. _function-exception-handling: diff --git a/docs/source/index.rst b/docs/source/index.rst index 3a5b70f1..5d62a025 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -9,8 +9,8 @@ Sol |version| ============= -a fast, simple C++ and Lua Binding ----------------------------------- +*a fast, simple C++ and Lua Binding* + When you need to hit the ground running with Lua and C++, `Sol`_ is the go-to framework for high-performance binding with an easy to use API. @@ -37,6 +37,7 @@ get going: features functions usertypes + containers threading traits api/api-top diff --git a/docs/source/mentions.rst b/docs/source/mentions.rst index 7ce2a1f3..c016c358 100644 --- a/docs/source/mentions.rst +++ b/docs/source/mentions.rst @@ -1,7 +1,6 @@ mentions ======== -so does anyone cool use this thing...? --------------------------------------- +*so does anyone cool use this thing...?* First off, feel free to `tell me about your uses!`_ @@ -26,6 +25,7 @@ Okay, so the features don't convince you, the documentation doesn't convince you * The `Multiple Arcade Machine Emulator (MAME)`_ project switched from using LuaBridge to sol2! - `The pull request`_ in which it was introduced to the master branch. +* For scripting, in `OpenMPT`_ * (CppNow) sol2 was mentioned in a comparison to other scripting languages by ChaiScript developer, Jason Turner (@lefticus), at a conference! - `Jason Turner's presentation`_ * (CppCast) Showed up in CppCast with Elias Daler! @@ -60,3 +60,4 @@ Are you using sol2 for something neat? Want it to be featured here or think it's .. _"sol2 saved my life.": https://twitter.com/EliasDaler/status/739215685264494593 .. _Multiple Arcade Machine Emulator (MAME): http://www.mamedev.org/index.php .. _The pull request: https://github.com/mamedev/mame/pull/1626 +.. _OpenMPT: https://openmpt.org/ \ No newline at end of file diff --git a/docs/source/performance.rst b/docs/source/performance.rst index 4115e01f..93543b71 100644 --- a/docs/source/performance.rst +++ b/docs/source/performance.rst @@ -1,7 +1,6 @@ getting performance =================== -things to make Sol as fast as possible --------------------------------------- +*things to make Sol as fast as possible* As shown by the :doc:`benchmarks`, Sol is very performant with its abstractions. However, in the case where you need every last drop of performance from Sol, a number of tips and API usage tricks will be documented here. PLEASE benchmark / profile your code before you start invoking these, as some of them trade in readability / clarity for performance. diff --git a/docs/source/rtti.rst b/docs/source/rtti.rst index 91aad3e9..74ee03b0 100644 --- a/docs/source/rtti.rst +++ b/docs/source/rtti.rst @@ -1,7 +1,6 @@ run-time type information (rtti) ================================ -because somebody's going to want to shut this off, too... ---------------------------------------------------------- +*because somebody's going to want to shut this off, too...* Sol does not use RTTI anymore. diff --git a/docs/source/usertypes.rst b/docs/source/usertypes.rst index 19142b39..8c47e301 100644 --- a/docs/source/usertypes.rst +++ b/docs/source/usertypes.rst @@ -12,6 +12,8 @@ To learn more about usertypes, visit: The examples folder also has a number of really great examples for you to see. There are also some notes about guarantees you can find about usertypes, and their associated userdata, below: +* Containers get pushed as special usertypes, but can be disabled if problems arise as detailed :doc:`here`. +* You can use bitfields but it requires some finesse on your part. We have an example to help you get started `here, that uses a few tricks`_. * All usertypes are runtime extensible in both `Lua`_ and `C++`_ * Please note that the semi-colon is necessary to "automatically" pass the ``this``/``self`` argument to Lua methods - ``obj:method_name()`` is how you call "member" methods in Lua @@ -37,8 +39,6 @@ The examples folder also has a number of really great examples for you to see. T - Work on a copy by taking arguments or returning by value. Do not use r-value references: they do not mean anything in Lua code. * The actual metatable associated with the usertype has a long name and is defined to be opaque by the Sol implementation. * The actual metatable inner workings is opaque and defined by the Sol implementation, and there are no internal docs because optimizations on the operations are applied based on heuristics we discover from performance testing the system. -* Containers get pushed as special usertypes, but can be disabled if problems arise as detailed :doc:`here`. -* You can use bitfields but it requires some finesse on your part. We have an example to help you get started `here, that uses a few tricks`_. .. _here, that uses a few tricks: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_bitfields.cpp .. _Lua: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_advanced.cpp#L81 diff --git a/examples/basic.cpp b/examples/basic.cpp index ab86abf7..b7174121 100644 --- a/examples/basic.cpp +++ b/examples/basic.cpp @@ -19,7 +19,7 @@ int main() { lua.script("print('hello world')"); // call lua code, and check to make sure it has loaded and run properly: - auto handler = &sol::default_on_error; + auto handler = &sol::script_default_on_error; lua.script("print('hello again, world')", handler); // Use a custom error handler if you need it diff --git a/examples/calling_lua_functions.cpp b/examples/calling_lua_functions.cpp new file mode 100644 index 00000000..5b5fe3cf --- /dev/null +++ b/examples/calling_lua_functions.cpp @@ -0,0 +1,49 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include + +int func_1(int value) { + return 20 + value; +} + +std::string func_2(std::string text) { + return "received: " + text; +} + +sol::variadic_results fallback(sol::this_state ts, sol::variadic_args args) { + sol::variadic_results r; + if (args.size() == 2) { + r.push_back({ ts, sol::in_place, args.get(0) + args.get(1) }); + } + else { + r.push_back({ ts, sol::in_place, 52 }); + } + return r; +} + +int main(int, char*[]) { + std::cout << "=== calling lua functions example ===" << std::endl; + + sol::state lua; + lua.open_libraries(); + + sol::table mLuaPackets = lua.create_named_table("mLuaPackets"); + mLuaPackets[1] = lua.create_table_with("timestamp", 0LL); + mLuaPackets[2] = lua.create_table_with("timestamp", 3LL); + mLuaPackets[3] = lua.create_table_with("timestamp", 1LL); + + lua.script("print('--- pre sort ---')"); + lua.script("for i=1,#mLuaPackets do print(i, mLuaPackets[i].timestamp) end"); + + lua["table"]["sort"](mLuaPackets, sol::as_function([](sol::table l, sol::table r) { + std::uint64_t tl = l["timestamp"]; + std::uint64_t tr = r["timestamp"]; + return tl < tr; + })); + + lua.script("print('--- post sort ---')"); + lua.script("for i=1,#mLuaPackets do print(i, mLuaPackets[i].timestamp) end"); + + return 0; +} \ No newline at end of file diff --git a/examples/overloading_with_fallback.cpp b/examples/overloading_with_fallback.cpp new file mode 100644 index 00000000..4a59b7e1 --- /dev/null +++ b/examples/overloading_with_fallback.cpp @@ -0,0 +1,43 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include + +int func_1(int value) { + return 20 + value; +} + +std::string func_2(std::string text) { + return "received: " + text; +} + +sol::variadic_results fallback(sol::this_state ts, sol::variadic_args args) { + sol::variadic_results r; + if (args.size() == 2) { + r.push_back({ ts, sol::in_place, args.get(0) + args.get(1) }); + } + else { + r.push_back({ ts, sol::in_place, 52 }); + } + return r; +} + +int main(int, char*[]) { + std::cout << "=== overloading with fallback example ===" << std::endl; + + sol::state lua; + lua.open_libraries(); + + lua.set_function("f", sol::overload( + func_1, + func_2, + fallback + )); + + lua.script("print(f(1))"); // func_1 + lua.script("print(f('hi'))"); // func_2 + lua.script("print(f(22, 11))"); // fallback + lua.script("print(f({}))"); // fallback + + return 0; +} diff --git a/examples/require_dll_example/my_object.cpp b/examples/require_dll_example/my_object.cpp new file mode 100644 index 00000000..bf27ee28 --- /dev/null +++ b/examples/require_dll_example/my_object.cpp @@ -0,0 +1,28 @@ +#include "my_object.hpp" + +#define SOL_CHECK_ARGUMENTS 1 +#include + +namespace my_object { + + sol::table open_my_object(sol::this_state L) { + sol::state_view lua(L); + sol::table module = lua.create_table(); + module.new_usertype("test", + sol::constructors(), + "value", &test::value + ); + + return module; + } + +} + +extern "C" int luaopen_my_object(lua_State* L) { + // pass the lua_State, + // the index to start grabbing arguments from, + // and the function itself + // optionally, you can pass extra arguments to the function if that's necessary, + // but that's advanced usage and is generally reserved for internals only + return sol::stack::call_lua(L, 1, my_object::open_my_object ); +} diff --git a/examples/require_dll_example/my_object.hpp b/examples/require_dll_example/my_object.hpp new file mode 100644 index 00000000..03aedf98 --- /dev/null +++ b/examples/require_dll_example/my_object.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "my_object_api.hpp" + +namespace my_object { + + struct test { + int value; + + test() = default; + test(int val) : value(val) {} + }; + +} // my_object + +// this function needs to be exported from your +// dll. "extern 'C'" should do the trick, but +// we're including platform-specific stuff here to help +// see my_object_api.hpp for details +extern "C" MY_OBJECT_API int luaopen_my_object(lua_State* L); diff --git a/examples/require_dll_example/my_object_api.hpp b/examples/require_dll_example/my_object_api.hpp new file mode 100644 index 00000000..1c1ab7a0 --- /dev/null +++ b/examples/require_dll_example/my_object_api.hpp @@ -0,0 +1,32 @@ +#pragma once + +namespace my_object { + +#if defined _MSC_VER +#define MY_OBJECT_VC +#elif defined __GNUC__ +#define MY_OBJECT_GCC +#elif defined __clang__ +#define MY_OBJECT_CLANG +#endif + +#if !defined(_NDEBUG) +#define MY_OBJECT_DEBUG +#else +#endif // Debug || Not-Debug + +#if defined MY_OBJECT_VC +#if defined MY_OBJECT_DLL +#if defined MY_OBJECT_BUILD +#define MY_OBJECT_API __declspec(dllexport) +#else +#define MY_OBJECT_API __declspec(dllexport) +#endif // MY_OBJECT_BUILD - Building the Library vs. Using the Library +#else +#define MY_OBJECT_API +#endif // Building a DLL vs. Static Library +#else +#define MY_OBJECT_API +#endif + +} // my_object diff --git a/examples/require_dll_example/require_from_dll.cpp b/examples/require_dll_example/require_from_dll.cpp new file mode 100644 index 00000000..c6f1e2b2 --- /dev/null +++ b/examples/require_dll_example/require_from_dll.cpp @@ -0,0 +1,25 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "my_object.hpp" + +#include +#include + +int main(int, char*[]) { + std::cout << "=== require from DLL example ===" << std::endl; + + sol::state lua; + + lua.script_file(R"( +mo = require("my_object") + +obj = mo.test.new(24) +print(obj.value) +)"); + + my_object::test& obj = lua["obj"]; + assert(obj.value == 24); + + return 0; +} \ No newline at end of file diff --git a/examples/runtime_additions.cpp b/examples/runtime_additions.cpp new file mode 100644 index 00000000..2dc4b81e --- /dev/null +++ b/examples/runtime_additions.cpp @@ -0,0 +1,41 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include +#include + +struct object { + int value = 0; +}; + +int main(int, char*[]) { + std::cout << "=== runtime_additions example ===" << std::endl; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_usertype("object"); + + // runtime additions: through the sol API + lua["object"]["func"] = [](object& o) { + ++o.value; + return o.value; + }; + // runtime additions: through a lua script + lua.script(R"( +function object:print () + print(self:func()) +end + )"); + + // see it work + lua.script(R"( +obj = object.new() +obj:print() + )"); + + object& obj = lua["obj"]; + assert(obj.value == 1); + + return 0; +} diff --git a/examples/usertype_call_from_c++.cpp b/examples/usertype_call_from_c++.cpp new file mode 100644 index 00000000..09db6fcc --- /dev/null +++ b/examples/usertype_call_from_c++.cpp @@ -0,0 +1,52 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include + +int main(int, char*[]) { + std::cout << "=== usertype call from C++ example ===" << std::endl; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + struct cpp_object { + int value = 5; + }; + + struct test { + int value = 0; + + int func(const cpp_object& obj) { + std::cout << "func\t" << obj.value << std::endl; + value += obj.value; + return value; + } + }; + + lua.new_usertype("test", + "value", &cpp_object::value + ); + lua.new_usertype("test", + "func", &test::func + ); + lua.script("function test:lua_func(obj) print('lua_func', obj.value) end"); + + lua["obj"] = test{}; + cpp_object cppobj; + + lua["obj"]["func"](lua["obj"], cppobj); + lua["obj"]["lua_func"](lua["obj"], cppobj); + + lua["test"]["func"](lua["obj"], cppobj); + lua["test"]["lua_func"](lua["obj"], cppobj); + + // crashes + //lua["obj"]["func"](cppobj); + //lua["obj"]["lua_func"](cppobj); + + // crashes + //lua["test"]["func"](cppobj); + //lua["test"]["lua_func"](cppobj); + + return 0; +} \ No newline at end of file diff --git a/examples/variadic_args.cpp b/examples/variadic_args.cpp index 6fe1f402..2f86f68e 100644 --- a/examples/variadic_args.cpp +++ b/examples/variadic_args.cpp @@ -18,7 +18,8 @@ int main() { int r = 0; for (auto v : va) { int value = v; // get argument out (implicit conversion) - // can also do int v = va.get(i); with index i + // can also do int v = v.as(); + // can also do int v = va.get(i); with index i r += value; } // Only have to add a, b was included from variadic_args and beyond diff --git a/sol/call.hpp b/sol/call.hpp index 8fd1a54d..b0dfa07e 100644 --- a/sol/call.hpp +++ b/sol/call.hpp @@ -79,7 +79,7 @@ namespace sol { typedef meta::tuple_types return_types; typedef typename traits::free_args_list args_list; // compile-time eliminate any functions that we know ahead of time are of improper arity - if (meta::find_in_pack_v, index_value...>::value) { + if (!traits::runtime_variadics_t::value && meta::find_in_pack_v, index_value...>::value) { return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } if (!traits::runtime_variadics_t::value && traits::free_arity != fxarity) { @@ -103,7 +103,7 @@ namespace sol { typedef meta::tuple_types return_types; typedef typename traits::free_args_list args_list; // compile-time eliminate any functions that we know ahead of time are of improper arity - if (meta::find_in_pack_v, index_value...>::value) { + if (!traits::runtime_variadics_t::value && meta::find_in_pack_v, index_value...>::value) { return overload_match_arity(types<>(), std::index_sequence<>(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } if (!traits::runtime_variadics_t::value && traits::free_arity != fxarity) { @@ -118,7 +118,7 @@ namespace sol { typedef meta::tuple_types return_types; typedef typename traits::free_args_list args_list; // compile-time eliminate any functions that we know ahead of time are of improper arity - if (meta::find_in_pack_v, index_value...>::value) { + if (!traits::runtime_variadics_t::value && meta::find_in_pack_v, index_value...>::value) { return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } if (!traits::runtime_variadics_t::value && traits::free_arity != fxarity) { @@ -239,6 +239,7 @@ namespace sol { return f(L); } }; + #ifdef SOL_NOEXCEPT_FUNCTION_TYPE template struct agnostic_lua_call_wrapper { diff --git a/sol/compatibility/5.1.0.h b/sol/compatibility/5.1.0.h index 7d520d20..e07070d3 100644 --- a/sol/compatibility/5.1.0.h +++ b/sol/compatibility/5.1.0.h @@ -167,6 +167,10 @@ inline int luaL_loadbufferx(lua_State* L, const char* buff, size_t size, const c return lua_load(L, kepler_lua_compat_get_string, &ls, name/*, mode*/); } +inline int luaL_loadfilex(lua_State* L, const char* filename, const char*) { + return luaL_loadfile(L, filename/*, mode*/); +} + #endif // LuaJIT 2.1.x beta and beyond #endif /* Lua 5.1 */ diff --git a/sol/container_traits.hpp b/sol/container_traits.hpp index eae539c5..705ec691 100644 --- a/sol/container_traits.hpp +++ b/sol/container_traits.hpp @@ -28,35 +28,13 @@ namespace sol { + template + struct container_traits; + namespace container_detail { + template - struct has_find { - private: - typedef std::array one; - typedef std::array two; - - template static one test(decltype(std::declval().find(std::declval>()))*); - template static two test(...); - - public: - static const bool value = sizeof(test(0)) == sizeof(char); - }; - - template - struct has_push_back { - private: - typedef std::array one; - typedef std::array two; - - template static one test(decltype(std::declval().push_back(std::declval>()))*); - template static two test(...); - - public: - static const bool value = sizeof(test(0)) == sizeof(char); - }; - - template - struct has_clear { + struct has_clear_test { private: typedef std::array one; typedef std::array two; @@ -69,129 +47,372 @@ namespace sol { }; template - struct has_insert { + struct has_size_test { private: typedef std::array one; typedef std::array two; - template static one test(decltype(std::declval().insert(std::declval>(), std::declval>()))*); + template static one test(decltype(&C::size)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; + template + struct has_erase_after_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(std::declval().erase_after(std::declval>()))*); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_find_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(std::declval().find(std::declval>()))*); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_find_test::value>> { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(std::declval().find(std::declval>()))*); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_erase_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(std::declval().erase(std::declval()))*); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_traits_find_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(&C::find)); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_traits_insert_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(&C::insert)); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_traits_erase_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(&C::erase)); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_traits_index_set_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(&C::index_set)); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_traits_index_get_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(&C::index_get)); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_traits_set_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(&C::set)); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_traits_get_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(&C::get)); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_traits_pairs_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(&C::pairs)); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_traits_add_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(&C::add)); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + using has_size = meta::boolean::value>; + + template + using has_clear = meta::boolean::value>; + + template + using has_find = meta::boolean::value>; + + template + using has_erase = meta::boolean::value>; + + template + using has_erase_after = meta::boolean::value>; + + template + using has_traits_get = meta::boolean::value>; + + template + using has_traits_set = meta::boolean::value>; + + template + using has_traits_index_get = meta::boolean::value>; + + template + using has_traits_index_set = meta::boolean::value>; + + template + using has_traits_pairs = meta::boolean::value>; + + template + using has_traits_add = meta::boolean::value>; + + template + using has_traits_size = has_size; + + template + using has_traits_clear = has_clear; + + template + using has_traits_find = meta::boolean::value>; + + template + using has_traits_insert = meta::boolean::value>; + + template + using has_traits_insert = meta::boolean::value>; + + template + using has_traits_erase = meta::boolean::value>; + + template + decltype(auto) get_key(std::false_type, T&& t) { + return std::forward(t); + } + + template + decltype(auto) get_key(std::true_type, T&& t) { + return t.first; + } + + template + decltype(auto) get_value(std::false_type, T&& t) { + return std::forward(t); + } + + template + decltype(auto) get_value(std::true_type, T&& t) { + return t.second; + } + template struct container_traits_default { private: typedef std::remove_pointer_t> T; public: - typedef std::false_type is_container; - typedef std::false_type is_associative; typedef lua_nil_t iterator; typedef lua_nil_t value_type; - static lua_nil_t get(lua_State* L, T* self, stack_object key) { - (void)self; - (void)key; - luaL_error(L, "sol: cannot call 'get(key)' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); + static int get(lua_State* L) { + return luaL_error(L, "sol: cannot call 'get(key)' on type '%s': it is not recognized as a container", detail::demangle().c_str()); + } + + static int index_get(lua_State* L) { + return luaL_error(L, "sol: cannot call 'container[key]' on type '%s': it is not recognized as a container", detail::demangle().c_str()); + } + + static int set(lua_State* L) { + return luaL_error(L, "sol: cannot call 'set(key, value)' on type '%s': it is not recognized as a container", detail::demangle().c_str()); + } + + static int index_set(lua_State* L) { + return luaL_error(L, "sol: cannot call 'container[key] = value' on type '%s': it is not recognized as a container", detail::demangle().c_str()); + } + + static int add(lua_State* L) { + return luaL_error(L, "sol: cannot call 'add' on type '%s': it is not recognized as a container", detail::demangle().c_str()); + } + + static int insert(lua_State* L) { + return luaL_error(L, "sol: cannot call 'insert' on type '%s': it is not recognized as a container", detail::demangle().c_str()); + } + + static int find(lua_State* L) { + return luaL_error(L, "sol: cannot call 'find' on type '%s': it is not recognized as a container", detail::demangle().c_str()); + } + + static int size(lua_State* L) { + return luaL_error(L, "sol: cannot call 'end' on type '%s': it is not recognized as a container", detail::demangle().c_str()); + } + + static int clear(lua_State* L) { + return luaL_error(L, "sol: cannot call 'clear' on type '%s': it is not recognized as a container", detail::demangle().c_str()); + } + + static int erase(lua_State* L) { + return luaL_error(L, "sol: cannot call 'erase' on type '%s': it is not recognized as a container", detail::demangle().c_str()); + } + + static int pairs(lua_State* L) { + return luaL_error(L, "sol: cannot call '__pairs' on type '%s': it is not recognized as a container", detail::demangle().c_str()); + } + + static iterator begin(lua_State* L, T& self) { + luaL_error(L, "sol: cannot call 'being' on type '%s': it is not recognized as a container", detail::demangle().c_str()); return lua_nil; } - static lua_nil_t index_get(lua_State* L, T* self, stack_object key) { - (void)self; - (void)key; - luaL_error(L, "sol: cannot call 'container[key]' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); - return lua_nil; - } - - static lua_nil_t index_set(lua_State* L, T* self, stack_object key, stack_object value) { - (void)self; - (void)key; - (void)value; - luaL_error(L, "sol: cannot call 'container[key] = value' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); - return lua_nil; - } - - static lua_nil_t add(lua_State* L, T* self, stack_object key, stack_object value) { - (void)self; - (void)key; - (void)value; - luaL_error(L, "sol: cannot call '%s' on type '%s' because it is not recognized as a container", value.valid() ? "add(key, value)" : "add(value)", detail::demangle().c_str()); - return lua_nil; - } - - static lua_nil_t insert(lua_State* L, T* self, stack_object where, stack_object value) { - (void)self; - (void)where; - (void)value; - luaL_error(L, "sol: cannot call 'container[key] = value' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); - return lua_nil; - } - - static lua_nil_t find(lua_State* L, T* self, stack_object key) { - (void)self; - (void)key; - luaL_error(L, "sol: cannot call 'container[key] = value' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); - return lua_nil; - } - - static lua_nil_t begin(lua_State* L, T* self) { - (void)self; - luaL_error(L, "sol: cannot call 'begin' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); - return lua_nil; - } - - static lua_nil_t end(lua_State* L, T* self) { - (void)self; - luaL_error(L, "sol: cannot call 'end' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); - return lua_nil; - } - - static lua_nil_t size(lua_State* L, T* self) { - (void)self; - luaL_error(L, "sol: cannot call 'end' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); - return lua_nil; - } - - static lua_nil_t clear(lua_State* L, T* self) { - (void)self; - luaL_error(L, "sol: cannot call 'clear' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); - return lua_nil; - } - - static lua_nil_t erase(lua_State* L, T* self, stack_object key) { - (void)self; - (void)key; - luaL_error(L, "sol: cannot call 'erase' on type '%s' because it is not recognized as a container", detail::demangle().c_str()); + static iterator end(lua_State* L, T& self) { + luaL_error(L, "sol: cannot call 'end' on type '%s': it is not recognized as a container", detail::demangle().c_str()); return lua_nil; } }; + template - struct container_traits_default>, meta::has_value_type>, meta::has_iterator>>::value - >> { + struct container_traits_default> + , meta::has_value_type> + , meta::has_iterator> + >::value + >> { private: typedef std::remove_pointer_t> T; + public: + typedef std::true_type is_container; + typedef meta::is_associative is_associative; + private: + typedef meta::is_lookup is_lookup; typedef typename T::iterator iterator; - typedef std::conditional_t> KV; + typedef typename T::value_type value_type; + typedef std::conditional_t, std::pair> + > KV; typedef typename KV::first_type K; typedef typename KV::second_type V; - typedef std::remove_reference_t())> iterator_return; - typedef typename meta::iterator_tag::type iterator_category; - typedef std::conditional_t::value, - V, - std::conditional_t()) - > + typedef decltype(*std::declval()) iterator_return; + typedef typename meta::iterator_tag::type iterator_category; + typedef std::is_same is_input_iterator; + typedef std::conditional_t, + std::conditional_t, iterator_return> > push_type; + typedef std::is_copy_assignable is_copyable; + typedef meta::neg + , std::is_const> + , meta::neg + >> is_writable; + typedef meta::unqualified_t>()))> key_type; + typedef meta::all, meta::neg>> is_linear_integral; + + struct iter { + T& source; + iterator it; + + iter(T& source, iterator it) : source(source), it(std::move(it)) {} + }; static auto& get_src(lua_State* L) { #ifdef SOL_SAFE_USERTYPE auto p = stack::check_get(L, 1); if (!p || p.value() == nullptr) { - luaL_error(L, "sol: 'self' argument is not the proper type (pass 'self' as first argument with ':' or call on proper type)"); + luaL_error(L, "sol: 'self' argument is nil or not of type '%s' (pass 'self' as first argument with ':' or call on proper type)", detail::demangle().c_str()); } return *p.value(); #else @@ -199,174 +420,461 @@ namespace sol { #endif // Safe getting with error } - public: - - static int delegate_call(lua_State* L) { - static std::unordered_map calls{ - { "add", &real_add_call }, - { "insert", &real_insert_call }, - { "clear", &real_clear_call }, - { "find", &real_find_call }, - { "get", &real_get_call } - }; - auto maybename = stack::check_get(L, 2); - if (maybename) { - auto& name = *maybename; - auto it = calls.find(name); - if (it != calls.cend()) { - return stack::push(L, it->second); - } - } - return stack::push(L, lua_nil); + static int get_associative(std::true_type, lua_State* L, iterator& it) { + auto& v = *it; + return stack::push_reference(L, v.second); } - static int real_index_call_associative(std::true_type, lua_State* L) { - auto k = stack::check_get(L, 2); - if (k) { - auto& src = get_src(L); - using std::end; - auto it = detail::find(src, *k); - if (it != end(src)) { - auto& v = *it; - return stack::stack_detail::push_reference(L, v.second); - } - } - else { - return delegate_call(L); - } - return stack::push(L, lua_nil); + static int get_associative(std::false_type, lua_State* L, iterator& it) { + return stack::push_reference(L, *it); } - static int real_index_call_associative(std::false_type, lua_State* L) { - auto& src = get_src(L); - auto maybek = stack::check_get(L, 2); - if (maybek) { - using std::begin; - auto it = begin(src); - K k = *maybek; - if (k > src.size() || k < 1) { + static int get_category(std::input_iterator_tag, lua_State* L, T& self, K& key) { + if (key < 1) { + return stack::push(L, lua_nil); + } + auto it = begin(L, self); + auto e = end(L, self); + if (it == e) { + return stack::push(L, lua_nil); + } + while (key > 1) { + --key; + ++it; + if (it == e) { return stack::push(L, lua_nil); } - --k; - std::advance(it, k); - return stack::stack_detail::push_reference(L, *it); } - else { - return delegate_call(L); - } - - return stack::push(L, lua_nil); + return get_associative(is_associative(), L, it); } - static int real_index_call(lua_State* L) { - return real_index_call_associative(is_associative(), L); + static int get_category(std::random_access_iterator_tag, lua_State* L, T& self, K& key) { + std::size_t len = size_start(L, self); + if (key < 1 || key > len) { + return stack::push(L, lua_nil); + } + --key; + auto it = std::next(begin(L, self), key); + return get_associative(is_associative(), L, it); } - static int real_get_call(lua_State* L) { - return real_index_call_associative(is_associative(), L); + static int get_it(std::true_type, lua_State* L, T& self, K& key) { + return get_category(iterator_category(), L, self, key); } - static int real_new_index_call_const(std::false_type, std::false_type, lua_State* L) { - return luaL_error(L, "sol: cannot write to a const value type or an immutable iterator (e.g., std::set)"); + static int get_comparative(std::true_type, lua_State* L, T& self, K& key) { + auto fx = [&](const value_type& r) -> bool { + return key == get_key(is_associative(), r); + }; + auto e = end(L, self); + auto it = std::find_if(begin(L, self), e, std::ref(fx)); + if (it == e) { + return stack::push(L, lua_nil); + } + return get_associative(is_associative(), L, it); } - static int real_new_index_call_const(std::false_type, std::true_type, lua_State* L) { - return luaL_error(L, "sol: cannot write to a const value type or an immutable iterator (e.g., std::set)"); + static int get_comparative(std::false_type, lua_State* L, T& self, K& key) { + return luaL_error(L, "cannot get this key on '%s': no suitable way to increment iterator and compare to key value '%s'", detail::demangle().data(), detail::demangle().data()); } - static int real_new_index_call_fixed(std::true_type, lua_State* L) { - auto& src = get_src(L); -#ifdef SOL_CHECK_ARGUMENTS - auto maybek = stack::check_get(L, 2); - if (!maybek) { - return luaL_error(L, "sol: improper key of type %s for %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); - } - K& k = *maybek; -#else - K k = stack::get(L, 2); -#endif - using std::end; - auto it = detail::find(src, k); - if (it != end(src)) { - auto& v = *it; - v.second = stack::get(L, 3); - } - else { - src.insert(it, { std::move(k), stack::get(L, 3) }); - } - return 0; + static int get_it(std::false_type, lua_State* L, T& self, K& key) { + return get_comparative(meta::supports_op_equal(), L, self, key); } - static int real_new_index_call_fixed(std::false_type, lua_State* L) { - auto& src = get_src(L); -#ifdef SOL_CHECK_ARGUMENTS - auto maybek = stack::check_get(L, 2); - if (!maybek) { - return luaL_error(L, "sol: improper key of type %s for %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); - } - K& k = *maybek; -#else - K k = stack::get(L, 2); -#endif - using std::end; - auto it = detail::find(src, k); - if (it != end(src)) { - auto& v = *it; - v.second = stack::get(L, 3); - } - else { - return luaL_error(L, "sol: cannot insert key of type %s to into %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); - } - return 0; + static void set_associative(std::true_type, iterator& it, stack_object value) { + auto& v = *it; + v.second = value.as(); } - static int real_new_index_call_const(std::true_type, std::true_type, lua_State* L) { - return real_new_index_call_fixed(std::integral_constant::value>(), L); + static void set_associative(std::false_type, iterator& it, stack_object value) { + auto& v = *it; + v = value.as(); } - static int real_new_index_call_const(std::true_type, std::false_type, lua_State* L) { - auto& src = get_src(L); -#ifdef SOL_CHECK_ARGUMENTS - auto maybek = stack::check_get(L, 2); - if (!maybek) { - return luaL_error(L, "sol: improper index of type %s to a %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); - } - K& k = *maybek; -#else - K k = stack::get(L, 2); -#endif - using std::begin; - auto it = begin(src); -#ifdef SOL_CHECK_ARGUMENTS - if (k < 1) { - return luaL_error(L, "sol: out of bounds index to a %s", detail::demangle().c_str()); - } -#endif - --k; - if (k == src.size()) { - real_add_call_push(std::integral_constant::value && std::is_copy_constructible::value>(), L, src, 1); - return 0; - } -#ifdef SOL_CHECK_ARGUMENTS - if (k > src.size()) { - return luaL_error(L, "sol: out of bounds index to a %s", detail::demangle().c_str()); - } -#endif - std::advance(it, k); - *it = stack::get(L, 3); - return 0; + static void set_writable(std::true_type, lua_State*, T&, iterator& it, stack_object value) { + set_associative(is_associative(), it, std::move(value)); } - static int real_new_index_call(lua_State* L) { - return real_new_index_call_const(meta::neg, std::is_const, meta::neg>>>(), meta::all>(), L); + static void set_writable(std::false_type, lua_State* L, T&, iterator&, stack_object) { + luaL_error(L, "cannot perform a 'set': '%s's iterator reference is not writable (non-copy-assignable or const)", detail::demangle().data()); } - static int real_pairs_next_call_assoc(std::true_type, lua_State* L) { - using std::end; + static void set_category(std::input_iterator_tag, lua_State* L, T& self, stack_object okey, stack_object value) { + decltype(auto) key = okey.as(); + auto e = end(L, self); + auto it = begin(L, self); + auto backit = it; + for (; key > 1 && it != e; --key, ++it) { + backit = it; + } + if (it == e) { + if (key == 1) { + add_copyable(is_copyable(), L, self, std::move(value), meta::has_insert_after::value ? backit : it); + return; + } + // TODO: error here in safety mode + return; + } + set_writable(is_writable(), L, self, it, std::move(value)); + } + + static void set_category(std::random_access_iterator_tag, lua_State* L, T& self, stack_object okey, stack_object value) { + decltype(auto) key = okey.as(); + std::size_t len = size_start(L, self); + if (key < 1) { + // error here in safety mode + return; + } + --key; + if (key == len) { + add_copyable(is_copyable(), L, self, std::move(value)); + return; + } + else if (key > len) { + // error here in safety mode + return; + } + auto it = std::next(begin(L, self), key); + set_writable(is_writable(), L, self, it, std::move(value)); + } + + static void set_comparative(std::true_type, lua_State* L, T& self, stack_object okey, stack_object value) { + decltype(auto) key = okey.as(); + if (!is_writable::value) { + luaL_error(L, "cannot perform a 'set': '%s's iterator reference is not writable (non-copy-assignable or const)", detail::demangle().data());; + return; + } + auto fx = [&](const value_type& r) -> bool { + return key == get_key(is_associative(), r); + }; + auto e = end(L, self); + auto it = std::find_if(begin(L, self), e, std::ref(fx)); + if (it == e) { + return; + } + set_writable(is_writable(), L, self, it, std::move(value)); + } + + static void set_comparative(std::false_type, lua_State* L, T& self, stack_object key, stack_object value) { + luaL_error(L, "cannot set this value on '%s': no suitable way to increment iterator or compare to '%s' key", detail::demangle().data(), detail::demangle().data()); + } + + static void set_associative_insert(std::true_type, lua_State*, T& self, iterator& it, K& key, stack_object value) { + self.insert(it, value_type(key, value.as())); + } + + static void set_associative_insert(std::false_type, lua_State*, T& self, iterator& it, K& key, stack_object) { + self.insert(it, key); + } + + static void set_associative_find(std::true_type, lua_State* L, T& self, stack_object okey, stack_object value) { + decltype(auto) key = okey.as(); + auto it = self.find(key); + if (it == end(L, self)) { + set_associative_insert(is_associative(), L, self, it, key, std::move(value)); + return; + } + set_writable(is_writable(), L, self, it, std::move(value)); + } + + static void set_associative_find(std::false_type, lua_State* L, T& self, stack_object key, stack_object value) { + set_comparative(meta::supports_op_equal(), L, self, std::move(key), std::move(value)); + } + + static void set_it(std::true_type, lua_State* L, T& self, stack_object key, stack_object value) { + set_category(iterator_category(), L, self, std::move(key), std::move(value)); + } + + static void set_it(std::false_type, lua_State* L, T& self, stack_object key, stack_object value) { + set_associative_find(meta::all, meta::any>(), L, self, std::move(key), std::move(value)); + } + + static int find_has_associative_lookup(std::true_type, lua_State* L, T& self) { + decltype(auto) key = stack::get(L, 2); + auto it = self.find(key); + if (it == end(L, self)) { + return stack::push(L, lua_nil); + } + return get_associative(is_associative(), L, it); + } + + static int find_has_associative_lookup(std::false_type, lua_State* L, T& self) { + decltype(auto) value = stack::get(L, 2); + auto it = self.find(value); + if (it == end(L, self)) { + return stack::push(L, lua_nil); + } + return get_associative(is_associative(), L, it); + } + + static int find_has(std::true_type, lua_State* L, T& self) { + return find_has_associative_lookup(meta::any(), L, self); + } + + static int find_associative_lookup(std::true_type, lua_State* L, iterator& it, std::size_t) { + return get_associative(is_associative(), L, it); + } + + static int find_associative_lookup(std::false_type, lua_State* L, iterator&, std::size_t index) { + return stack::push(L, index); + } + + static int find_comparative(std::false_type, lua_State* L, T& ) { + return luaL_error(L, "cannot call 'find' on '%s': there is no 'find' function and the value_type is not equality comparable", detail::demangle().c_str()); + } + + static int find_comparative(std::true_type, lua_State* L, T& self) { + V value = stack::get(L, 2); + auto it = begin(L, self); + auto e = end(L, self); + std::size_t index = 1; + for (;; ++it, ++index) { + if (it == e) { + return stack::push(L, lua_nil); + } + if (value == get_value(is_associative(), *it)) { + break; + } + } + return find_associative_lookup(meta::any(), L, it, index); + } + + static int find_has(std::false_type, lua_State* L, T& self) { + return find_comparative(meta::supports_op_equal(), L, self); + } + + static void add_insert_after(std::false_type, lua_State* L, T& self, stack_object value, iterator&) { + add_insert_after(std::false_type(), L, self, value); + } + + static void add_insert_after(std::false_type, lua_State* L, T&, stack_object) { + luaL_error(L, "cannot call 'add' on type '%s': no suitable insert/push_back C++ functions", sol::detail::demangle().data()); + } + + static void add_insert_after(std::true_type, lua_State* L, T& self, stack_object value, iterator& at) { + self.insert_after(at, value.as()); + } + + static void add_insert_after(std::true_type, lua_State* L, T& self, stack_object value) { + auto backit = self.before_begin(); + { + auto e = end(L, self); + for (auto it = begin(L, self); it != e; ++backit, ++it) {} + } + return add_insert_after(std::true_type(), L, self, value, backit); + } + + static void add_insert(std::true_type, lua_State*, T& self, stack_object value, iterator& at) { + self.insert(at, value.as()); + } + + static void add_insert(std::true_type, lua_State* L, T& self, stack_object value) { + auto at = end(L, self); + add_insert(std::true_type(), L, self, value, at); + } + + static void add_insert(std::false_type, lua_State* L, T& self, stack_object value, iterator& at) { + return add_insert_after(meta::has_insert_after(), L, self, std::move(value), at); + } + + static void add_insert(std::false_type, lua_State* L, T& self, stack_object value) { + return add_insert_after(meta::has_insert_after(), L, self, std::move(value)); + } + + static void add_push_back(std::true_type, lua_State*, T& self, stack_object value, iterator&) { + self.push_back(value.as()); + } + + static void add_push_back(std::true_type, lua_State*, T& self, stack_object value) { + self.push_back(value.as()); + } + + static void add_push_back(std::false_type, lua_State* L, T& self, stack_object value, iterator& at) { + add_insert(meta::has_insert(), L, self, value, at); + } + + static void add_push_back(std::false_type, lua_State* L, T& self, stack_object value) { + add_insert(meta::has_insert(), L, self, value); + } + + static void add_associative(std::true_type, lua_State* L, T& self, stack_object key, iterator& at) { + self.insert(at, value_type(key.as(), stack::get(L, 3))); + } + + static void add_associative(std::true_type, lua_State* L, T& self, stack_object key) { + auto at = end(L, self); + add_associative(std::true_type(), L, self, std::move(key), at); + } + + static void add_associative(std::false_type, lua_State* L, T& self, stack_object value, iterator& at) { + add_push_back(meta::has_push_back(), L, self, value, at); + } + + static void add_associative(std::false_type, lua_State* L, T& self, stack_object value) { + add_push_back(meta::has_push_back(), L, self, value); + } + + static void add_copyable(std::true_type, lua_State* L, T& self, stack_object value, iterator& at) { + add_associative(is_associative(), L, self, std::move(value), at); + } + + static void add_copyable(std::true_type, lua_State* L, T& self, stack_object value) { + add_associative(is_associative(), L, self, value); + } + + static void add_copyable(std::false_type, lua_State* L, T& self, stack_object value, iterator&) { + add_copyable(std::false_type(), L, self, std::move(value)); + } + + static void add_copyable(std::false_type, lua_State* L, T&, stack_object) { + luaL_error(L, "cannot call 'add' on '%s': value_type is non-copyable", detail::demangle().data()); + } + + static void insert_lookup(std::true_type, lua_State* L, T& self, stack_object, stack_object value) { + // TODO: should we warn or error about someone calling insert on an ordered / lookup container with no associativity? + add_copyable(std::true_type(), L, self, std::move(value)); + } + + static void insert_lookup(std::false_type, lua_State* L, T& self, stack_object where, stack_object value) { + auto it = begin(L, self); + auto key = where.as(); + --key; + std::advance(it, key); + self.insert(it, value.as()); + } + + static void insert_after_has(std::true_type, lua_State* L, T& self, stack_object where, stack_object value) { + auto key = where.as(); + auto backit = self.before_begin(); + { + --key; + auto e = end(L, self); + for (auto it = begin(L, self); key > 0; ++backit, ++it, --key) { + if (backit == e) { + // TODO: error here in safety mode + return; + } + } + } + self.insert_after(backit, value.as()); + } + + static void insert_after_has(std::false_type, lua_State* L, T&, stack_object, stack_object) { + luaL_error(L, "cannot call 'insert' on '%s': no suitable or similar functionality detected on this container", detail::demangle().data()); + } + + static void insert_has(std::true_type, lua_State* L, T& self, stack_object key, stack_object value) { + insert_lookup(meta::all(), L, self, std::move(key), std::move(value)); + } + + static void insert_has(std::false_type, lua_State* L, T& self, stack_object where, stack_object value) { + insert_after_has(meta::has_insert_after(), L, self, where, value); + } + + static void insert_copyable(std::true_type, lua_State* L, T& self, stack_object key, stack_object value) { + insert_has(has_find(), L, self, std::move(key), std::move(value)); + } + + static void insert_copyable(std::false_type, lua_State* L, T&, stack_object, stack_object) { + luaL_error(L, "cannot call 'insert' on '%s': value_type is non-copyable", detail::demangle().data()); + } + + static void erase_integral(std::true_type, lua_State* L, T& self, K& key) { + auto it = begin(L, self); + --key; + std::advance(it, key); + self.erase(it); + } + + static void erase_integral(std::false_type, lua_State* L, T& self, const K& key) { + auto fx = [&](const value_type& r) -> bool { + return key == r; + }; + auto e = end(L, self); + auto it = std::find_if(begin(L, self), e, std::ref(fx)); + if (it == e) { + return; + } + self.erase(it); + } + + static void erase_associative_lookup(std::true_type, lua_State*, T& self, const K& key) { + self.erase(key); + } + + static void erase_associative_lookup(std::false_type, lua_State* L, T& self, K& key) { + erase_integral(std::is_integral(), L, self, key); + } + + static void erase_after_has(std::true_type, lua_State* L, T& self, K& key) { + auto backit = self.before_begin(); + { + --key; + auto e = end(L, self); + for (auto it = begin(L, self); key > 0; ++backit, ++it, --key) { + if (backit == e) { + // TODO: error here in safety mode + return; + } + } + } + self.erase_after(backit); + } + + static void erase_after_has(std::false_type, lua_State* L, T& self, const K& key) { + luaL_error(L, "sol: cannot call erase on '%s'", detail::demangle().c_str()); + } + + static void erase_has(std::true_type, lua_State* L, T& self, K& key) { + erase_associative_lookup(meta::any(), L, self, key); + } + + static void erase_has(std::false_type, lua_State* L, T& self, K& key) { + erase_after_has(has_erase_after(), L, self, key); + } + + static auto size_has(std::false_type, lua_State* L, T& self) { + return std::distance(container_traits::begin(L, self), container_traits::end(L, self)); + } + + static auto size_has(std::true_type, lua_State*, T& self) { + return self.size(); + } + + static void clear_has(std::true_type, lua_State*, T& self) { + self.clear(); + } + + static void clear_has(std::false_type, lua_State* L, T&) { + luaL_error(L, "sol: cannot call clear on '%s'", detail::demangle().c_str()); + } + + static int get_start(lua_State* L, T& self, K& key) { + return get_it(is_linear_integral(), L, self, key); + } + + static void set_start(lua_State* L, T& self, stack_object key, stack_object value) { + set_it(is_linear_integral(), L, self, std::move(key), std::move(value)); + } + + static std::size_t size_start(lua_State* L, T& self) { + return size_has(has_size(), L, self); + } + + static void clear_start(lua_State* L, T& self) { + clear_has(has_clear(), L, self); + } + + static void erase_start(lua_State* L, T& self, K& key) { + erase_has(has_erase(), L, self, key); + } + + static int next_associative(std::true_type, lua_State* L) { iter& i = stack::get>(L, 1); auto& source = i.source; auto& it = i.it; - if (it == end(source)) { + if (it == container_traits::end(L, source)) { return 0; } int p; @@ -376,22 +884,20 @@ namespace sol { return p; } - static int real_pairs_call_assoc(std::true_type, lua_State* L) { + static int pairs_associative(std::true_type, lua_State* L) { auto& src = get_src(L); - using std::begin; - stack::push(L, pairs_next_call); - stack::push>(L, src, begin(src)); + stack::push(L, next); + stack::push>(L, src, container_traits::begin(L, src)); stack::push(L, 1); return 3; } - static int real_pairs_next_call_assoc(std::false_type, lua_State* L) { - using std::end; + static int next_associative(std::false_type, lua_State* L) { iter& i = stack::get>(L, 1); auto& source = i.source; auto& it = i.it; K k = stack::get(L, 2); - if (it == end(source)) { + if (it == container_traits::end(L, source)) { return 0; } int p; @@ -401,137 +907,222 @@ namespace sol { return p; } - static int real_pairs_call_assoc(std::false_type, lua_State* L) { + static int pairs_associative(std::false_type, lua_State* L) { auto& src = get_src(L); - using std::begin; - stack::push(L, pairs_next_call); - stack::push>(L, src, begin(src)); + stack::push(L, next); + stack::push>(L, src, container_traits::begin(L, src)); stack::push(L, 0); return 3; } - static int real_pairs_next_call(lua_State* L) { - return real_pairs_next_call_assoc(is_associative(), L); + static int next(lua_State* L) { + return next_associative(is_associative(), L); } - static int real_pairs_call(lua_State* L) { - return real_pairs_call_assoc(is_associative(), L); + public: + static int get(lua_State* L) { + auto& self = get_src(L); + decltype(auto) key = stack::get(L); + return get_start(L, self, key); } - static int real_length_call(lua_State*L) { - auto& src = get_src(L); - return stack::push(L, src.size()); + static int index_get(lua_State* L) { + return get(L); } - static int real_add_call_insert(std::true_type, lua_State*L, T& src, int boost = 0) { - using std::end; - src.insert(end(src), stack::get(L, 2 + boost)); + static int set(lua_State* L) { + stack_object value = stack_object(L, raw_index(3)); + if (type_of(L, 3) == type::nil) { + return erase(L); + } + auto& self = get_src(L); + set_start(L, self, stack_object(L, raw_index(2)), std::move(value)); return 0; } - static int real_add_call_insert(std::false_type, lua_State*L, T&, int = 0) { - static const std::string& s = detail::demangle(); - return luaL_error(L, "sol: cannot call insert on type %s", s.c_str()); + static int index_set(lua_State* L) { + return set(L); } - static int real_add_call_push(std::true_type, lua_State*L, T& src, int boost = 0) { - src.push_back(stack::get(L, 2 + boost)); + static int add(lua_State* L) { + auto& self = get_src(L); + add_copyable(is_copyable(), L, self, stack_object(L, raw_index(2))); return 0; } - static int real_add_call_push(std::false_type, lua_State*L, T& src, int boost = 0) { - return real_add_call_insert(std::integral_constant::value && std::is_copy_constructible::value>(), L, src, boost); + static int insert(lua_State* L) { + auto& self = get_src(L); + insert_copyable(meta::any(), L, self, stack_object(L, raw_index(2)), stack_object(L, raw_index(3))); + return 0; } - static int real_add_call_associative(std::true_type, lua_State* L) { - return real_insert_call(L); + static int find(lua_State* L) { + auto& self = get_src(L); + return find_has(has_find(), L, self); } - static int real_add_call_associative(std::false_type, lua_State* L) { - auto& src = get_src(L); - return real_add_call_push(std::integral_constant::value && std::is_copy_constructible::value>(), L, src); - } - - static int real_add_call_capable(std::true_type, lua_State* L) { - return real_add_call_associative(is_associative(), L); - } - - static int real_add_call_capable(std::false_type, lua_State* L) { - static const std::string& s = detail::demangle(); - return luaL_error(L, "sol: cannot call add on type %s", s.c_str()); - } - - static int real_add_call(lua_State* L) { - return real_add_call_capable(std::integral_constant::value || detail::has_insert::value) && std::is_copy_constructible::value>(), L); - } - - static int real_insert_call_capable(std::false_type, std::false_type, lua_State*L) { - static const std::string& s = detail::demangle(); - return luaL_error(L, "sol: cannot call insert on type %s", s.c_str()); - } - - static int real_insert_call_capable(std::false_type, std::true_type, lua_State*L) { - return real_insert_call_capable(std::false_type(), std::false_type(), L); - } - - static int real_insert_call_capable(std::true_type, std::false_type, lua_State* L) { + static iterator begin(lua_State*, T& self) { using std::begin; - auto& src = get_src(L); - src.insert(std::next(begin(src), stack::get(L, 2)), stack::get(L, 3)); + return begin(self); + } + + static iterator end(lua_State*, T& self) { + using std::end; + return end(self); + } + + static int size(lua_State* L) { + auto& self = get_src(L); + std::size_t r = size_start(L, self); + return stack::push(L, r); + } + + static int clear(lua_State* L) { + auto& self = get_src(L); + clear_start(L, self); return 0; } - static int real_insert_call_capable(std::true_type, std::true_type, lua_State* L) { - return real_new_index_call(L); - } - - static int real_insert_call(lua_State*L) { - return real_insert_call_capable(std::integral_constant::value && std::is_copy_assignable::value>(), is_associative(), L); - } - - static int real_clear_call_capable(std::false_type, lua_State* L) { - static const std::string& s = detail::demangle(); - return luaL_error(L, "sol: cannot call clear on type %s", s.c_str()); - } - - static int real_clear_call_capable(std::true_type, lua_State* L) { - auto& src = get_src(L); - src.clear(); + static int erase(lua_State* L) { + auto& self = get_src(L); + decltype(auto) key = stack::get(L, 2); + erase_start(L, self, key); return 0; } - static int real_clear_call(lua_State*L) { - return real_clear_call_capable(std::integral_constant::value>(), L); + static int pairs(lua_State* L) { + return pairs_associative(is_associative(), L); + } + }; + + template + struct container_traits_default>>::value>> { + private: + typedef std::remove_pointer_t> T; + public: + typedef std::remove_extent_t value_type; + typedef value_type* iterator; + + private: + struct iter { + T& source; + iterator it; + + iter(T& source, iterator it) : source(source), it(std::move(it)) {} + }; + + static auto& get_src(lua_State* L) { +#ifdef SOL_SAFE_USERTYPE + auto p = stack::check_get(L, 1); + if (!p || p.value() == nullptr) { + luaL_error(L, "sol: 'self' argument is nil or not of type '%s' (pass 'self' as first argument with ':' or call on proper type)", detail::demangle().c_str()); + } + return *p.value(); +#else + return stack::get(L, 1); +#endif // Safe getting with error } - static int real_find_call_capable(std::false_type, std::false_type, lua_State*L) { - static const std::string& s = detail::demangle(); - return luaL_error(L, "sol: cannot call find on type %s", s.c_str()); - } - - static int real_find_call_capable(std::false_type, std::true_type, lua_State*L) { - return real_index_call(L); - } - - static int real_find_call_capable(std::true_type, std::false_type, lua_State* L) { - auto k = stack::check_get(L, 2); - if (k) { - auto& src = get_src(L); - auto it = src.find(*k); - if (it != src.end()) { - auto& v = *it; - return stack::stack_detail::push_reference(L, v); + static int find(std::true_type, lua_State* L) { + T& self = get_src(L); + decltype(auto) value = stack::get(L, 2); + std::size_t N = std::extent::value; + for (std::size_t idx = 0; idx < N; ++idx) { + const auto& v = self[idx]; + if (v == value) { + return stack::push(L, idx + 1); } } return stack::push(L, lua_nil); } - static int real_find_call_capable(std::true_type, std::true_type, lua_State* L) { - return real_index_call(L); + static int find(std::false_type, lua_State* L) { + return luaL_error(L, "sol: cannot call 'find' on '%s': no supported comparison operator for the value type", detail::demangle().c_str()); } - static int real_find_call(lua_State*L) { - return real_find_call_capable(std::integral_constant::value>(), is_associative(), L); + static int next(lua_State* L) { + iter& i = stack::get>(L, 1); + auto& source = i.source; + auto& it = i.it; + std::size_t k = stack::get(L, 2); + if (it == container_traits::end(L, source)) { + return 0; + } + int p; + p = stack::push_reference(L, k + 1); + p += stack::push_reference(L, *it); + std::advance(it, 1); + return p; + } + + public: + static int clear(lua_State* L) { + return luaL_error(L, "sol: cannot call 'clear' on type '%s': cannot remove all items from a fixed array", detail::demangle().c_str()); + } + + static int erase(lua_State* L) { + return luaL_error(L, "sol: cannot call 'erase' on type '%s': cannot remove an item from fixed arrays", detail::demangle().c_str()); + } + + static int add(lua_State* L) { + return luaL_error(L, "sol: cannot call 'add' on type '%s': cannot add to fixed arrays", detail::demangle().c_str()); + } + + static int insert(lua_State* L) { + return luaL_error(L, "sol: cannot call 'insert' on type '%s': cannot insert new entries into fixed arrays", detail::demangle().c_str()); + } + + static int get(lua_State* L) { + T& self = get_src(L); + std::size_t idx = stack::get(L, 2); + if (idx > std::extent::value || idx < 1) { + return stack::push(L, lua_nil); + } + --idx; + return stack::push_reference(L, self[idx]); + } + + static int index_get(lua_State* L) { + return get(L); + } + + static int set(lua_State* L) { + T& self = get_src(L); + std::size_t idx = stack::get(L, 2); + if (idx > std::extent::value || idx < 1) { + return luaL_error(L, "sol: index out of bounds on set"); + } + --idx; + self[idx] = stack::get(L, 3); + return 0; + } + + static int index_set(lua_State* L) { + return set(L); + } + + static int find(lua_State* L) { + return find(meta::supports_op_equal(), L); + } + + static int size(lua_State* L) { + return stack::push(L, std::extent::value); + } + + static int pairs(lua_State* L) { + auto& src = get_src(L); + stack::push(L, next); + stack::push>(L, src, container_traits::begin(L, src)); + stack::push(L, 0); + return 3; + } + + static iterator begin(lua_State* L, T& self) { + return std::addressof(self[0]); + } + + static iterator end(lua_State* L, T& self) { + return std::addressof(self[std::extent::value]); } }; } // container_detail diff --git a/sol/container_usertype_metatable.hpp b/sol/container_usertype_metatable.hpp index 30b747cf..8087badb 100644 --- a/sol/container_usertype_metatable.hpp +++ b/sol/container_usertype_metatable.hpp @@ -19,461 +19,179 @@ // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#ifndef SOL_CONTAINER_USERTYPE_HPP -#define SOL_CONTAINER_USERTYPE_HPP +#ifndef SOL_CONTAINER_USERTYPE_METATABLE_HPP +#define SOL_CONTAINER_USERTYPE_METATABLE_HPP #include "stack.hpp" +#include "container_traits.hpp" #include namespace sol { - namespace detail { - - template - struct has_find { - private: - typedef std::array one; - typedef std::array two; - - template static one test(decltype(std::declval().find(std::declval>()))*); - template static two test(...); - - public: - static const bool value = sizeof(test(0)) == sizeof(char); - }; - - template - struct has_push_back { - private: - typedef std::array one; - typedef std::array two; - - template static one test(decltype(std::declval().push_back(std::declval>()))*); - template static two test(...); - - public: - static const bool value = sizeof(test(0)) == sizeof(char); - }; - - template - struct has_clear { - private: - typedef std::array one; - typedef std::array two; - - template static one test(decltype(&C::clear)); - template static two test(...); - - public: - static const bool value = sizeof(test(0)) == sizeof(char); - }; - - template - struct has_insert { - private: - typedef std::array one; - typedef std::array two; - - template static one test(decltype(std::declval().insert(std::declval>(), std::declval>()))*); - template static two test(...); - - public: - static const bool value = sizeof(test(0)) == sizeof(char); - }; - - template - T& get_first(const T& t) { - return std::forward(t); - } - - template - decltype(auto) get_first(const std::pair& t) { - return t.first; - } - - template >> = meta::enabler> - auto find(C& c, I&& i) { - return c.find(std::forward(i)); - } - - template >> = meta::enabler> - auto find(C& c, I&& i) { - using std::begin; - using std::end; - return std::find_if(begin(c), end(c), [&i](auto&& x) { - return i == get_first(x); - }); - } - - } - - template + template struct container_usertype_metatable { - typedef meta::is_associative>> is_associative; - typedef meta::unqualified_t T; - typedef typename T::iterator I; - typedef std::conditional_t> KV; - typedef typename KV::first_type K; - typedef typename KV::second_type V; - typedef std::remove_reference_t())> IR; - typedef typename meta::iterator_tag::type tag_t; - typedef std::conditional_t::value, - V, - std::conditional_t()) - > - > push_type; + typedef std::remove_pointer_t> T; + typedef container_traits traits; + typedef container_detail::container_traits_default default_traits; - struct iter { - T& source; - I it; - iter(T& source, I it) : source(source), it(std::move(it)) {} - }; - - static auto& get_src(lua_State* L) { -#ifdef SOL_SAFE_USERTYPE - auto p = stack::check_get(L, 1); - if (!p || p.value() == nullptr) { - luaL_error(L, "sol: 'self' argument is not the proper type (pass 'self' as first argument with ':' or call on proper type)"); - } - return *p.value(); -#else - return stack::get(L, 1); -#endif // Safe getting with error + static int real_index_get_traits(std::true_type, lua_State* L) { + return traits::index_get(L); } - static int delegate_call(lua_State* L) { + static int real_index_get_traits(std::false_type, lua_State* L) { + return default_traits::index_get(L); + } + + static int real_index_call(lua_State* L) { static std::unordered_map calls{ + { "get", &real_get_call }, + { "set", &real_set_call }, { "add", &real_add_call }, { "insert", &real_insert_call }, { "clear", &real_clear_call }, { "find", &real_find_call }, - { "get", &real_get_call } + { "erase", &real_erase_call } }; auto maybename = stack::check_get(L, 2); if (maybename) { - auto& name = *maybename; + const std::string& name = *maybename; auto it = calls.find(name); if (it != calls.cend()) { return stack::push(L, it->second); } - } - return stack::push(L, lua_nil); + } + return real_index_get_traits(container_detail::has_traits_index_get(), L); } - static int real_index_call_associative(std::true_type, lua_State* L) { - auto k = stack::check_get(L, 2); - if (k) { - auto& src = get_src(L); - using std::end; - auto it = detail::find(src, *k); - if (it != end(src)) { - auto& v = *it; - return stack::stack_detail::push_reference(L, v.second); - } - } - return delegate_call(L); + static int real_get_traits(std::true_type, lua_State* L) { + return traits::get(L); } - static int real_index_call_associative(std::false_type, lua_State* L) { - auto& src = get_src(L); - auto maybek = stack::check_get(L, 2); - if (maybek) { - using std::begin; - auto it = begin(src); - K k = *maybek; - if (k > src.size() || k < 1) { - return stack::push(L, lua_nil); - } - --k; - std::advance(it, k); - return stack::stack_detail::push_reference(L, *it); - } - return delegate_call(L); - } - - static int real_index_call(lua_State* L) { - return real_index_call_associative(is_associative(), L); + static int real_get_traits(std::false_type, lua_State* L) { + return default_traits::get(L); } static int real_get_call(lua_State* L) { - return real_index_call_associative(is_associative(), L); + return real_get_traits(container_detail::has_traits_get(), L); } - static int real_new_index_call_const(std::false_type, std::false_type, lua_State* L) { - return luaL_error(L, "sol: cannot write to a const value type or an immutable iterator (e.g., std::set)"); + static int real_set_traits(std::true_type, lua_State* L) { + return traits::set(L); } - static int real_new_index_call_const(std::false_type, std::true_type, lua_State* L) { - return luaL_error(L, "sol: cannot write to a const value type or an immutable iterator (e.g., std::set)"); + static int real_set_traits(std::false_type, lua_State* L) { + return default_traits::set(L); } - static int real_new_index_call_fixed(std::true_type, lua_State* L) { - auto& src = get_src(L); -#ifdef SOL_CHECK_ARGUMENTS - auto maybek = stack::check_get(L, 2); - if (!maybek) { - return luaL_error(L, "sol: improper key of type %s for %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); - } - K& k = *maybek; -#else - K k = stack::get(L, 2); -#endif - using std::end; - auto it = detail::find(src, k); - if (it != end(src)) { - auto& v = *it; - v.second = stack::get(L, 3); - } - else { - src.insert(it, { std::move(k), stack::get(L, 3) }); - } - return 0; + static int real_set_call(lua_State* L) { + return real_set_traits(container_detail::has_traits_set(), L); + } + + static int real_index_set_traits(std::true_type, lua_State* L) { + return traits::index_set(L); } - static int real_new_index_call_fixed(std::false_type, lua_State* L) { - auto& src = get_src(L); -#ifdef SOL_CHECK_ARGUMENTS - auto maybek = stack::check_get(L, 2); - if (!maybek) { - return luaL_error(L, "sol: improper key of type %s for %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); - } - K& k = *maybek; -#else - K k = stack::get(L, 2); -#endif - using std::end; - auto it = detail::find(src, k); - if (it != end(src)) { - auto& v = *it; - v.second = stack::get(L, 3); - } - else { - return luaL_error(L, "sol: cannot insert key of type %s to into %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); - } - return 0; - } - - static int real_new_index_call_const(std::true_type, std::true_type, lua_State* L) { - return real_new_index_call_fixed(std::integral_constant::value>(), L); - } - - static int real_new_index_call_const(std::true_type, std::false_type, lua_State* L) { - auto& src = get_src(L); -#ifdef SOL_CHECK_ARGUMENTS - auto maybek = stack::check_get(L, 2); - if (!maybek) { - return luaL_error(L, "sol: improper index of type %s to a %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); - } - K& k = *maybek; -#else - K k = stack::get(L, 2); -#endif - using std::begin; - auto it = begin(src); -#ifdef SOL_CHECK_ARGUMENTS - if (k < 1) { - return luaL_error(L, "sol: out of bounds index to a %s", detail::demangle().c_str()); - } -#endif - --k; - if (k == src.size()) { - real_add_call_push(std::integral_constant::value && std::is_copy_constructible::value>(), L, src, 1); - return 0; - } -#ifdef SOL_CHECK_ARGUMENTS - if (k > src.size()) { - return luaL_error(L, "sol: out of bounds index to a %s", detail::demangle().c_str()); - } -#endif - std::advance(it, k); - *it = stack::get(L, 3); - return 0; + static int real_index_set_traits(std::false_type, lua_State* L) { + return default_traits::index_set(L); } static int real_new_index_call(lua_State* L) { - return real_new_index_call_const(meta::neg, std::is_const, meta::neg>>>(), meta::all>(), L); + return real_index_set_traits(container_detail::has_traits_index_set(), L); } - static int real_pairs_next_call_assoc(std::true_type, lua_State* L) { - using std::end; - iter& i = stack::get>(L, 1); - auto& source = i.source; - auto& it = i.it; - if (it == end(source)) { - return 0; - } - int p; - p = stack::push_reference(L, it->first); - p += stack::stack_detail::push_reference(L, it->second); - std::advance(it, 1); - return p; + static int real_pairs_traits(std::true_type, lua_State* L) { + return traits::pairs(L); } - static int real_pairs_call_assoc(std::true_type, lua_State* L) { - auto& src = get_src(L); - using std::begin; - stack::push(L, pairs_next_call); - stack::push>(L, src, begin(src)); - stack::push(L, 1); - return 3; - } - - static int real_pairs_next_call_assoc(std::false_type, lua_State* L) { - using std::end; - iter& i = stack::get>(L, 1); - auto& source = i.source; - auto& it = i.it; - K k = stack::get(L, 2); - if (it == end(source)) { - return 0; - } - int p; - p = stack::push_reference(L, k + 1); - p += stack::stack_detail::push_reference(L, *it); - std::advance(it, 1); - return p; - } - - static int real_pairs_call_assoc(std::false_type, lua_State* L) { - auto& src = get_src(L); - using std::begin; - stack::push(L, pairs_next_call); - stack::push>(L, src, begin(src)); - stack::push(L, 0); - return 3; - } - - static int real_pairs_next_call(lua_State* L) { - return real_pairs_next_call_assoc(is_associative(), L); + static int real_pairs_traits(std::false_type, lua_State* L) { + return default_traits::pairs(L); } static int real_pairs_call(lua_State* L) { - return real_pairs_call_assoc(is_associative(), L); + return real_pairs_traits(container_detail::has_traits_pairs(), L); } - static int real_length_call(lua_State*L) { - auto& src = get_src(L); - return stack::push(L, src.size()); + static int real_size_traits(std::true_type, lua_State* L) { + return traits::size(L); } - static int real_add_call_insert(std::true_type, lua_State*L, T& src, int boost = 0) { - using std::end; - src.insert(end(src), stack::get(L, 2 + boost)); - return 0; + static int real_size_traits(std::false_type, lua_State* L) { + return default_traits::size(L); } - static int real_add_call_insert(std::false_type, lua_State*L, T&, int = 0) { - static const std::string& s = detail::demangle(); - return luaL_error(L, "sol: cannot call insert on type %s", s.c_str()); + static int real_length_call(lua_State* L) { + return real_size_traits(container_detail::has_traits_size(), L); } - static int real_add_call_push(std::true_type, lua_State*L, T& src, int boost = 0) { - src.push_back(stack::get(L, 2 + boost)); - return 0; + static int real_add_traits(std::true_type, lua_State* L) { + return traits::add(L); } - static int real_add_call_push(std::false_type, lua_State*L, T& src, int boost = 0) { - return real_add_call_insert(std::integral_constant::value && std::is_copy_constructible::value>(), L, src, boost); - } - - static int real_add_call_associative(std::true_type, lua_State* L) { - return real_insert_call(L); - } - - static int real_add_call_associative(std::false_type, lua_State* L) { - auto& src = get_src(L); - return real_add_call_push(std::integral_constant::value && std::is_copy_constructible::value>(), L, src); - } - - static int real_add_call_capable(std::true_type, lua_State* L) { - return real_add_call_associative(is_associative(), L); - } - - static int real_add_call_capable(std::false_type, lua_State* L) { - static const std::string& s = detail::demangle(); - return luaL_error(L, "sol: cannot call add on type %s", s.c_str()); + static int real_add_traits(std::false_type, lua_State* L) { + return default_traits::add(L); } static int real_add_call(lua_State* L) { - return real_add_call_capable(std::integral_constant::value || detail::has_insert::value) && std::is_copy_constructible::value>(), L); + return real_add_traits(container_detail::has_traits_add(), L); } - static int real_insert_call_capable(std::false_type, std::false_type, lua_State*L) { - static const std::string& s = detail::demangle(); - return luaL_error(L, "sol: cannot call insert on type %s", s.c_str()); + static int real_insert_traits(std::true_type, lua_State* L) { + return traits::insert(L); } - static int real_insert_call_capable(std::false_type, std::true_type, lua_State*L) { - return real_insert_call_capable(std::false_type(), std::false_type(), L); + static int real_insert_traits(std::false_type, lua_State* L) { + return default_traits::insert(L); } - static int real_insert_call_capable(std::true_type, std::false_type, lua_State* L) { - using std::begin; - auto& src = get_src(L); - src.insert(std::next(begin(src), stack::get(L, 2)), stack::get(L, 3)); - return 0; + static int real_insert_call(lua_State* L) { + return real_insert_traits(container_detail::has_traits_insert(), L); } - static int real_insert_call_capable(std::true_type, std::true_type, lua_State* L) { - return real_new_index_call(L); + static int real_clear_traits(std::true_type, lua_State* L) { + return traits::clear(L); } - static int real_insert_call(lua_State*L) { - return real_insert_call_capable(std::integral_constant::value && std::is_copy_assignable::value>(), is_associative(), L); + static int real_clear_traits(std::false_type, lua_State* L) { + return default_traits::clear(L); } - static int real_clear_call_capable(std::false_type, lua_State* L) { - static const std::string& s = detail::demangle(); - return luaL_error(L, "sol: cannot call clear on type %s", s.c_str()); + static int real_clear_call(lua_State* L) { + return real_clear_traits(container_detail::has_traits_clear(), L); } - static int real_clear_call_capable(std::true_type, lua_State* L) { - auto& src = get_src(L); - src.clear(); - return 0; + static int real_erase_traits(std::true_type, lua_State* L) { + return traits::erase(L); } - static int real_clear_call(lua_State*L) { - return real_clear_call_capable(std::integral_constant::value>(), L); + static int real_erase_traits(std::false_type, lua_State* L) { + return default_traits::erase(L); } - static int real_find_call_capable(std::false_type, std::false_type, lua_State*L) { - static const std::string& s = detail::demangle(); - return luaL_error(L, "sol: cannot call find on type %s", s.c_str()); + static int real_erase_call(lua_State* L) { + return real_erase_traits(container_detail::has_traits_erase(), L); } - static int real_find_call_capable(std::false_type, std::true_type, lua_State*L) { - return real_index_call(L); + static int real_find_traits(std::true_type, lua_State* L) { + return traits::find(L); } - static int real_find_call_capable(std::true_type, std::false_type, lua_State* L) { - auto k = stack::check_get(L, 2); - if (k) { - auto& src = get_src(L); - auto it = src.find(*k); - if (it != src.end()) { - auto& v = *it; - return stack::stack_detail::push_reference(L, v); - } - } - return stack::push(L, lua_nil); + static int real_find_traits(std::false_type, lua_State* L) { + return default_traits::find(L); } - static int real_find_call_capable(std::true_type, std::true_type, lua_State* L) { - return real_index_call(L); - } - - static int real_find_call(lua_State*L) { - return real_find_call_capable(std::integral_constant::value>(), is_associative(), L); + static int real_find_call(lua_State* L) { + return real_find_traits(container_detail::has_traits_find(), L); } static int add_call(lua_State*L) { return detail::typed_static_trampoline(L); } + static int erase_call(lua_State*L) { + return detail::typed_static_trampoline(L); + } + static int insert_call(lua_State*L) { return detail::typed_static_trampoline(L); } @@ -490,10 +208,6 @@ namespace sol { return detail::typed_static_trampoline(L); } - static int pairs_next_call(lua_State*L) { - return detail::typed_static_trampoline(L); - } - static int pairs_call(lua_State*L) { return detail::typed_static_trampoline(L); } @@ -502,6 +216,10 @@ namespace sol { return detail::typed_static_trampoline(L); } + static int set_call(lua_State*L) { + return detail::typed_static_trampoline(L); + } + static int index_call(lua_State*L) { return detail::typed_static_trampoline(L); } @@ -513,37 +231,6 @@ namespace sol { namespace stack { namespace stack_detail { - template - inline auto container_metatable() { - typedef container_usertype_metatable> meta_cumt; - std::array reg = { { - { "__index", &meta_cumt::index_call }, - { "__newindex", &meta_cumt::new_index_call }, - { "__pairs", &meta_cumt::pairs_call }, - { "__ipairs", &meta_cumt::pairs_call }, - { "__len", &meta_cumt::length_call }, - { "get", &meta_cumt::get_call }, - { "clear", &meta_cumt::clear_call }, - { "insert", &meta_cumt::insert_call }, - { "add", &meta_cumt::add_call }, - { "find", &meta_cumt::find_call }, - std::is_pointer::value ? luaL_Reg{ nullptr, nullptr } : luaL_Reg{ "__gc", &detail::usertype_alloc_destroy }, - { nullptr, nullptr } - } }; - return reg; - } - - template - inline auto container_metatable_behind() { - typedef container_usertype_metatable> meta_cumt; - std::array reg = { { - { "__index", &meta_cumt::index_call }, - { "__newindex", &meta_cumt::new_index_call }, - { nullptr, nullptr } - } }; - return reg; - } - template struct metatable_setup { lua_State* L; @@ -551,20 +238,28 @@ namespace sol { metatable_setup(lua_State* L) : L(L) {} void operator()() { - static const auto reg = container_metatable(); - static const auto containerreg = container_metatable_behind(); + typedef container_usertype_metatable> meta_cumt; static const char* metakey = &usertype_traits::metatable()[0]; + static const std::array reg = { { + { "__pairs", &meta_cumt::pairs_call }, + { "__ipairs", &meta_cumt::pairs_call }, + { "__len", &meta_cumt::length_call }, + { "__index", &meta_cumt::index_call }, + { "__newindex", &meta_cumt::new_index_call }, + { "get", &meta_cumt::get_call }, + { "set", &meta_cumt::set_call }, + { "clear", &meta_cumt::clear_call }, + { "insert", &meta_cumt::insert_call }, + { "add", &meta_cumt::add_call }, + { "find", &meta_cumt::find_call }, + { "erase", &meta_cumt::erase_call }, + std::is_pointer::value ? luaL_Reg{ nullptr, nullptr } : luaL_Reg{ "__gc", &detail::usertype_alloc_destroy }, + { nullptr, nullptr } + } }; if (luaL_newmetatable(L, metakey) == 1) { stack_reference metatable(L, -1); luaL_setfuncs(L, reg.data(), 0); - - lua_createtable(L, 0, static_cast(containerreg.size())); - stack_reference metabehind(L, -1); - luaL_setfuncs(L, containerreg.data(), 0); - - stack::set_field(L, metatable_key, metabehind, metatable.stack_index()); - metabehind.pop(); } lua_setmetatable(L, -2); } @@ -573,21 +268,25 @@ namespace sol { template struct pusher>, meta::neg>, std::is_base_of>>>>::value>> { + typedef meta::unqualified_t C; + static int push(lua_State* L, const T& cont) { - stack_detail::metatable_setup fx(L); + stack_detail::metatable_setup fx(L); return pusher>{}.push_fx(L, fx, cont); } static int push(lua_State* L, T&& cont) { - stack_detail::metatable_setup fx(L); + stack_detail::metatable_setup fx(L); return pusher>{}.push_fx(L, fx, std::move(cont)); } }; template struct pusher>, meta::neg>, std::is_base_of>>>>::value>> { + typedef std::add_pointer_t>> C; + static int push(lua_State* L, T* cont) { - stack_detail::metatable_setup>*> fx(L); + stack_detail::metatable_setup fx(L); return pusher>{}.push_fx(L, fx, cont); } }; @@ -595,4 +294,4 @@ namespace sol { } // sol -#endif // SOL_CONTAINER_USERTYPE_HPP +#endif // SOL_CONTAINER_USERTYPE_METATABLE_HPP diff --git a/sol/environment.hpp b/sol/environment.hpp index b9c1ec10..9a8576ce 100644 --- a/sol/environment.hpp +++ b/sol/environment.hpp @@ -32,6 +32,8 @@ namespace sol { typedef basic_table base_t; public: + using base_t::lua_state; + basic_environment() noexcept = default; basic_environment(const basic_environment&) = default; basic_environment(basic_environment&&) = default; @@ -76,7 +78,7 @@ namespace sol { #ifdef SOL_CHECK_ARGUMENTS if (!is_environment>::value) { auto pp = stack::push_pop(*this); - stack::check(base_t::lua_state(), -1, type_panic); + stack::check(lua_state(), -1, type_panic); } #endif // Safety } diff --git a/sol/forward.hpp b/sol/forward.hpp index a72575c6..1ad635d5 100644 --- a/sol/forward.hpp +++ b/sol/forward.hpp @@ -28,6 +28,7 @@ namespace sol { class reference; class stack_reference; + struct proxy_base_tag; template struct proxy; template @@ -50,30 +51,38 @@ namespace sol { struct basic_environment; using environment = basic_environment; using stack_environment = basic_environment; - template + template class basic_function; - template + template class basic_protected_function; - using protected_function = basic_protected_function; - using stack_protected_function = basic_protected_function; - using unsafe_function = basic_function; - using safe_function = basic_protected_function; - using stack_unsafe_function = basic_function; - using stack_safe_function = basic_protected_function; + using unsafe_function = basic_function; + using safe_function = basic_protected_function; + using stack_unsafe_function = basic_function; + using stack_safe_function = basic_protected_function; + using stack_aligned_unsafe_function = basic_function; + using stack_aligned_safe_function = basic_protected_function; + using protected_function = safe_function; + using stack_protected_function = stack_safe_function; + using stack_aligned_protected_function = stack_aligned_safe_function; #ifdef SOL_SAFE_FUNCTIONS using function = protected_function; using stack_function = stack_protected_function; + using stack_aligned_function = stack_aligned_safe_function; #else using function = unsafe_function; using stack_function = stack_unsafe_function; + using stack_aligned_function = stack_aligned_unsafe_function; #endif + struct function_result; + struct protected_function_result; + using safe_function_result = protected_function_result; + using unsafe_function_result = function_result; template class basic_object; template class basic_userdata; template class basic_lightuserdata; - struct variadic_args; using object = basic_object; using stack_object = basic_object; using userdata = basic_userdata; @@ -84,9 +93,14 @@ namespace sol { class thread; struct variadic_args; struct variadic_results; + struct stack_count; struct this_state; struct this_environment; template + struct as_table_t; + template + struct nested; + template struct light; template struct user; diff --git a/sol/function.hpp b/sol/function.hpp index f73a61f7..d4a03742 100644 --- a/sol/function.hpp +++ b/sol/function.hpp @@ -22,9 +22,9 @@ #ifndef SOL_FUNCTION_HPP #define SOL_FUNCTION_HPP +#include "stack.hpp" #include "unsafe_function.hpp" #include "protected_function.hpp" -#include "stack.hpp" #include namespace sol { diff --git a/sol/function_result.hpp b/sol/function_result.hpp index c66c2563..4cb6a87d 100644 --- a/sol/function_result.hpp +++ b/sol/function_result.hpp @@ -78,11 +78,26 @@ namespace sol { lua_State* lua_state() const { return L; }; int stack_index() const { return index; }; + int return_count() const { return returncount; }; ~function_result() { lua_pop(L, returncount); } }; + + namespace stack { + template <> + struct pusher { + static int push(lua_State* L, const function_result& fr) { + int p = 0; + for (int i = 0; i < fr.return_count(); ++i) { + lua_pushvalue(L, i + fr.stack_index()); + ++p; + } + return p; + } + }; + } // stack } // sol #endif // SOL_FUNCTION_RESULT_HPP diff --git a/sol/object.hpp b/sol/object.hpp index c2c507ea..278ed460 100644 --- a/sol/object.hpp +++ b/sol/object.hpp @@ -78,6 +78,8 @@ namespace sol { template basic_object(proxy_base&& r) noexcept : basic_object(r.operator basic_object()) {} basic_object(lua_State* L, int index = -1) noexcept : base_t(L, index) {} + basic_object(lua_State* L, absolute_index index) noexcept : base_t(L, index) {} + basic_object(lua_State* L, raw_index index) noexcept : base_t(L, index) {} basic_object(lua_State* L, ref_index index) noexcept : base_t(L, index) {} template basic_object(lua_State* L, in_place_type_t, Args&&... args) noexcept diff --git a/sol/protected_function.hpp b/sol/protected_function.hpp index da9d5c6e..b5de9f0c 100644 --- a/sol/protected_function.hpp +++ b/sol/protected_function.hpp @@ -31,9 +31,9 @@ namespace sol { namespace detail { - inline reference& handler_storage() { - static sol::reference h; - return h; + inline const char (&default_handler_name())[11] { + static const char name[11] = "sol.\xF0\x9F\x94\xA9"; + return name; } struct handler { @@ -54,36 +54,37 @@ namespace sol { }; } - template + template class basic_protected_function : public base_t { public: - static reference& get_default_handler() { - return detail::handler_storage(); + static reference get_default_handler(lua_State* L) { + if (L == nullptr) + return reference(lua_nil); + lua_getglobal(L, detail::default_handler_name()); + auto pp = stack::pop_n(L, 1); + return reference(L, -1); } static void set_default_handler(const reference& ref) { - detail::handler_storage() = ref; - } - - static void set_default_handler(reference&& ref) { - detail::handler_storage() = std::move(ref); + ref.push(); + lua_setglobal(ref.lua_state(), detail::default_handler_name()); } private: call_status luacall(std::ptrdiff_t argcount, std::ptrdiff_t resultcount, detail::handler& h) const { - return static_cast(lua_pcallk(base_t::lua_state(), static_cast(argcount), static_cast(resultcount), h.stackindex, 0, nullptr)); + return static_cast(lua_pcallk(lua_state(), static_cast(argcount), static_cast(resultcount), h.stackindex, 0, nullptr)); } template auto invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { luacall(n, sizeof...(Ret), h); - return stack::pop>(base_t::lua_state()); + return stack::pop>(lua_state()); } template Ret invoke(types, std::index_sequence, std::ptrdiff_t n, detail::handler& h) const { luacall(n, 1, h); - return stack::pop(base_t::lua_state()); + return stack::pop(lua_state()); } template @@ -92,7 +93,7 @@ namespace sol { } protected_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n, detail::handler& h) const { - int stacksize = lua_gettop(base_t::lua_state()); + int stacksize = lua_gettop(lua_state()); int poststacksize = stacksize; int firstreturn = 1; int returncount = 0; @@ -102,42 +103,44 @@ namespace sol { h.stackindex = 0; if (h.target.valid()) { h.target.push(); - stack::push(base_t::lua_state(), error); - lua_call(base_t::lua_state(), 1, 1); + stack::push(lua_state(), error); + lua_call(lua_state(), 1, 1); } else { - stack::push(base_t::lua_state(), error); + stack::push(lua_state(), error); } }; try { #endif // No Exceptions firstreturn = (std::max)(1, static_cast(stacksize - n - static_cast(h.valid()))); code = luacall(n, LUA_MULTRET, h); - poststacksize = lua_gettop(base_t::lua_state()) - static_cast(h.valid()); + poststacksize = lua_gettop(lua_state()) - static_cast(h.valid()); returncount = poststacksize - (firstreturn - 1); #ifndef SOL_NO_EXCEPTIONS } // Handle C++ errors thrown from C++ functions bound inside of lua catch (const char* error) { onexcept(error); - firstreturn = lua_gettop(base_t::lua_state()); - return protected_function_result(base_t::lua_state(), firstreturn, 0, 1, call_status::runtime); + firstreturn = lua_gettop(lua_state()); + return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime); } catch (const std::exception& error) { onexcept(error.what()); - firstreturn = lua_gettop(base_t::lua_state()); - return protected_function_result(base_t::lua_state(), firstreturn, 0, 1, call_status::runtime); + firstreturn = lua_gettop(lua_state()); + return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime); } catch (...) { onexcept("caught (...) unknown error during protected_function call"); - firstreturn = lua_gettop(base_t::lua_state()); - return protected_function_result(base_t::lua_state(), firstreturn, 0, 1, call_status::runtime); + firstreturn = lua_gettop(lua_state()); + return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime); } #endif // No Exceptions - return protected_function_result(base_t::lua_state(), firstreturn, returncount, returncount, code); + return protected_function_result(lua_state(), firstreturn, returncount, returncount, code); } public: + using base_t::lua_state; + reference error_handler; basic_protected_function() = default; @@ -146,7 +149,7 @@ namespace sol { #ifdef SOL_CHECK_ARGUMENTS if (!is_function>::value) { auto pp = stack::push_pop(*this); - stack::check(base_t::lua_state(), -1, type_panic); + stack::check(lua_state(), -1, type_panic); } #endif // Safety } @@ -154,22 +157,42 @@ namespace sol { basic_protected_function& operator=(const basic_protected_function&) = default; basic_protected_function(basic_protected_function&&) = default; basic_protected_function& operator=(basic_protected_function&&) = default; - basic_protected_function(const basic_function& b, reference eh = get_default_handler()) : base_t(b), error_handler(std::move(eh)) {} - basic_protected_function(basic_function&& b, reference eh = get_default_handler()) : base_t(std::move(b)), error_handler(std::move(eh)) {} - basic_protected_function(const stack_reference& r, reference eh = get_default_handler()) : basic_protected_function(r.lua_state(), r.stack_index(), std::move(eh)) {} - basic_protected_function(stack_reference&& r, reference eh = get_default_handler()) : basic_protected_function(r.lua_state(), r.stack_index(), std::move(eh)) {} + basic_protected_function(const basic_function& b) : basic_protected_function(b, get_default_handler(b.lua_state())) {} + basic_protected_function(basic_function&& b) : basic_protected_function(std::move(b), get_default_handler(b.lua_state())) {} + basic_protected_function(const basic_function& b, reference eh) : base_t(b), error_handler(std::move(eh)) {} + basic_protected_function(basic_function&& b, reference eh) : base_t(std::move(b)), error_handler(std::move(eh)) {} + basic_protected_function(const stack_reference& r) : basic_protected_function(r.lua_state(), r.stack_index(), get_default_handler(r.lua_state())) {} + basic_protected_function(stack_reference&& r) : basic_protected_function(r.lua_state(), r.stack_index(), get_default_handler(r.lua_state())) {} + basic_protected_function(const stack_reference& r, reference eh) : basic_protected_function(r.lua_state(), r.stack_index(), std::move(eh)) {} + basic_protected_function(stack_reference&& r, reference eh) : basic_protected_function(r.lua_state(), r.stack_index(), std::move(eh)) {} template - basic_protected_function(proxy_base&& p, reference eh = get_default_handler()) : basic_protected_function(p.operator basic_function(), std::move(eh)) {} + basic_protected_function(const proxy_base& p) : basic_protected_function(p.operator basic_function(), get_default_handler(p.lua_state())) {} template - basic_protected_function(const proxy_base& p, reference eh = get_default_handler()) : basic_protected_function(p.operator basic_function(), std::move(eh)) {} - template >>, meta::neg>> = meta::enabler> + basic_protected_function(proxy_base&& p) : basic_protected_function(p.operator basic_function(), get_default_handler(p.lua_state())) {} + template >>, meta::neg>>> = meta::enabler> + basic_protected_function(lua_State* L, T&& r) : basic_protected_function(L, std::forward(r), get_default_handler(L)) {} + template >>, meta::neg>>> = meta::enabler> basic_protected_function(lua_State* L, T&& r, reference eh) : basic_protected_function(L, sol::ref_index(r.registry_index()), std::move(eh)) {} - basic_protected_function(lua_State* L, int index = -1, reference eh = get_default_handler()) : base_t(L, index), error_handler(std::move(eh)) { + basic_protected_function(lua_State* L, int index = -1) : basic_protected_function(L, index, get_default_handler(L)) {} + basic_protected_function(lua_State* L, int index, reference eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS stack::check(L, index, type_panic); #endif // Safety } - basic_protected_function(lua_State* L, ref_index index, reference eh = get_default_handler()) : base_t(L, index), error_handler(std::move(eh)) { + basic_protected_function(lua_State* L, absolute_index index) : basic_protected_function(L, index, get_default_handler(L)) {} + basic_protected_function(lua_State* L, absolute_index index, reference eh) : base_t(L, index), error_handler(std::move(eh)) { +#ifdef SOL_CHECK_ARGUMENTS + stack::check(L, index, type_panic); +#endif // Safety + } + basic_protected_function(lua_State* L, raw_index index) : basic_protected_function(L, index, get_default_handler(L)) {} + basic_protected_function(lua_State* L, raw_index index, reference eh) : base_t(L, index), error_handler(std::move(eh)) { +#ifdef SOL_CHECK_ARGUMENTS + stack::check(L, index, type_panic); +#endif // Safety + } + basic_protected_function(lua_State* L, ref_index index) : basic_protected_function(L, index, get_default_handler(L)) {} + basic_protected_function(lua_State* L, ref_index index, reference eh) : base_t(L, index), error_handler(std::move(eh)) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); stack::check(L, -1, type_panic); @@ -189,8 +212,10 @@ namespace sol { template decltype(auto) call(Args&&... args) const { detail::handler h(error_handler); - base_t::push(); - int pushcount = stack::multi_push_reference(base_t::lua_state(), std::forward(args)...); + if (!aligned) { + base_t::push(); + } + int pushcount = stack::multi_push_reference(lua_state(), std::forward(args)...); return invoke(types(), std::make_index_sequence(), pushcount, h); } }; diff --git a/sol/protected_function_result.hpp b/sol/protected_function_result.hpp index a4185960..d10217f5 100644 --- a/sol/protected_function_result.hpp +++ b/sol/protected_function_result.hpp @@ -120,11 +120,27 @@ namespace sol { lua_State* lua_state() const noexcept { return L; }; int stack_index() const noexcept { return index; }; + int return_count() const noexcept { return returncount; }; + int pop_count() const noexcept { return popcount; }; ~protected_function_result() { stack::remove(L, index, popcount); } }; + + namespace stack { + template <> + struct pusher { + static int push(lua_State* L, const protected_function_result& pfr) { + int p = 0; + for (int i = 0; i < pfr.pop_count(); ++i) { + lua_pushvalue(L, i + pfr.stack_index()); + ++p; + } + return p; + } + }; + } // stack } // sol #endif // SOL_PROTECTED_FUNCTION_RESULT_HPP diff --git a/sol/proxy.hpp b/sol/proxy.hpp index 5b1af665..95bc9488 100644 --- a/sol/proxy.hpp +++ b/sol/proxy.hpp @@ -114,21 +114,25 @@ namespace sol { bool valid() const { auto pp = stack::push_pop(tbl); - auto p = stack::probe_get_field, global_table>::value>(tbl.lua_state(), key, lua_gettop(tbl.lua_state())); - lua_pop(tbl.lua_state(), p.levels); + auto p = stack::probe_get_field, global_table>::value>(lua_state(), key, lua_gettop(lua_state())); + lua_pop(lua_state(), p.levels); return p; } type get_type() const { type t = type::none; auto pp = stack::push_pop(tbl); - auto p = stack::probe_get_field, global_table>::value>(tbl.lua_state(), key, lua_gettop(tbl.lua_state())); + auto p = stack::probe_get_field, global_table>::value>(lua_state(), key, lua_gettop(lua_state())); if (p) { - t = type_of(tbl.lua_state(), -1); + t = type_of(lua_state(), -1); } - lua_pop(tbl.lua_state(), p.levels); + lua_pop(lua_state(), p.levels); return t; } + + lua_State* lua_state() const { + return tbl.lua_state(); + } }; template diff --git a/sol/proxy_base.hpp b/sol/proxy_base.hpp index 470b80ba..b0d76190 100644 --- a/sol/proxy_base.hpp +++ b/sol/proxy_base.hpp @@ -47,6 +47,11 @@ namespace sol { const Super& super = *static_cast(static_cast(this)); return super.template get(); } + + lua_State* lua_state() const { + const Super& super = *static_cast(static_cast(this)); + return super.lua_state(); + } }; } // sol diff --git a/sol/stack.hpp b/sol/stack.hpp index bcd6afdc..09e5bd61 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -35,6 +35,13 @@ #include namespace sol { + namespace detail { + inline const std::string& default_chunk_name() { + static const std::string name = "string"; + return name; + } + } // detail + namespace stack { namespace stack_detail { template @@ -156,7 +163,7 @@ namespace sol { typedef lua_bind_traits> traits_type; typedef typename traits_type::args_list args_list; typedef typename traits_type::returns_list returns_list; - return call_into_lua(returns_list(), args_list(), L, start, std::forward(fx), std::forward(fxargs)...); + return call_into_lua(returns_list(), args_list(), L, start, std::forward(fx), std::forward(fxargs)...); } inline call_syntax get_call_syntax(lua_State* L, const std::string& key, int index) { @@ -171,14 +178,14 @@ namespace sol { return call_syntax::colon; } - inline void script(lua_State* L, const std::string& code) { - if (luaL_dostring(L, code.c_str())) { + inline void script(lua_State* L, const string_detail::string_shim& code, string_detail::string_shim name = detail::default_chunk_name(), load_mode mode = load_mode::any) { + if (luaL_loadbufferx(L, code.data(), code.size(), name.data(), to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) { lua_error(L); } } - inline void script_file(lua_State* L, const std::string& filename) { - if (luaL_dofile(L, filename.c_str())) { + inline void script_file(lua_State* L, const std::string& filename, load_mode mode = load_mode::any) { + if (luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) { lua_error(L); } } diff --git a/sol/stack_core.hpp b/sol/stack_core.hpp index 40c349f2..08ce81fe 100644 --- a/sol/stack_core.hpp +++ b/sol/stack_core.hpp @@ -30,6 +30,7 @@ #include "tie.hpp" #include "stack_guard.hpp" #include +#include #include namespace sol { @@ -158,6 +159,18 @@ namespace sol { #else false; #endif + + template + static int get_size_hint(const C& c) { + return static_cast(c.size()); + } + + template + static int get_size_hint(const std::forward_list& c) { + // forward_list makes me sad + return static_cast(32); + } + template inline decltype(auto) unchecked_get(lua_State* L, int index, record& tracking) { return getter>{}.get(L, index, tracking); @@ -180,6 +193,10 @@ namespace sol { return t == type::userdata || t == type::table; } + inline int top(lua_State* L) { + return lua_gettop(L); + } + template inline int push(lua_State* L, T&& t, Args&&... args) { return pusher>{}.push(L, std::forward(t), std::forward(args)...); @@ -248,7 +265,10 @@ namespace sol { template inline decltype(auto) check_get(lua_State* L, int index, Handler&& handler, record& tracking) { - return check_getter>{}.get(L, index, std::forward(handler), tracking); + typedef meta::unqualified_t Tu; + check_getter cg{}; + (void)cg; + return cg.get(L, index, std::forward(handler), tracking); } template diff --git a/sol/stack_get.hpp b/sol/stack_get.hpp index bfe7b1ab..81005f2d 100644 --- a/sol/stack_get.hpp +++ b/sol/stack_get.hpp @@ -83,21 +83,52 @@ namespace sol { }; template - struct getter, std::enable_if_t>::value>> { + struct getter> { + typedef meta::unqualified_t Tu; + + template + static void push_back_at_end(std::true_type, types, lua_State* L, T& arr, std::size_t) { + arr.push_back(stack::get(L, -lua_size::value)); + } + + template + static void push_back_at_end(std::false_type, types t, lua_State* L, T& arr, std::size_t idx) { + insert_at_end(meta::has_insert(), t, L, arr, idx); + } + + template + static void insert_at_end(std::true_type, types, lua_State* L, T& arr, std::size_t) { + using std::cend; + arr.insert(cend(arr), stack::get(L, -lua_size::value)); + } + + template + static void insert_at_end(std::false_type, types, lua_State* L, T& arr, std::size_t idx) { + arr[idx] = stack::get(L, -lua_size::value); + } + static T get(lua_State* L, int relindex, record& tracking) { + return get(meta::has_key_value_pair>(), L, relindex, tracking); + } + + static T get(std::false_type, lua_State* L, int relindex, record& tracking) { typedef typename T::value_type V; return get(types(), L, relindex, tracking); } template - static T get(types, lua_State* L, int relindex, record& tracking) { + static T get(types t, lua_State* L, int relindex, record& tracking) { tracking.use(1); int index = lua_absindex(L, relindex); T arr; + std::size_t idx = 0; #if SOL_LUA_VERSION >= 503 // This method is HIGHLY performant over regular table iteration thanks to the Lua API changes in 5.3 for (lua_Integer i = 0; ; i += lua_size::value, lua_pop(L, lua_size::value)) { + if (idx >= arr.max_size()) { + return arr; + } bool isnil = false; for (int vi = 0; vi < lua_size::value; ++vi) { type t = static_cast(lua_geti(L, index, i + vi)); @@ -112,7 +143,8 @@ namespace sol { } if (isnil) continue; - arr.push_back(stack::get(L, -lua_size::value)); + push_back_at_end(meta::has_push_back(), t, L, arr, idx); + ++idx; } #else // Zzzz slower but necessary thanks to the lower version API and missing functions qq @@ -133,16 +165,14 @@ namespace sol { } if (isnil) continue; - arr.push_back(stack::get(L, -1)); + push_back_at_end(meta::has_push_back(), t, L, arr, idx); + ++idx; } #endif return arr; } - }; - template - struct getter, std::enable_if_t>::value>> { - static T get(lua_State* L, int index, record& tracking) { + static T get(std::true_type, lua_State* L, int index, record& tracking) { typedef typename T::value_type P; typedef typename P::first_type K; typedef typename P::second_type V; @@ -169,6 +199,104 @@ namespace sol { } }; + template + struct getter>> { + typedef std::forward_list C; + + static C get(lua_State* L, int relindex, record& tracking) { + return get(meta::has_key_value_pair(), L, relindex, tracking); + } + + static C get(std::true_type, lua_State* L, int index, record& tracking) { + typedef typename T::value_type P; + typedef typename P::first_type K; + typedef typename P::second_type V; + return get(types(), L, index, tracking); + } + + static C get(std::false_type, lua_State* L, int relindex, record& tracking) { + typedef typename C::value_type V; + return get(types(), L, relindex, tracking); + } + + template + static C get(types t, lua_State* L, int relindex, record& tracking) { + tracking.use(1); + + int index = lua_absindex(L, relindex); + C arr; + auto at = arr.cbefore_begin(); + std::size_t idx = 0; +#if SOL_LUA_VERSION >= 503 + // This method is HIGHLY performant over regular table iteration thanks to the Lua API changes in 5.3 + for (lua_Integer i = 0; ; i += lua_size::value, lua_pop(L, lua_size::value)) { + if (idx >= arr.max_size()) { + return arr; + } + bool isnil = false; + for (int vi = 0; vi < lua_size::value; ++vi) { + type t = static_cast(lua_geti(L, index, i + vi)); + isnil = t == type::lua_nil; + if (isnil) { + if (i == 0) { + break; + } + lua_pop(L, (vi + 1)); + return arr; + } + } + if (isnil) + continue; + at = arr.insert_after(at, stack::get(L, -lua_size::value)); + ++idx; + } +#else + // Zzzz slower but necessary thanks to the lower version API and missing functions qq + for (lua_Integer i = 0; ; i += lua_size::value, lua_pop(L, lua_size::value)) { + bool isnil = false; + for (int vi = 0; vi < lua_size::value; ++vi) { + lua_pushinteger(L, i); + lua_gettable(L, index); + type t = type_of(L, -1); + isnil = t == type::lua_nil; + if (isnil) { + if (i == 0) { + break; + } + lua_pop(L, (vi + 1)); + return arr; + } + } + if (isnil) + continue; + at = arr.insert_after(at, stack::get(L, -lua_size::value)); + ++idx; + } +#endif + return arr; + } + + template + static C get(types, lua_State* L, int relindex, record& tracking) { + tracking.use(1); + + C associative; + auto at = associative.cbefore_begin(); + int index = lua_absindex(L, relindex); + lua_pushnil(L); + while (lua_next(L, index) != 0) { + decltype(auto) key = stack::check_get(L, -2); + if (!key) { + lua_pop(L, 1); + continue; + } + at = associative.emplace_after(at, std::forward(*key), stack::get(L, -1)); + lua_pop(L, 1); + } + return associative; + } + }; + template struct getter, std::enable_if_t::value>> { static T get(lua_State* L, int index, record& tracking) { diff --git a/sol/stack_proxy.hpp b/sol/stack_proxy.hpp index 498d8def..7ba728d0 100644 --- a/sol/stack_proxy.hpp +++ b/sol/stack_proxy.hpp @@ -42,6 +42,16 @@ namespace sol { return stack::get(L, stack_index()); } + template + bool is() const { + return stack::check(L, stack_index()); + } + + template + decltype(auto) as() const { + return get(); + } + type get_type() const noexcept { return type_of(lua_state(), stack_index()); } diff --git a/sol/stack_push.hpp b/sol/stack_push.hpp index 3c0294d5..6ffa81d9 100644 --- a/sol/stack_push.hpp +++ b/sol/stack_push.hpp @@ -41,7 +41,7 @@ namespace sol { namespace stack { inline int push_environment_of(lua_State* L, int index = -1) { #if SOL_LUA_VERSION < 502 - // Use lua_setfenv + // Use lua_getfenv lua_getfenv(L, index); return 1; #else @@ -199,16 +199,6 @@ namespace sol { } }; - template - struct pusher::value>> { - static int push(lua_State* L, const T& value) { - if (std::is_same::value) { - return stack::push(L, static_cast(value)); - } - return stack::push(L, static_cast>(value)); - } - }; - template struct pusher, std::is_unsigned>::value>> { static int push(lua_State* L, const T& value) { @@ -218,11 +208,35 @@ namespace sol { }; template - struct pusher, std::enable_if_t>>::value>> { - static int push(lua_State* L, const as_table_t& tablecont) { - auto& cont = detail::deref(detail::unwrap(tablecont.source)); + struct pusher::value>> { + static int push(lua_State* L, const T& value) { + if (std::is_same>::value) { + return stack::push(L, static_cast(value)); + } + return stack::push(L, static_cast>(value)); + } + }; + + template + struct pusher, std::enable_if_t>::value>> { + static int push(lua_State* L, const T& tablecont) { + return push(meta::has_key_value_pair>>(), L, tablecont); + } + + static int push(std::true_type, lua_State* L, const T& tablecont) { + auto& cont = detail::deref(detail::unwrap(tablecont)); lua_createtable(L, static_cast(cont.size()), 0); int tableindex = lua_gettop(L); + for (const auto& pair : cont) { + set_field(L, pair.first, pair.second, tableindex); + } + return 1; + } + + static int push(std::false_type, lua_State* L, const T& tablecont) { + auto& cont = detail::deref(detail::unwrap(tablecont)); + lua_createtable(L, stack_detail::get_size_hint(tablecont), 0); + int tableindex = lua_gettop(L); std::size_t index = 1; for (const auto& i : cont) { #if SOL_LUA_VERSION >= 503 @@ -257,15 +271,19 @@ namespace sol { }; template - struct pusher, std::enable_if_t>>::value>> { - static int push(lua_State* L, const as_table_t& tablecont) { - auto& cont = detail::deref(detail::unwrap(tablecont.source)); - lua_createtable(L, static_cast(cont.size()), 0); - int tableindex = lua_gettop(L); - for (const auto& pair : cont) { - set_field(L, pair.first, pair.second, tableindex); - } - return 1; + struct pusher, std::enable_if_t>::value>> { + static int push(lua_State* L, const T& v) { + return stack::push(L, v); + } + }; + + template + struct pusher> { + static int push(lua_State* L, const T& tablecont) { + pusher> p{}; + // silence annoying VC++ warning + (void)p; + return p.push(L, tablecont); } }; @@ -296,6 +314,13 @@ namespace sol { } }; + template<> + struct pusher { + static int push(lua_State*, stack_count st) { + return st.count; + } + }; + template<> struct pusher { static int push(lua_State* L, metatable_t) { diff --git a/sol/state.hpp b/sol/state.hpp index 5f439d29..7b716bf8 100644 --- a/sol/state.hpp +++ b/sol/state.hpp @@ -25,55 +25,59 @@ #include "state_view.hpp" namespace sol { - inline int default_at_panic(lua_State* L) { + + namespace detail { + inline int default_at_panic(lua_State* L) { #ifdef SOL_NO_EXCEPTIONS - (void)L; - return -1; + (void)L; + return -1; #else - const char* message = lua_tostring(L, -1); - if (message) { - std::string err = message; + const char* message = lua_tostring(L, -1); + if (message) { + std::string err = message; + lua_settop(L, 0); + throw error(err); + } lua_settop(L, 0); - throw error(err); - } - lua_settop(L, 0); - throw error(std::string("An unexpected error occurred and forced the lua state to call atpanic")); + throw error(std::string("An unexpected error occurred and forced the lua state to call atpanic")); #endif - } - - inline int default_error_handler(lua_State*L) { - using namespace sol; - std::string msg = "An unknown error has triggered the default error handler"; - optional maybetopmsg = stack::check_get(L, 1); - if (maybetopmsg) { - const string_detail::string_shim& topmsg = maybetopmsg.value(); - msg.assign(topmsg.data(), topmsg.size()); } - luaL_traceback(L, L, msg.c_str(), 1); - optional maybetraceback = stack::check_get(L, -1); - if (maybetraceback) { - const string_detail::string_shim& traceback = maybetraceback.value(); - msg.assign(traceback.data(), traceback.size()); - } - return stack::push(L, msg); - } + inline int default_traceback_error_handler(lua_State*L) { + using namespace sol; + std::string msg = "An unknown error has triggered the default error handler"; + optional maybetopmsg = stack::check_get(L, 1); + if (maybetopmsg) { + const string_detail::string_shim& topmsg = maybetopmsg.value(); + msg.assign(topmsg.data(), topmsg.size()); + } + luaL_traceback(L, L, msg.c_str(), 1); + optional maybetraceback = stack::check_get(L, -1); + if (maybetraceback) { + const string_detail::string_shim& traceback = maybetraceback.value(); + msg.assign(traceback.data(), traceback.size()); + } + return stack::push(L, msg); + } + } // detail class state : private std::unique_ptr, public state_view { private: typedef std::unique_ptr unique_base; public: - state(lua_CFunction panic = default_at_panic) : unique_base(luaL_newstate(), lua_close), + state(lua_CFunction panic = detail::default_at_panic) : unique_base(luaL_newstate(), lua_close), state_view(unique_base::get()) { set_panic(panic); - sol::protected_function::set_default_handler(sol::object(lua_state(), in_place, default_error_handler)); + lua_CFunction f = c_call; + sol::protected_function::set_default_handler(sol::object(lua_state(), in_place, f)); stack::luajit_exception_handler(unique_base::get()); } state(lua_CFunction panic, lua_Alloc alfunc, void* alpointer = nullptr) : unique_base(lua_newstate(alfunc, alpointer), lua_close), state_view(unique_base::get()) { set_panic(panic); - sol::protected_function::set_default_handler(sol::object(lua_state(), in_place, default_error_handler)); + lua_CFunction f = c_call; + sol::protected_function::set_default_handler(sol::object(lua_state(), in_place, f)); stack::luajit_exception_handler(unique_base::get()); } @@ -88,12 +92,7 @@ namespace sol { using state_view::get; - ~state() { - auto& handler = protected_function::get_default_handler(); - if (handler.lua_state() == this->lua_state()) { - protected_function::set_default_handler(reference()); - } - } + ~state() {} }; } // sol diff --git a/sol/state_view.hpp b/sol/state_view.hpp index 4cd365b9..d33c08aa 100644 --- a/sol/state_view.hpp +++ b/sol/state_view.hpp @@ -53,24 +53,28 @@ namespace sol { return kb; } - inline protected_function_result simple_on_error(lua_State*, sol::protected_function_result result) { + inline protected_function_result script_pass_on_error(lua_State*, sol::protected_function_result result) { return result; } - inline protected_function_result default_on_error( lua_State* L, protected_function_result pfr ) { + inline protected_function_result script_default_on_error(lua_State* L, protected_function_result pfr) { type t = type_of(L, pfr.stack_index()); - std::string err = to_string(pfr.status()) + " error"; + std::string err = "sol: "; + err += to_string(pfr.status()); + err += " error:"; if (t == type::string) { err += " "; - err += stack::get(L, pfr.stack_index()); + string_detail::string_shim serr = stack::get(L, pfr.stack_index()); + err.append(serr.data(), serr.size()); } #ifdef SOL_NO_EXCEPTIONS + // replacing information of stack error into pfr if (t != type::nil) { lua_pop(L, 1); } stack::push(L, err); - lua_error(L); #else + // just throw our error throw error(detail::direct_error, err); #endif return pfr; @@ -146,7 +150,7 @@ namespace sol { } - state_view(this_state Ls) : state_view(Ls.L){ + state_view(this_state Ls) : state_view(Ls.L) { } @@ -249,17 +253,17 @@ namespace sol { return stack::pop(L); } - object require_script(const std::string& key, const std::string& code, bool create_global = true) { - return require_core(key, [this, &code]() {stack::script(L, code); }, create_global); + object require_script(const std::string& key, const string_detail::string_shim& code, bool create_global = true, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return require_core(key, [this, &code, &chunkname, &mode]() {stack::script(L, code, chunkname, mode); }, create_global); } - object require_file(const std::string& key, const std::string& filename, bool create_global = true) { - return require_core(key, [this, &filename]() {stack::script_file(L, filename); }, create_global); + object require_file(const std::string& key, const std::string& filename, bool create_global = true, load_mode mode = load_mode::any) { + return require_core(key, [this, &filename, &mode]() {stack::script_file(L, filename, mode); }, create_global); } template - protected_function_result do_string(const std::string& code, const basic_environment& env) { - load_status x = static_cast(luaL_loadstring(L, code.c_str())); + protected_function_result do_string(const string_detail::string_shim& code, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), chunkname.c_str(), to_string(mode).c_str())); if (x != load_status::ok) { return protected_function_result(L, -1, 0, 1, static_cast(x)); } @@ -270,8 +274,8 @@ namespace sol { } template - protected_function_result do_file(const std::string& filename, const basic_environment& env) { - load_status x = static_cast(luaL_loadfile(L, filename.c_str())); + protected_function_result do_file(const std::string& filename, const basic_environment& env, load_mode mode = load_mode::any) { + load_status x = static_cast(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())); if (x != load_status::ok) { return protected_function_result(L, -1, 0, 1, static_cast(x)); } @@ -281,8 +285,8 @@ namespace sol { return pf(); } - protected_function_result do_string(const std::string& code) { - load_status x = static_cast(luaL_loadstring(L, code.c_str())); + protected_function_result do_string(const string_detail::string_shim& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), chunkname.c_str(), to_string(mode).c_str())); if (x != load_status::ok) { return protected_function_result(L, -1, 0, 1, static_cast(x)); } @@ -291,8 +295,8 @@ namespace sol { return pf(); } - protected_function_result do_file(const std::string& filename) { - load_status x = static_cast(luaL_loadfile(L, filename.c_str())); + protected_function_result do_file(const std::string& filename, load_mode mode = load_mode::any) { + load_status x = static_cast(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())); if (x != load_status::ok) { return protected_function_result(L, -1, 0, 1, static_cast(x)); } @@ -301,17 +305,9 @@ namespace sol { return pf(); } - protected_function_result script(const std::string& code, const environment& env) { - return script(code, env, sol::default_on_error); - } - - protected_function_result script_file(const std::string& filename, const environment& env) { - return script_file(filename, env, sol::default_on_error); - } - template >> = meta::enabler> - protected_function_result script(const std::string& code, Fx&& on_error) { - protected_function_result pfr = do_string(code); + protected_function_result safe_script(const string_detail::string_shim& code, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + protected_function_result pfr = do_string(code, chunkname, mode); if (!pfr.valid()) { return on_error(L, std::move(pfr)); } @@ -319,8 +315,8 @@ namespace sol { } template >> = meta::enabler> - protected_function_result script_file(const std::string& filename, Fx&& on_error) { - protected_function_result pfr = do_file(filename); + protected_function_result safe_script_file(const std::string& filename, Fx&& on_error, load_mode mode = load_mode::any) { + protected_function_result pfr = do_file(filename, mode); if (!pfr.valid()) { return on_error(L, std::move(pfr)); } @@ -328,8 +324,8 @@ namespace sol { } template - protected_function_result script(const std::string& code, const basic_environment& env, Fx&& on_error) { - protected_function_result pfr = do_string(code, env); + protected_function_result safe_script(const string_detail::string_shim& code, const basic_environment& env, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + protected_function_result pfr = do_string(code, env, chunkname, mode); if (!pfr.valid()) { return on_error(L, std::move(pfr)); } @@ -337,42 +333,94 @@ namespace sol { } template - protected_function_result script_file(const std::string& filename, const basic_environment& env, Fx&& on_error) { - protected_function_result pfr = do_file(filename, env); + protected_function_result safe_script_file(const std::string& filename, const basic_environment& env, Fx&& on_error, load_mode mode = load_mode::any) { + protected_function_result pfr = do_file(filename, env, mode); if (!pfr.valid()) { return on_error(L, std::move(pfr)); } return pfr; } - function_result script(const std::string& code) { + protected_function_result safe_script(const string_detail::string_shim& code, const environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return safe_script(code, env, sol::script_default_on_error, chunkname, mode); + } + + protected_function_result safe_script_file(const std::string& filename, const environment& env, load_mode mode = load_mode::any) { + return safe_script_file(filename, env, sol::script_default_on_error, mode); + } + + protected_function_result safe_script(const string_detail::string_shim& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return safe_script(code, sol::script_default_on_error, chunkname, mode); + } + + protected_function_result safe_script_file(const std::string& filename, load_mode mode = load_mode::any) { + return safe_script_file(filename, sol::script_default_on_error, mode); + } + + function_result unsafe_script(const string_detail::string_shim& code, const std::string& name = detail::default_chunk_name(), load_mode mode = load_mode::any) { int index = lua_gettop(L); - stack::script(L, code); + stack::script(L, code, name, mode); int postindex = lua_gettop(L); int returns = postindex - index; return function_result(L, (std::max)(postindex - (returns - 1), 1), returns); } - function_result script_file(const std::string& filename) { + function_result unsafe_script_file(const std::string& filename, load_mode mode = load_mode::any) { int index = lua_gettop(L); - stack::script_file(L, filename); + stack::script_file(L, filename, mode); int postindex = lua_gettop(L); int returns = postindex - index; return function_result(L, (std::max)(postindex - (returns - 1), 1), returns); } - load_result load(const std::string& code) { - load_status x = static_cast(luaL_loadstring(L, code.c_str())); + template >> = meta::enabler> + protected_function_result script(const string_detail::string_shim& code, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return safe_script(code, std::forward(on_error), chunkname, mode); + } + + template >> = meta::enabler> + protected_function_result script_file(const std::string& filename, Fx&& on_error, load_mode mode = load_mode::any) { + return safe_script_file(filename, std::forward(on_error), mode); + } + + template + protected_function_result script(const string_detail::string_shim& code, const basic_environment& env, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return safe_script(code, env, std::forward(on_error), chunkname, mode); + } + + template + protected_function_result script_file(const std::string& filename, const basic_environment& env, Fx&& on_error, load_mode mode = load_mode::any) { + return safe_script_file(filename, env, std::forward(on_error), mode); + } + + protected_function_result script(const string_detail::string_shim& code, const environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return safe_script(code, env, sol::script_default_on_error, chunkname, mode); + } + + protected_function_result script_file(const std::string& filename, const environment& env, load_mode mode = load_mode::any) { + return safe_script_file(filename, env, sol::script_default_on_error, mode); + } + + function_result script(const string_detail::string_shim& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return unsafe_script(code, chunkname, mode); + } + + function_result script_file(const std::string& filename, load_mode mode = load_mode::any) { + return unsafe_script_file(filename, mode); + } + + load_result load(const string_detail::string_shim& code, const std::string& name = detail::default_chunk_name(), load_mode mode = load_mode::any) { + load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), name.c_str(), to_string(mode).c_str())); return load_result(L, lua_absindex(L, -1), 1, 1, x); } - load_result load_file(const std::string& filename) { - load_status x = static_cast(luaL_loadfile(L, filename.c_str())); + load_result load_file(const std::string& filename, load_mode mode = load_mode::any) { + load_status x = static_cast(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())); return load_result(L, lua_absindex(L, -1), 1, 1, x); } - load_result load_buffer(const char *buff, size_t size, const char *name, const char* mode = nullptr) { - load_status x = static_cast(luaL_loadbufferx(L, buff, size, name, mode)); + load_result load(lua_Reader reader, void* data, const std::string& name = detail::default_chunk_name(), load_mode mode = load_mode::any) { + load_status x = static_cast(lua_load(L, reader, data, name.c_str(), to_string(mode).c_str())); return load_result(L, lua_absindex(L, -1), 1, 1, x); } diff --git a/sol/traits.hpp b/sol/traits.hpp index 6b2d58bd..048a67a6 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -102,13 +102,16 @@ namespace sol { struct any_same : std::false_type { }; template - struct any_same : std::integral_constant ::value || any_same::value> { }; + struct any_same : std::integral_constant ::value || any_same::value> {}; + + template + using boolean = std::integral_constant; template using invoke_t = typename T::type; - template - using boolean = std::integral_constant; + template + using invoke_b = boolean; template using neg = boolean; @@ -326,24 +329,63 @@ namespace sol { static std::false_type test(...); }; - template () < std::declval())> - std::true_type supports_op_less_test(const T&); + template + struct has_push_back_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(std::declval().push_back(std::declval>()))*); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_insert_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(std::declval().insert(std::declval>(), std::declval>()))*); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_insert_after_test { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(std::declval().insert_after(std::declval>(), std::declval>()))*); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template () < std::declval())> + std::true_type supports_op_less_test(const T&, const U&); std::false_type supports_op_less_test(...); - template () == std::declval())> - std::true_type supports_op_equal_test(const T&); + template () == std::declval())> + std::true_type supports_op_equal_test(const T&, const U&); std::false_type supports_op_equal_test(...); - template () <= std::declval())> - std::true_type supports_op_less_equal_test(const T&); + template () <= std::declval())> + std::true_type supports_op_less_equal_test(const T&, const U&); std::false_type supports_op_less_equal_test(...); } // meta_detail - template - using supports_op_less = decltype(meta_detail::supports_op_less_test(std::declval())); - template - using supports_op_equal = decltype(meta_detail::supports_op_equal_test(std::declval())); - template - using supports_op_less_equal = decltype(meta_detail::supports_op_less_equal_test(std::declval())); + template + using supports_op_less = decltype(meta_detail::supports_op_less_test(std::declval(), std::declval())); + template + using supports_op_equal = decltype(meta_detail::supports_op_equal_test(std::declval(), std::declval())); + template + using supports_op_less_equal = decltype(meta_detail::supports_op_less_equal_test(std::declval(), std::declval())); template struct is_callable : boolean::value> {}; @@ -367,7 +409,19 @@ namespace sol { struct has_value_type : decltype(meta_detail::has_value_type_impl::test(0)) {}; template - struct is_associative : meta::all, has_mapped_type> {}; + using has_push_back = meta::boolean::value>; + + template + using has_insert = meta::boolean::value>; + + template + using has_insert_after = meta::boolean::value>; + + template + struct is_associative : meta::all, has_key_value_pair, has_mapped_type> {}; + + template + struct is_lookup : meta::all, has_value_type> {}; template using is_string_constructible = any< @@ -464,6 +518,16 @@ namespace sol { return std::forward(item); } + template + inline auto& deref(T(&item)[5]) { + return item; + } + + template + inline auto& deref(const T(&item)[5]) { + return item; + } + template inline T& deref(T* item) { return *item; diff --git a/sol/types.hpp b/sol/types.hpp index 88d0f5df..81694a50 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -281,6 +281,12 @@ namespace sol { } }; + struct stack_count { + int count; + + stack_count(int cnt) : count(cnt) {} + }; + struct lightuserdata_value { void* value; lightuserdata_value(void* data) : value(data) {} @@ -380,8 +386,19 @@ namespace sol { template struct as_table_t { T source; - template - as_table_t(Args&&... args) : source(std::forward(args)...) {} + + as_table_t() = default; + as_table_t(const as_table_t&) = default; + as_table_t(as_table_t&&) = default; + as_table_t& operator=(const as_table_t&) = default; + as_table_t& operator=(as_table_t&&) = default; + template , as_table_t>>, + meta::neg>> + > = meta::enabler> + as_table_t(Arg&& arg) : source(std::forward(arg)) {} + template + as_table_t(Arg0&& arg0, Arg1&& arg1, Args&&... args) : source(std::forward(arg0), std::forward(arg1), std::forward(args)...) {} operator std::add_lvalue_reference_t () { return source; @@ -392,8 +409,18 @@ namespace sol { struct nested { T source; - template - nested(Args&&... args) : source(std::forward(args)...) {} + nested() = default; + nested(const nested&) = default; + nested(nested&&) = default; + nested& operator=(const nested&) = default; + nested& operator=(nested&&) = default; + template , nested>>, + meta::neg>> + > = meta::enabler> + nested(Arg&& arg) : source(std::forward(arg)) {} + template + nested(Arg0&& arg0, Arg1&& arg1, Args&&... args) : source(std::forward(arg0), std::forward(arg1), std::forward(args)...) {} operator std::add_lvalue_reference_t() { return source; @@ -407,7 +434,7 @@ namespace sol { template nested as_nested(T&& container) { - return as_nested(std::forward(container)); + return nested(std::forward(container)); } struct this_state { @@ -438,6 +465,12 @@ namespace sol { colon = 1 }; + enum class load_mode { + any = 0, + text = 1, + binary = 2, + }; + enum class call_status : int { ok = LUA_OK, yielded = LUA_YIELD, @@ -518,13 +551,13 @@ namespace sol { } inline const std::string& to_string(load_status c) { - static const std::array names{ { - "ok", - "memory", - "gc", - "syntax", - "file", - } }; + static const std::array names{{ + "ok", + "memory", + "gc", + "syntax", + "file", + }}; switch (c) { case load_status::ok: return names[0]; @@ -540,6 +573,15 @@ namespace sol { return names[0]; } + inline const std::string& to_string(load_mode c) { + static const std::array names{{ + "bt", + "t", + "b", + }}; + return names[static_cast(c)]; + } + enum class meta_function { construct, index, @@ -656,6 +698,49 @@ namespace sol { return lua_typename(L, static_cast(t)); } + namespace detail { + template + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + +#ifdef SOL_CXX17_FEATURES + template <> + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; + + template <> + struct is_container : std::false_type {}; +#endif // C++ 17 + + template + struct is_container>::value>> : std::true_type {}; + + template + struct is_container>::value + && !meta::any_same>, char, wchar_t, char16_t, char32_t>::value + >> : std::true_type {}; + } // detail + + template + struct is_container : detail::is_container {}; + namespace detail { template struct lua_type_of : std::integral_constant {}; @@ -733,7 +818,7 @@ namespace sol { struct lua_type_of : std::integral_constant { }; template <> - struct lua_type_of : std::integral_constant { }; + struct lua_type_of : std::integral_constant {}; template struct lua_type_of> : std::integral_constant {}; @@ -780,11 +865,11 @@ namespace sol { template <> struct lua_type_of> : std::integral_constant {}; - template - struct lua_type_of> : std::integral_constant {}; + template + struct lua_type_of> : std::integral_constant {}; - template - struct lua_type_of> : std::integral_constant {}; + template + struct lua_type_of> : std::integral_constant {}; template <> struct lua_type_of : std::integral_constant {}; @@ -801,6 +886,12 @@ namespace sol { template <> struct lua_type_of : std::integral_constant {}; + template <> + struct lua_type_of : std::integral_constant {}; + + template <> + struct lua_type_of : std::integral_constant {}; + template <> struct lua_type_of : std::integral_constant {}; @@ -841,39 +932,12 @@ namespace sol { template <> struct lua_type_of : std::integral_constant {}; #endif // C++ 17 (or not) features - - template - struct is_container : std::false_type {}; - - template <> - struct is_container : std::false_type {}; - - template <> - struct is_container : std::false_type {}; - - template <> - struct is_container : std::false_type {}; - - template <> - struct is_container : std::false_type {}; - -#ifdef SOL_CXX17_FEATURES - template <> - struct is_container : std::false_type {}; - - template <> - struct is_container : std::false_type {}; - - template <> - struct is_container : std::false_type {}; - - template <> - struct is_container : std::false_type {}; - -#endif // C++ 17 + + template + struct lua_type_of, std::enable_if_t<::sol::is_container::value>> : std::integral_constant {}; template - struct is_container>::value>> : std::true_type {}; + struct lua_type_of, std::enable_if_t::value>> : lua_type_of {}; template class V, typename... Args> struct accumulate : std::integral_constant {}; @@ -942,6 +1006,10 @@ namespace sol { struct is_lua_primitive> : is_lua_primitive { }; template struct is_lua_primitive> : std::true_type {}; + template + struct is_lua_primitive> : std::true_type {}; + template + struct is_lua_primitive> : std::true_type {}; template <> struct is_lua_primitive : std::true_type {}; template <> @@ -954,25 +1022,32 @@ namespace sol { template struct is_transparent_argument : std::false_type {}; - template <> struct is_transparent_argument : std::true_type {}; - template <> struct is_transparent_argument : std::true_type {}; - template <> struct is_transparent_argument : std::true_type {}; + template + struct is_variadic_arguments : std::is_same, variadic_args> {}; template - struct is_variadic_arguments : std::is_same {}; + struct is_lua_index : std::false_type {}; + template <> + struct is_lua_index : std::true_type {}; + template <> + struct is_lua_index : std::true_type {}; + template <> + struct is_lua_index : std::true_type {}; + template <> + struct is_lua_index : std::true_type {}; template struct lua_bind_traits : meta::bind_traits { private: typedef meta::bind_traits base_t; public: - typedef std::integral_constant::value != 0> runtime_variadics_t; + typedef std::integral_constant::value != 0> runtime_variadics_t; static const std::size_t true_arity = base_t::arity; static const std::size_t arity = base_t::arity - meta::count_for::value; static const std::size_t true_free_arity = base_t::free_arity; @@ -986,10 +1061,10 @@ namespace sol { template struct is_function : std::false_type {}; - template - struct is_function> : std::true_type {}; - template - struct is_function> : std::true_type {}; + template + struct is_function> : std::true_type {}; + template + struct is_function> : std::true_type{}; template struct is_lightuserdata : std::false_type {}; @@ -1003,22 +1078,11 @@ namespace sol { template struct is_environment : std::integral_constant::value || is_table::value> {}; - - template - struct is_container : detail::is_container{}; template inline type type_of() { return lua_type_of>::value; } - - namespace detail { - template - struct lua_type_of, std::enable_if_t<::sol::is_container::value>> : std::integral_constant {}; - - template - struct lua_type_of, std::enable_if_t::value>> : lua_type_of {}; - } // detail } // sol #endif // SOL_TYPES_HPP diff --git a/sol/unsafe_function.hpp b/sol/unsafe_function.hpp index 27d7d8b1..3a125ac4 100644 --- a/sol/unsafe_function.hpp +++ b/sol/unsafe_function.hpp @@ -29,23 +29,23 @@ #include namespace sol { - template + template class basic_function : public base_t { private: void luacall(std::ptrdiff_t argcount, std::ptrdiff_t resultcount) const { - lua_callk(base_t::lua_state(), static_cast(argcount), static_cast(resultcount), 0, nullptr); + lua_callk(lua_state(), static_cast(argcount), static_cast(resultcount), 0, nullptr); } template auto invoke(types, std::index_sequence, std::ptrdiff_t n) const { luacall(n, lua_size>::value); - return stack::pop>(base_t::lua_state()); + return stack::pop>(lua_state()); } template Ret invoke(types, std::index_sequence, std::ptrdiff_t n) const { luacall(n, lua_size::value); - return stack::pop(base_t::lua_state()); + return stack::pop(lua_state()); } template @@ -54,22 +54,24 @@ namespace sol { } function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n) const { - int stacksize = lua_gettop(base_t::lua_state()); + int stacksize = lua_gettop(lua_state()); int firstreturn = (std::max)(1, stacksize - static_cast(n)); luacall(n, LUA_MULTRET); - int poststacksize = lua_gettop(base_t::lua_state()); + int poststacksize = lua_gettop(lua_state()); int returncount = poststacksize - (firstreturn - 1); - return function_result(base_t::lua_state(), firstreturn, returncount); + return function_result(lua_state(), firstreturn, returncount); } public: + using base_t::lua_state; + basic_function() = default; template , basic_function>>, meta::neg>, std::is_base_of>> = meta::enabler> basic_function(T&& r) noexcept : base_t(std::forward(r)) { #ifdef SOL_CHECK_ARGUMENTS if (!is_function>::value) { auto pp = stack::push_pop(*this); - stack::check(base_t::lua_state(), -1, type_panic); + stack::check(lua_state(), -1, type_panic); } #endif // Safety } @@ -105,8 +107,10 @@ namespace sol { template decltype(auto) call(Args&&... args) const { - base_t::push(); - int pushcount = stack::multi_push_reference(base_t::lua_state(), std::forward(args)...); + if (!aligned) { + base_t::push(); + } + int pushcount = stack::multi_push_reference(lua_state(), std::forward(args)...); return invoke(types(), std::make_index_sequence(), pushcount); } }; diff --git a/sol/userdata.hpp b/sol/userdata.hpp index d6a52636..a665cfdc 100644 --- a/sol/userdata.hpp +++ b/sol/userdata.hpp @@ -30,13 +30,15 @@ namespace sol { class basic_userdata : public basic_table { typedef basic_table base_t; public: + using base_t::lua_state; + basic_userdata() noexcept = default; template , basic_userdata>>, meta::neg>, std::is_base_of>> = meta::enabler> basic_userdata(T&& r) noexcept : base_t(std::forward(r)) { #ifdef SOL_CHECK_ARGUMENTS if (!is_userdata>::value) { auto pp = stack::push_pop(*this); - type_assert(base_t::lua_state(), -1, type::userdata); + type_assert(lua_state(), -1, type::userdata); } #endif // Safety } @@ -65,13 +67,15 @@ namespace sol { class basic_lightuserdata : public basic_object_base { typedef basic_object_base base_t; public: + using base_t::lua_state; + basic_lightuserdata() noexcept = default; template , basic_lightuserdata>>, meta::neg>, std::is_base_of>> = meta::enabler> basic_lightuserdata(T&& r) noexcept : base_t(std::forward(r)) { #ifdef SOL_CHECK_ARGUMENTS if (!is_lightuserdata>::value) { auto pp = stack::push_pop(*this); - type_assert(base_t::lua_state(), -1, type::lightuserdata); + type_assert(lua_state(), -1, type::lightuserdata); } #endif // Safety } diff --git a/test_container_semantics.cpp b/test_container_semantics.cpp new file mode 100644 index 00000000..7c251341 --- /dev/null +++ b/test_container_semantics.cpp @@ -0,0 +1,812 @@ +#define SOL_CHECK_ARGUMENTS + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +void sequence_container_check(sol::state& lua, T& items) { + { + auto r1 = lua.script(R"( +for i=1,#c do + v = c[i] + assert(v == (i + 10)) +end + )", sol::script_pass_on_error); + REQUIRE(r1.valid()); + } + { + auto ffind = [&]() { + auto r1 = lua.script("i1 = c:find(11)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("i2 = c:find(14)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto fget = [&]() { + auto r1 = lua.script("v1 = c:get(1)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("v2 = c:get(3)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto fset = [&]() { + auto r1 = lua.script("c:set(2, 20)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("c:set(6, 16)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto ferase = [&]() { + auto r5 = lua.script("s1 = #c", sol::script_pass_on_error); + REQUIRE(r5.valid()); + auto r1 = lua.script("c:erase(i1)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r3 = lua.script("s2 = #c", sol::script_pass_on_error); + REQUIRE(r3.valid()); + auto r2 = lua.script("c:erase(i2)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + auto r4 = lua.script("s3 = #c", sol::script_pass_on_error); + REQUIRE(r4.valid()); + }; + auto fadd = [&]() { + auto r = lua.script("c:add(17)", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + auto fopset = [&]() { + auto r = lua.script("c[#c + 1] = 18", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + auto fopget = [&]() { + auto r = lua.script("v3 = c[#c]", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + REQUIRE_NOTHROW(ffind()); + REQUIRE_NOTHROW(fget()); + REQUIRE_NOTHROW(fset()); + REQUIRE_NOTHROW(ferase()); + REQUIRE_NOTHROW(fadd()); + REQUIRE_NOTHROW(fopset()); + REQUIRE_NOTHROW(fopget()); + } + auto backit = items.begin(); + std::size_t len = 0; + { + auto e = items.end(); + auto last = backit; + for (; backit != e; ++backit, ++len) { + if (backit == e) { + break; + } + last = backit; + } + backit = last; + } + const int& first = *items.begin(); + const int& last = *backit; + std::size_t i1 = lua["i1"]; + std::size_t i2 = lua["i2"]; + std::size_t s1 = lua["s1"]; + std::size_t s2 = lua["s2"]; + std::size_t s3 = lua["s3"]; + int v1 = lua["v1"]; + int v2 = lua["v2"]; + int v3 = lua["v3"]; + int values[6] = { + 20, 13, 14, 16, 17, 18 + }; + { + std::size_t idx = 0; + for (const auto& i : items) { + const auto& v = values[idx]; + REQUIRE(i == v); + ++idx; + } + } + REQUIRE(s1 == 6); + REQUIRE(s2 == 5); + REQUIRE(s3 == 4); + REQUIRE(len == 6); + REQUIRE(first == 20); + REQUIRE(last == 18); + REQUIRE(i1 == 1); + REQUIRE(i2 == 4); + REQUIRE(v1 == 11); + REQUIRE(v2 == 13); + REQUIRE(v3 == 18); +} + +template +void ordered_container_check(sol::state& lua, T& items) { + { + auto r1 = lua.script(R"( +for i=1,#c do + v = c[i] + assert(v == (i + 10)) +end + )", sol::script_pass_on_error); + REQUIRE(r1.valid()); + } + { + auto ffind = [&]() { + auto r1 = lua.script("i1 = c:find(11)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("i2 = c:find(14)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto fget = [&]() { + auto r1 = lua.script("v1 = c:get(11)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("v2 = c:get(13)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto fset = [&]() { + auto r1 = lua.script("c:set(20)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("c:set(16)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto ferase = [&]() { + auto r5 = lua.script("s1 = #c", sol::script_pass_on_error); + REQUIRE(r5.valid()); + auto r1 = lua.script("c:erase(i1)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r3 = lua.script("s2 = #c", sol::script_pass_on_error); + REQUIRE(r3.valid()); + auto r2 = lua.script("c:erase(i2)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + auto r4 = lua.script("s3 = #c", sol::script_pass_on_error); + REQUIRE(r4.valid()); + }; + auto fadd = [&]() { + auto r = lua.script("c:add(17)", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + auto fopset = [&]() { + auto r = lua.script("c[18] = true", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + auto fopget = [&]() { + auto r = lua.script("v3 = c[20]", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + REQUIRE_NOTHROW(ffind()); + REQUIRE_NOTHROW(fget()); + REQUIRE_NOTHROW(fset()); + REQUIRE_NOTHROW(ferase()); + REQUIRE_NOTHROW(fadd()); + REQUIRE_NOTHROW(fopset()); + REQUIRE_NOTHROW(fopget()); + } + auto backit = items.begin(); + std::size_t len = 0; + { + auto e = items.end(); + auto last = backit; + for (; backit != e; ++backit, ++len) { + if (backit == e) { + break; + } + last = backit; + } + backit = last; + } + const int& first = *items.begin(); + const int& last = *backit; + int i1 = lua["i1"]; + int i2 = lua["i2"]; + std::size_t s1 = lua["s1"]; + std::size_t s2 = lua["s2"]; + std::size_t s3 = lua["s3"]; + int v1 = lua["v1"]; + int v2 = lua["v2"]; + int v3 = lua["v3"]; + int values[] = { + 12, 13, 15, 16, 17, 18, 20 + }; + { + std::size_t idx = 0; + for (const auto& i : items) { + const auto& v = values[idx]; + REQUIRE(i == v); + ++idx; + } + } + REQUIRE(s1 == 7); + REQUIRE(s2 == 6); + REQUIRE(s3 == 5); + REQUIRE(len == 7); + REQUIRE(first == 12); + REQUIRE(last == 20); + REQUIRE(i1 == 11); + REQUIRE(i2 == 14); + REQUIRE(v1 == 11); + REQUIRE(v2 == 13); + REQUIRE(v3 == 20); +} + +template +void unordered_container_check(sol::state& lua, T& items) { + { + auto ffind = [&]() { + auto r1 = lua.script("i1 = c:find(11)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("i2 = c:find(14)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto fget = [&]() { + auto r1 = lua.script("v1 = c:get(11)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("v2 = c:get(13)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto fset = [&]() { + auto r1 = lua.script("c:set(20)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("c:set(16)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto ferase = [&]() { + auto r5 = lua.script("s1 = #c", sol::script_pass_on_error); + REQUIRE(r5.valid()); + auto r1 = lua.script("c:erase(i1)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r3 = lua.script("s2 = #c", sol::script_pass_on_error); + REQUIRE(r3.valid()); + auto r2 = lua.script("c:erase(i2)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + auto r4 = lua.script("s3 = #c", sol::script_pass_on_error); + REQUIRE(r4.valid()); + }; + auto fadd = [&]() { + auto r = lua.script("c:add(17)", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + auto fopset = [&]() { + auto r = lua.script("c[18] = true", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + auto fopget = [&]() { + auto r = lua.script("v3 = c[20]", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + REQUIRE_NOTHROW(ffind()); + REQUIRE_NOTHROW(fget()); + REQUIRE_NOTHROW(fset()); + REQUIRE_NOTHROW(ferase()); + REQUIRE_NOTHROW(fadd()); + REQUIRE_NOTHROW(fopset()); + REQUIRE_NOTHROW(fopget()); + } + std::size_t len = items.size(); + int i1 = lua["i1"]; + int i2 = lua["i2"]; + std::size_t s1 = lua["s1"]; + std::size_t s2 = lua["s2"]; + std::size_t s3 = lua["s3"]; + int v1 = lua["v1"]; + int v2 = lua["v2"]; + int v3 = lua["v3"]; + int values[] = { + 12, 13, 15, 16, 17, 18, 20 + }; + { + std::size_t idx = 0; + for (const auto& i : items) { + const auto& v = values[idx]; + auto it = items.find(v); + REQUIRE(it != items.cend()); + REQUIRE(*it == v); + ++idx; + } + } + REQUIRE(s1 == 7); + REQUIRE(s2 == 6); + REQUIRE(s3 == 5); + REQUIRE(len == 7); + REQUIRE(i1 == 11); + REQUIRE(i2 == 14); + REQUIRE(v1 == 11); + REQUIRE(v2 == 13); + REQUIRE(v3 == 20); +} + +template +void associative_ordered_container_check(sol::state& lua, T& items) { + { + auto r1 = lua.script(R"( +for i=1,#c do + v = c[i] + assert(v == (i + 10)) +end + )", sol::script_pass_on_error); + REQUIRE(r1.valid()); + } + { + auto ffind = [&]() { + auto r1 = lua.script("i1 = c:find(11)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("i2 = c:find(14)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto fget = [&]() { + auto r1 = lua.script("v1 = c:get(11)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("v2 = c:get(13)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto fset = [&]() { + auto r1 = lua.script("c:set(20, 30)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("c:set(16, 26)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + auto r3 = lua.script("c:set(12, 31)", sol::script_pass_on_error); + REQUIRE(r3.valid()); + }; + auto ferase = [&]() { + auto r5 = lua.script("s1 = #c", sol::script_pass_on_error); + REQUIRE(r5.valid()); + auto r1 = lua.script("c:erase(11)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r3 = lua.script("s2 = #c", sol::script_pass_on_error); + REQUIRE(r3.valid()); + auto r2 = lua.script("c:erase(14)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + auto r4 = lua.script("s3 = #c", sol::script_pass_on_error); + REQUIRE(r4.valid()); + }; + auto fadd = [&]() { + auto r = lua.script("c:add(17, 27)", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + auto fopset = [&]() { + auto r = lua.script("c[18] = 28", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + auto fopget = [&]() { + auto r = lua.script("v3 = c[20]", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + REQUIRE_NOTHROW(ffind()); + REQUIRE_NOTHROW(fget()); + REQUIRE_NOTHROW(fset()); + REQUIRE_NOTHROW(ferase()); + REQUIRE_NOTHROW(fadd()); + REQUIRE_NOTHROW(fopset()); + REQUIRE_NOTHROW(fopget()); + } + auto backit = items.begin(); + std::size_t len = 0; + { + auto e = items.end(); + auto last = backit; + for (; backit != e; ++backit, ++len) { + if (backit == e) { + break; + } + last = backit; + } + backit = last; + } + const std::pair& first = *items.begin(); + const std::pair& last = *backit; + int i1 = lua["i1"]; + int i2 = lua["i2"]; + std::size_t s1 = lua["s1"]; + std::size_t s2 = lua["s2"]; + std::size_t s3 = lua["s3"]; + int v1 = lua["v1"]; + int v2 = lua["v2"]; + int v3 = lua["v3"]; + std::pair values[] = { + { 12, 31 }, + { 13, 23 }, + { 15, 25 }, + { 16, 26 }, + { 17, 27 }, + { 18, 28 }, + { 20, 30 } + }; + { + std::size_t idx = 0; + for (const auto& i : items) { + const auto& v = values[idx]; + REQUIRE(i == v); + ++idx; + } + } + REQUIRE(s1 == 7); + REQUIRE(s2 == 6); + REQUIRE(s3 == 5); + REQUIRE(len == 7); + REQUIRE(first.first == 12); + REQUIRE(last.first == 20); + REQUIRE(first.second == 31); + REQUIRE(last.second == 30); + REQUIRE(i1 == 21); + REQUIRE(i2 == 24); + REQUIRE(v1 == 21); + REQUIRE(v2 == 23); + REQUIRE(v3 == 30); +} + +template +void associative_unordered_container_check(sol::state& lua, T& items) { + { + auto ffind = [&]() { + auto r1 = lua.script("i1 = c:find(11)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("i2 = c:find(14)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto fget = [&]() { + auto r1 = lua.script("v1 = c:get(11)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("v2 = c:get(13)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto fset = [&]() { + auto r1 = lua.script("c:set(20, 30)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("c:set(16, 26)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + auto r3 = lua.script("c:set(12, 31)", sol::script_pass_on_error); + REQUIRE(r3.valid()); + }; + auto ferase = [&]() { + auto r5 = lua.script("s1 = #c", sol::script_pass_on_error); + REQUIRE(r5.valid()); + auto r1 = lua.script("c:erase(11)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r3 = lua.script("s2 = #c", sol::script_pass_on_error); + REQUIRE(r3.valid()); + auto r2 = lua.script("c:erase(14)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + auto r4 = lua.script("s3 = #c", sol::script_pass_on_error); + REQUIRE(r4.valid()); + }; + auto fadd = [&]() { + auto r = lua.script("c:add(17, 27)", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + auto fopset = [&]() { + auto r = lua.script("c[18] = 28", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + auto fopget = [&]() { + auto r = lua.script("v3 = c[20]", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + REQUIRE_NOTHROW(ffind()); + REQUIRE_NOTHROW(fget()); + REQUIRE_NOTHROW(fset()); + REQUIRE_NOTHROW(ferase()); + REQUIRE_NOTHROW(fadd()); + REQUIRE_NOTHROW(fopset()); + REQUIRE_NOTHROW(fopget()); + } + std::size_t len = items.size(); + int i1 = lua["i1"]; + int i2 = lua["i2"]; + std::size_t s1 = lua["s1"]; + std::size_t s2 = lua["s2"]; + std::size_t s3 = lua["s3"]; + int v1 = lua["v1"]; + int v2 = lua["v2"]; + int v3 = lua["v3"]; + std::pair values[] = { + { 12, 31 }, + { 13, 23 }, + { 15, 25 }, + { 16, 26 }, + { 17, 27 }, + { 18, 28 }, + { 20, 30 } + }; + std::pair item_values[7]; + { + std::size_t idx = 0; + for (const auto& i : items) { + const auto& v = values[idx]; + auto it = items.find(v.first); + REQUIRE(it != items.cend()); + REQUIRE(it->second == v.second); + ++idx; + } + } + REQUIRE(s1 == 7); + REQUIRE(s2 == 6); + REQUIRE(s3 == 5); + REQUIRE(len == 7); + REQUIRE(i1 == 21); + REQUIRE(i2 == 24); + REQUIRE(v1 == 21); + REQUIRE(v2 == 23); + REQUIRE(v3 == 30); +} + +template +void fixed_container_check(sol::state& lua, T& items) { + { + auto r1 = lua.script(R"( +for i=1,#c do + v = c[i] + assert(v == (i + 10)) +end + )", sol::script_pass_on_error); + REQUIRE(r1.valid()); + } + { + auto ffind = [&]() { + auto r1 = lua.script("i1 = c:find(11)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("i2 = c:find(14)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto fget = [&]() { + auto r1 = lua.script("v1 = c:get(2)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("v2 = c:get(5)", sol::script_pass_on_error); + REQUIRE(r2.valid()); + }; + auto fset = [&]() { + auto r1 = lua.script("c:set(2, 20)", sol::script_pass_on_error); + REQUIRE(r1.valid()); + auto r2 = lua.script("c:set(6, 16)", sol::script_pass_on_error); + REQUIRE_FALSE(r2.valid()); + }; + auto ferase = [&]() { + auto r5 = lua.script("s1 = #c", sol::script_pass_on_error); + REQUIRE(r5.valid()); + auto r1 = lua.script("c:erase(i1)", sol::script_pass_on_error); + REQUIRE_FALSE(r1.valid()); + auto r3 = lua.script("s2 = #c", sol::script_pass_on_error); + REQUIRE(r3.valid()); + auto r2 = lua.script("c:erase(i2)", sol::script_pass_on_error); + REQUIRE_FALSE(r2.valid()); + auto r4 = lua.script("s3 = #c", sol::script_pass_on_error); + REQUIRE(r4.valid()); + }; + auto fadd = [&]() { + auto r = lua.script("c:add(17)", sol::script_pass_on_error); + REQUIRE_FALSE(r.valid()); + }; + auto fopset = [&]() { + auto r = lua.script("c[5] = 18", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + auto fopget = [&]() { + auto r = lua.script("v3 = c[4]", sol::script_pass_on_error); + REQUIRE(r.valid()); + }; + REQUIRE_NOTHROW(ffind()); + REQUIRE_NOTHROW(fget()); + REQUIRE_NOTHROW(fset()); + REQUIRE_NOTHROW(ferase()); + REQUIRE_NOTHROW(fadd()); + REQUIRE_NOTHROW(fopset()); + REQUIRE_NOTHROW(fopget()); + } + auto backit = std::begin(items); + std::size_t len = 0; + { + auto e = std::end(items); + auto last = backit; + for (; backit != e; ++backit, ++len) { + if (backit == e) { + break; + } + last = backit; + } + backit = last; + } + const int& first = *std::begin(items); + const int& last = *backit; + int i1 = lua["i1"]; + int i2 = lua["i2"]; + std::size_t s1 = lua["s1"]; + std::size_t s2 = lua["s2"]; + std::size_t s3 = lua["s3"]; + int v1 = lua["v1"]; + int v2 = lua["v2"]; + int v3 = lua["v3"]; + int values[] = { + 11, 20, 13, 14, 18 + }; + { + std::size_t idx = 0; + for (const auto& i : items) { + const auto& v = values[idx]; + REQUIRE(i == v); + ++idx; + } + } + REQUIRE(first == 11); + REQUIRE(last == 18); + REQUIRE(s1 == 5); + REQUIRE(s2 == 5); + REQUIRE(s3 == 5); + REQUIRE(len == 5); + REQUIRE(i1 == 1); + REQUIRE(i2 == 4); + REQUIRE(v1 == 12); + REQUIRE(v2 == 15); + REQUIRE(v3 == 14); +} + +TEST_CASE("containers/sequence containers", "check all of the functinos for every single container") { + SECTION("vector") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::vector items{ 11, 12, 13, 14, 15 }; + lua["c"] = &items; + sequence_container_check(lua, items); + } + SECTION("list") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::list items{ 11, 12, 13, 14, 15 }; + lua["c"] = &items; + sequence_container_check(lua, items); + } + SECTION("forward_list") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::forward_list items{ 11, 12, 13, 14, 15 }; + lua["c"] = &items; + sequence_container_check(lua, items); + } + SECTION("deque") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::deque items{ 11, 12, 13, 14, 15 }; + lua["c"] = &items; + sequence_container_check(lua, items); + } +} + +TEST_CASE("containers/fixed containers", "check immutable container types") { + SECTION("array") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::array items{ 11, 12, 13, 14, 15 }; + lua["c"] = &items; + fixed_container_check(lua, items); + } + SECTION("array ref") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::array items{ 11, 12, 13, 14, 15 }; + lua["c"] = std::ref(items); + fixed_container_check(lua, items); + } + SECTION("c array") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + int items[5] = { 11, 12, 13, 14, 15 }; + lua["c"] = &items; + fixed_container_check(lua, items); + } + SECTION("c array ref") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + int items[5] = { 11, 12, 13, 14, 15 }; + lua["c"] = std::ref(items); + fixed_container_check(lua, items); + } +} + +TEST_CASE("containers/ordered lookup containers", "check ordered container types") { + SECTION("set") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::set items{ 11, 12, 13, 14, 15 }; + lua["c"] = &items; + ordered_container_check(lua, items); + } + SECTION("multiset") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::multiset items{ 11, 12, 13, 14, 15 }; + lua["c"] = &items; + ordered_container_check(lua, items); + } +} + +TEST_CASE("containers/unordered lookup containers", "check ordered container types") { + SECTION("unordered_set") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::unordered_set items{ 11, 12, 13, 14, 15 }; + lua["c"] = &items; + unordered_container_check(lua, items); + } + SECTION("unordered_multiset") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::unordered_multiset items{ 11, 12, 13, 14, 15 }; + lua["c"] = &items; + unordered_container_check(lua, items); + } +} + +TEST_CASE("containers/associative ordered containers", "check associative (map) containers that are ordered fulfill basic functionality requirements") { + SECTION("map") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::map items{ + { 11, 21 }, + { 12, 22 }, + { 13, 23 }, + { 14, 24 }, + { 15, 25 } + }; + lua["c"] = &items; + associative_ordered_container_check(lua, items); + } + SECTION("multimap") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::multimap items{ + { 11, 21 }, + { 12, 22 }, + { 13, 23 }, + { 14, 24 }, + { 15, 25 } + }; + lua["c"] = &items; + associative_ordered_container_check(lua, items); + } +} + +TEST_CASE("containers/associative unordered containers", "check associative (map) containers that are ordered that they fulfill basic functionality requirements") { + SECTION("unordered_map") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::unordered_map items{ + { 11, 21 }, + { 12, 22 }, + { 13, 23 }, + { 14, 24 }, + { 15, 25 } + }; + lua["c"] = &items; + associative_unordered_container_check(lua, items); + } + SECTION("unordered_multimap") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + std::unordered_multimap items{ + { 11, 21 }, + { 12, 22 }, + { 13, 23 }, + { 14, 24 }, + { 15, 25 } + }; + lua["c"] = &items; + associative_unordered_container_check(lua, items); + } +} diff --git a/test_containers.cpp b/test_containers.cpp index 159c6c2f..1f50c90f 100644 --- a/test_containers.cpp +++ b/test_containers.cpp @@ -6,7 +6,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -47,6 +50,29 @@ TEST_CASE("containers/returns", "make sure that even references to vectors are b REQUIRE(matching); } +TEST_CASE("containers/table conversion", "test table conversions with as_table and nested") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.set_function("bark", []() { + return sol::as_nested(std::vector{"bark", "woof"}); + }); + + lua.set_function("woof", []() { + return sol::as_nested(std::vector{"bark", "woof"}); + }); + + lua.script("v1 = bark()"); + lua.script("v2 = woof()"); + + sol::as_table_t> as_table_strings = lua["v"]; + sol::nested> nested_strings = lua["v"]; + + std::vector expected_values{"bark", "woof"}; + REQUIRE(as_table_strings.source == expected_values); + REQUIRE(nested_strings.source == expected_values); +} + TEST_CASE("containers/vector roundtrip", "make sure vectors can be round-tripped") { sol::state lua; std::vector v{ 1, 2, 3 }; @@ -59,6 +85,30 @@ TEST_CASE("containers/vector roundtrip", "make sure vectors can be round-tripped REQUIRE(areequal); } +TEST_CASE("containers/deque roundtrip", "make sure deques can be round-tripped") { + sol::state lua; + std::deque v{ 1, 2, 3 }; + lua.set_function("f", [&]() -> std::deque& { + return v; + }); + lua.script("x = f()"); + std::deque x = lua["x"]; + bool areequal = x == v; + REQUIRE(areequal); +} + +TEST_CASE("containers/array roundtrip", "make sure arrays can be round-tripped") { + sol::state lua; + std::array v{ 1, 2, 3 }; + lua.set_function("f", [&]() -> std::array& { + return v; + }); + lua.script("x = f()"); + std::array x = lua["x"]; + bool areequal = x == v; + REQUIRE(areequal); +} + TEST_CASE("containers/list roundtrip", "make sure lists can be round-tripped") { sol::state lua; std::list v{ 1, 2, 3 }; @@ -71,6 +121,18 @@ TEST_CASE("containers/list roundtrip", "make sure lists can be round-tripped") { REQUIRE(areequal); } +TEST_CASE("containers/forward_list roundtrip", "make sure forward_lists can be round-tripped") { + sol::state lua; + std::forward_list v{ 1, 2, 3 }; + lua.set_function("f", [&]() -> std::forward_list& { + return v; + }); + lua.script("x = f()"); + std::forward_list x = lua["x"]; + bool areequal = x == v; + REQUIRE(areequal); +} + TEST_CASE("containers/map roundtrip", "make sure maps can be round-tripped") { sol::state lua; std::map v{ { "a", 1 },{ "b", 2 },{ "c", 3 } }; @@ -119,6 +181,114 @@ TEST_CASE("containers/set roundtrip", "make sure sets can be round-tripped") { REQUIRE(areequal); } +TEST_CASE("containers/vector table roundtrip", "make sure vectors can be round-tripped") { + sol::state lua; + std::vector v{ 1, 2, 3 }; + lua.set_function("f", [&]() { + return sol::as_table(v); + }); + lua.script("x = f()"); + sol::as_table_t> x = lua["x"]; + bool areequal = x.source == v; + REQUIRE(areequal); +} + +TEST_CASE("containers/deque table roundtrip", "make sure deques can be round-tripped") { + sol::state lua; + std::deque v{ 1, 2, 3 }; + lua.set_function("f", [&]() { + return sol::as_table(v); + }); + lua.script("x = f()"); + sol::as_table_t> x = lua["x"]; + bool areequal = x.source == v; + REQUIRE(areequal); +} + +TEST_CASE("containers/array table roundtrip", "make sure arrays can be round-tripped") { + sol::state lua; + std::array v{ 1, 2, 3 }; + lua.set_function("f", [&]() { + return sol::as_table(v); + }); + lua.script("x = f()"); + sol::as_table_t> x = lua["x"]; + bool areequal = x.source == v; + REQUIRE(areequal); +} + +TEST_CASE("containers/list table roundtrip", "make sure lists can be round-tripped") { + sol::state lua; + std::list v{ 1, 2, 3 }; + lua.set_function("f", [&]() { + return sol::as_table(v); + }); + lua.script("x = f()"); + sol::as_table_t> x = lua["x"]; + bool areequal = x.source == v; + REQUIRE(areequal); +} + +TEST_CASE("containers/forward_list table roundtrip", "make sure forward_lists can be round-tripped") { + sol::state lua; + std::forward_list v{ 1, 2, 3 }; + lua.set_function("f", [&]() { + return sol::as_table(v); + }); + lua.script("x = f()"); + sol::as_table_t> x = lua["x"]; + bool areequal = x.source == v; + REQUIRE(areequal); +} + +TEST_CASE("containers/map table roundtrip", "make sure maps can be round-tripped") { + sol::state lua; + std::map v{ { "a", 1 },{ "b", 2 },{ "c", 3 } }; + lua.set_function("f", [&]() { + return sol::as_table(v); + }); + lua.script("x = f()"); + sol::as_table_t> x = lua["x"]; + bool areequal = x.source == v; + REQUIRE(areequal); +} + +TEST_CASE("containers/unordered_map table roundtrip", "make sure unordered_maps can be round-tripped") { + sol::state lua; + std::unordered_map v{ { "a", 1 },{ "b", 2 },{ "c", 3 } }; + lua.set_function("f", [&]() { + return sol::as_table(v); + }); + lua.script("x = f()"); + sol::as_table_t> x = lua["x"]; + bool areequal = x.source == v; + REQUIRE(areequal); +} + +TEST_CASE("containers/unordered_set table roundtrip", "make sure unordered_sets can be round-tripped") { + sol::state lua; + std::unordered_set v{ 1, 2, 3 }; + lua.set_function("f", [&]() { + return sol::as_table(v); + }); + lua.script("x = f()"); + sol::as_table_t> x = lua["x"]; + bool areequal = x.source == v; + REQUIRE(areequal); +} + +TEST_CASE("containers/set table roundtrip", "make sure sets can be round-tripped") { + sol::state lua; + std::set v{ 1, 2, 3 }; + lua.set_function("f", [&]() { + return sol::as_table(v); + }); + lua.script("x = f()"); + sol::as_table_t> x = lua["x"]; + bool areequal = x.source == v; + REQUIRE(areequal); +} + TEST_CASE("containers/custom usertype", "make sure container usertype metatables can be overridden") { typedef std::unordered_map bark; @@ -176,8 +346,8 @@ TEST_CASE("containers/basic serialization", "make sure containers are turned int ); } -#if 0 // glibc is a fuccboi -TEST_CASE("containers/const-serialization", "make sure containers are turned into proper userdata and the basic hooks respect const-ness") { +#if 0 // LUL const int holders +TEST_CASE("containers/const serialization", "make sure containers are turned into proper userdata and the basic hooks respect const-ness") { typedef std::vector woof; sol::state lua; lua.open_libraries(); @@ -187,7 +357,7 @@ TEST_CASE("containers/const-serialization", "make sure containers are turned int ); REQUIRE_THROWS(lua.script("b[1] = 20")); } -#endif // Fuck you, glibc +#endif TEST_CASE("containers/table serialization", "ensure types can be serialized as tables still") { typedef std::vector woof; @@ -665,7 +835,7 @@ TEST_CASE("containers/non_copyable", "make sure non-copyable types in containers } -TEST_CASE("containers/input_iterators", "test shitty input iterators that are all kinds of B L E H") { +TEST_CASE("containers/input iterators", "test shitty input iterators that are all kinds of B L E H") { class int_shim { public: int_shim() = default; @@ -734,8 +904,7 @@ TEST_CASE("containers/input_iterators", "test shitty input iterators that are al value_type fuck_off_gcc_warning() { // "typedef not used locally" // but it's used elsewhere in the code GCC - // so maybe your shitty warning should go - // fuck itself??? + // so maybe your warning should sod off return int_shim(); } diff --git a/test_environments.cpp b/test_environments.cpp index bf9f087f..30be5f61 100644 --- a/test_environments.cpp +++ b/test_environments.cpp @@ -96,7 +96,7 @@ TEST_CASE("environments/shadowing", "Environments can properly shadow and fallba } SECTION("fallback") { sol::environment env_with_fallback(lua, sol::create, lua.globals()); - lua.script("a = 56", env_with_fallback, sol::default_on_error); + lua.script("a = 56", env_with_fallback, sol::script_default_on_error); sol::optional maybe_env_a = env_with_fallback["a"]; sol::optional maybe_global_a = lua["a"]; sol::optional maybe_env_b = env_with_fallback["b"]; @@ -115,7 +115,7 @@ TEST_CASE("environments/shadowing", "Environments can properly shadow and fallba sol::environment env_with_fallback(lua, sol::create, lua.globals()); lua["env"] = env_with_fallback; sol::environment env = lua["env"]; - lua.script("a = 56", env, sol::default_on_error); + lua.script("a = 56", env, sol::script_default_on_error); sol::optional maybe_env_a = env["a"]; sol::optional maybe_global_a = lua["a"]; sol::optional maybe_env_b = env["b"]; diff --git a/test_functions.cpp b/test_functions.cpp index 2f502620..4bdfc354 100644 --- a/test_functions.cpp +++ b/test_functions.cpp @@ -317,7 +317,7 @@ TEST_CASE("functions/function_result and protected_function_result", "Function r auto nontrampolinefx = [](lua_State*) -> int { throw "x"; }; lua_CFunction c_nontrampolinefx = nontrampolinefx; lua.set("nontrampoline", c_nontrampolinefx); - lua.set_function("bark", []() -> int {return 100; }); + lua.set_function("bark", []() -> int { return 100; }); sol::function luahandler = lua["luahandler"]; sol::function cpphandler = lua["cpphandler"]; @@ -866,7 +866,7 @@ TEST_CASE("overloading/c_call", "Make sure that overloading works with c_call fu REQUIRE(r5 == 1); } -TEST_CASE("functions/stack-protect", "make sure functions don't impede on the stack") { +TEST_CASE("functions/stack atomic", "make sure functions don't impede on the stack") { //setup sol/lua sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::string); @@ -920,7 +920,7 @@ TEST_CASE("functions/stack-protect", "make sure functions don't impede on the st REQUIRE(sg.check_stack()); } -TEST_CASE("functions/same-type-closures", "make sure destructions are per-object, not per-type, by destroying one type multiple times") { +TEST_CASE("functions/same type closures", "make sure destructions are per-object, not per-type, by destroying one type multiple times") { static std::set last_my_closures; static bool checking_closures = false; static bool check_failed = false; @@ -959,7 +959,7 @@ TEST_CASE("functions/same-type-closures", "make sure destructions are per-object REQUIRE(last_my_closures.size() == 2); } -TEST_CASE("functions/stack-multi-return", "Make sure the stack is protected after multi-returns") { +TEST_CASE("functions/stack multi-return", "Make sure the stack is protected after multi-returns") { sol::state lua; lua.script("function f () return 1, 2, 3, 4, 5 end"); @@ -982,7 +982,7 @@ TEST_CASE("functions/stack-multi-return", "Make sure the stack is protected afte } } -TEST_CASE("functions/protected-stack-multi-return", "Make sure the stack is protected after multi-returns") { +TEST_CASE("functions/protected stack multi-return", "Make sure the stack is protected after multi-returns") { sol::state lua; lua.script("function f () return 1, 2, 3, 4, 5 end"); @@ -1005,7 +1005,65 @@ TEST_CASE("functions/protected-stack-multi-return", "Make sure the stack is prot } } -TEST_CASE("functions/overloaded-variadic", "make sure variadics work to some degree with overloading") { +TEST_CASE("functions/function_result as arguments", "ensure that function_result can be pushed as its results and not a userdata") { + sol::state lua; + lua.open_libraries(); + + lua.script("function f () return 1, 2, 3, 4, 5 end"); + lua.script("function g (a, b, c, d, e) assert(a == 1) assert(b == 2) assert(c == 3) assert(d == 4) assert(e == 5) end"); + + { + sol::stack_guard sg(lua); + sol::stack::push(lua, double(256.78)); + { + int a, b, c, d, e; + sol::stack_guard sg2(lua); + sol::function pf = lua["f"]; + sol::tie(a, b, c, d, e) = pf(); + REQUIRE(a == 1); + REQUIRE(b == 2); + REQUIRE(c == 3); + REQUIRE(d == 4); + REQUIRE(e == 5); + REQUIRE_NOTHROW([&]() { + lua["g"](pf()); + }()); + } + double f = sol::stack::pop(lua); + REQUIRE(f == 256.78); + } +} + +TEST_CASE("functions/protected_function_result as arguments", "ensure that protected_function_result can be pushed as its results and not a userdata") { + sol::state lua; + lua.open_libraries(); + + lua.script("function f () return 1, 2, 3, 4, 5 end"); + lua.script("function g (a, b, c, d, e) assert(a == 1) assert(b == 2) assert(c == 3) assert(d == 4) assert(e == 5) end"); + + { + sol::stack_guard sg(lua); + sol::stack::push(lua, double(256.78)); + { + int a, b, c, d, e; + sol::stack_guard sg2(lua); + sol::protected_function pf = lua["f"]; + sol::tie(a, b, c, d, e) = pf(); + REQUIRE(a == 1); + REQUIRE(b == 2); + REQUIRE(c == 3); + REQUIRE(d == 4); + REQUIRE(e == 5); + REQUIRE_NOTHROW([&]() { + lua["g"](pf()); + }()); + } + double f = sol::stack::pop(lua); + REQUIRE(f == 256.78); + } +} + +TEST_CASE("functions/overloaded variadic", "make sure variadics work to some degree with overloading") { sol::state lua; lua.open_libraries(); @@ -1024,7 +1082,7 @@ TEST_CASE("functions/overloaded-variadic", "make sure variadics work to some deg REQUIRE(c == 2.2); } -TEST_CASE("functions/sectioning-variadic", "make sure variadics can bite off chunks of data") { +TEST_CASE("functions/sectioning variadic", "make sure variadics can bite off chunks of data") { sol::state lua; lua.open_libraries(sol::lib::base); @@ -1047,7 +1105,7 @@ TEST_CASE("functions/sectioning-variadic", "make sure variadics can bite off chu lua.script("print(x3) assert(x3 == 18)"); } -TEST_CASE("functions/set_function-already-wrapped", "setting a function returned from Lua code that is already wrapped into a sol::function or similar") { +TEST_CASE("functions/set_function already wrapped", "setting a function returned from Lua code that is already wrapped into a sol::function or similar") { SECTION("test different types") { sol::state lua; lua.open_libraries(sol::lib::base); @@ -1118,7 +1176,7 @@ TEST_CASE("functions/set_function-already-wrapped", "setting a function returned } } -TEST_CASE("functions/pointer-nil", "ensure specific semantics for handling pointer-nils passed through sol") { +TEST_CASE("functions/pointer nullptr + nil", "ensure specific semantics for handling pointer-nils passed through sol") { struct nil_test { static void f(nil_test* p) { @@ -1282,7 +1340,7 @@ TEST_CASE("functions/pointer-nil", "ensure specific semantics for handling point } } -TEST_CASE("functions/unique_usertype-overloading", "make sure overloading can work with ptr vs. specifically asking for a unique_usertype") { +TEST_CASE("functions/unique_usertype overloading", "make sure overloading can work with ptr vs. specifically asking for a unique_usertype") { struct test { int special_value = 17; test() : special_value(17) {} diff --git a/test_inheritance.cpp b/test_inheritance.cpp index ccd46b7d..3071dac8 100644 --- a/test_inheritance.cpp +++ b/test_inheritance.cpp @@ -58,7 +58,7 @@ TEST_CASE("inheritance/basic", "test that metatables are properly inherited") { REQUIRE(a == 5); } -TEST_CASE("inheritance/multi-base", "test that multiple bases all work and overloading for constructors works with them") { +TEST_CASE("inheritance/multi base", "test that multiple bases all work and overloading for constructors works with them") { class TestClass00 { public: int Thing() const { return 123; } @@ -153,7 +153,7 @@ tc3 = TestClass03(tc1) REQUIRE(tc3.c == 1); } -TEST_CASE("inheritance/simple-multi-base", "test that multiple bases all work and overloading for constructors works with them") { +TEST_CASE("inheritance/simple multi base", "test that multiple bases all work and overloading for constructors works with them") { class TestClass00 { public: int Thing() const { return 123; } diff --git a/test_operators.cpp b/test_operators.cpp index f07bf77c..fee3a430 100644 --- a/test_operators.cpp +++ b/test_operators.cpp @@ -80,4 +80,4 @@ TEST_CASE("operators/default", "test that generic equality operators and all sor lua.script("assert(v1 == v3)"); lua.script("assert(not (v2 == v3))"); }()); -} \ No newline at end of file +} diff --git a/test_overflow.cpp b/test_overflow.cpp index 2b647d50..ef7b2e4c 100644 --- a/test_overflow.cpp +++ b/test_overflow.cpp @@ -4,7 +4,7 @@ #include -TEST_CASE("issues/stack-overflow", "make sure various operations repeated don't trigger stack overflow") { +TEST_CASE("issues/stack overflow", "make sure various operations repeated don't trigger stack overflow") { sol::state lua; lua.script("t = {};t[0]=20"); lua.script("lua_function=function(i)return i;end"); @@ -30,7 +30,7 @@ TEST_CASE("issues/stack-overflow", "make sure various operations repeated don't } -TEST_CASE("issues/stack-overflow-2", "make sure basic iterators clean up properly when they're not iterated through (e.g., with empty())") { +TEST_CASE("issues/stack overflow 2", "make sure basic iterators clean up properly when they're not iterated through (e.g., with empty())") { sol::state lua; sol::table t = lua.create_table_with(1, "wut"); int MAX = 50000; diff --git a/test_simple_usertypes.cpp b/test_simple_usertypes.cpp index 1b56dcf3..6a35108b 100644 --- a/test_simple_usertypes.cpp +++ b/test_simple_usertypes.cpp @@ -87,7 +87,7 @@ TEST_CASE("simple_usertype/usertypes", "Ensure that simple usertypes properly wo REQUIRE(z == 29); } -TEST_CASE("simple_usertype/usertypes-constructors", "Ensure that calls with specific arguments work") { +TEST_CASE("simple_usertype/usertype constructors", "Ensure that calls with specific arguments work") { struct marker { bool value = false; }; @@ -173,7 +173,7 @@ TEST_CASE("simple_usertype/usertypes-constructors", "Ensure that calls with spec REQUIRE(z == 29); } -TEST_CASE("simple_usertype/shared-ptr-regression", "simple usertype metatables should not screw over unique usertype metatables") { +TEST_CASE("simple_usertype/shared_ptr regression", "simple usertype metatables should not screw over unique usertype metatables") { static int created = 0; static int destroyed = 0; struct test { @@ -322,7 +322,7 @@ TEST_CASE("simple_usertype/variable-control", "test to see if usertypes respond lua.script("print(sw.pb)assert(sw.pb == 27)"); } -TEST_CASE("simple_usertype/factory-constructor-overload-usage", "simple usertypes should probably invoke types") { +TEST_CASE("simple_usertype/factory constructor overloads", "simple usertypes should invoke the proper factories") { class A { public: virtual void a() { throw std::runtime_error("entered base pure virtual implementation"); }; @@ -378,7 +378,7 @@ TEST_CASE("simple_usertype/factory-constructor-overload-usage", "simple usertype REQUIRE(y4 == 3); } -TEST_CASE("simple_usertype/runtime-append", "allow extra functions to be appended at runtime directly to the metatable itself") { +TEST_CASE("simple_usertype/runtime append", "allow extra functions to be appended at runtime directly to the metatable itself") { class A { }; @@ -407,7 +407,7 @@ TEST_CASE("simple_usertype/runtime-append", "allow extra functions to be appende REQUIRE(w == 100); } -TEST_CASE("simple_usertype/destruction-test", "make sure usertypes are properly destructed and don't double-delete memory or segfault") { +TEST_CASE("simple_usertype/destruction test", "make sure usertypes are properly destructed and don't double-delete memory or segfault") { sol::state lua; class CrashClass { @@ -438,7 +438,7 @@ TEST_CASE("simple_usertype/destruction-test", "make sure usertypes are properly } } -TEST_CASE("simple_usertype/table-append", "Ensure that appending to the meta table also affects the internal function table for pointers as well") { +TEST_CASE("simple_usertype/table append", "Ensure that appending to the meta table also affects the internal function table for pointers as well") { struct A { int func() { return 5000; @@ -462,7 +462,7 @@ TEST_CASE("simple_usertype/table-append", "Ensure that appending to the meta tab }()); } -TEST_CASE("simple_usertype/class-propogation", "make sure methods and variables from base classes work properly in SAFE_USERTYPE mode") { +TEST_CASE("simple_usertype/class call propogation", "make sure methods and variables from base classes work properly in SAFE_USERTYPE mode") { class A { public: int var = 200; @@ -488,7 +488,7 @@ TEST_CASE("simple_usertype/class-propogation", "make sure methods and variables )"); } -TEST_CASE("simple_usertype/call-constructor", "ensure that all kinds of call-based constructors can be serialized") { +TEST_CASE("simple_usertype/call constructor", "ensure that all kinds of call-based constructors can be serialized") { struct thing {}; struct v_test { @@ -589,7 +589,7 @@ TEST_CASE("simple_usertype/no_constructor", "make sure simple usertype errors wh } } -TEST_CASE("simple_usertype/missing-key", "make sure a missing key returns nil") { +TEST_CASE("simple_usertype/missing key", "make sure a missing key returns nil") { struct thing {}; sol::state lua; @@ -599,7 +599,7 @@ TEST_CASE("simple_usertype/missing-key", "make sure a missing key returns nil") REQUIRE_NOTHROW(lua.script("print(thing.missingKey)")); } -TEST_CASE("simple_usertype/runtime-extensibility", "Check if usertypes are runtime extensible") { +TEST_CASE("simple_usertype/runtime extensibility", "Check if usertypes are runtime extensible") { struct thing { int v = 20; int func(int a) { return a; } @@ -697,7 +697,7 @@ end } } -TEST_CASE("simple_usertype/runtime-replacement", "ensure that functions can be properly replaced at runtime for non-indexed things") { +TEST_CASE("simple_usertype/runtime replacement", "ensure that functions can be properly replaced at runtime for non-indexed things") { struct heart_base_t {}; struct heart_t : heart_base_t { void func() {} @@ -775,7 +775,7 @@ TEST_CASE("simple_usertype/runtime-replacement", "ensure that functions can be p } } -TEST_CASE("simple_usertype/meta-key-retrievals", "allow for special meta keys (__index, __newindex) to trigger methods even if overwritten directly") { +TEST_CASE("simple_usertype/meta key retrievals", "allow for special meta keys (__index, __newindex) to trigger methods even if overwritten directly") { SECTION("dynamically") { static int writes = 0; static std::string keys[4] = {}; @@ -841,7 +841,7 @@ TEST_CASE("simple_usertype/meta-key-retrievals", "allow for special meta keys (_ } } -TEST_CASE("simple_usertype/static-properties", "allow for static functions to get and set things as a property") { +TEST_CASE("simple_usertype/static properties", "allow for static functions to get and set things as a property") { static int b = 50; struct test_t { static double s_func() { @@ -900,7 +900,7 @@ TEST_CASE("simple_usertype/indexing", "make sure simple usertypes can be indexed } }; - SECTION("no-runtime-additions") { + SECTION("no runtime additions") { sol::state lua; lua.open_libraries(sol::lib::base); lua.new_simple_usertype("test", @@ -918,7 +918,7 @@ TEST_CASE("simple_usertype/indexing", "make sure simple usertypes can be indexed REQUIRE(v == 2); REQUIRE(val == 50); } - SECTION("runtime-additions") { + SECTION("runtime additions") { sol::state lua; lua.open_libraries(sol::lib::base); lua.new_simple_usertype("test", diff --git a/test_state.cpp b/test_state.cpp index 026c4e82..ab22cd68 100644 --- a/test_state.cpp +++ b/test_state.cpp @@ -102,7 +102,7 @@ TEST_CASE("state/require", "opening using a file") { // REQUIRE(thingy1 == thingy2); } -TEST_CASE("state/multi-require", "make sure that requires transfers across hand-rolled script implementation and standard requiref") { +TEST_CASE("state/multi require", "make sure that requires transfers across hand-rolled script implementation and standard requiref") { struct open { static int open_func(lua_State* L) { sol::state_view lua = L; @@ -145,7 +145,7 @@ return 'test3')"); REQUIRE(t3 == "test3"); } -TEST_CASE("state/leak-check", "make sure there are no humongous memory leaks in iteration") { +TEST_CASE("state/leak check", "make sure there are no humongous memory leaks in iteration") { #if 0 sol::state lua; lua.script(R"( @@ -194,7 +194,7 @@ end #endif } -TEST_CASE("state/script-returns", "make sure script returns are done properly") { +TEST_CASE("state/script returns", "make sure script returns are done properly") { std::string script = R"( local example = @@ -271,7 +271,7 @@ return example; lua.script("bar() bar2() foo(1) foo2(1)"); } -TEST_CASE("state/copy-move", "ensure state can be properly copied and moved") { +TEST_CASE("state/copy and move", "ensure state can be properly copied and moved") { sol::state lua; lua["a"] = 1; @@ -292,7 +292,7 @@ TEST_CASE("state/requires-reload", "ensure that reloading semantics do not cause lua.script("require 'io'\nreturn 'test3'"); } -TEST_CASE("state/script-do-load", "test success and failure cases for loading and running scripts") { +TEST_CASE("state/script, do, and load", "test success and failure cases for loading and running scripts") { const static std::string bad_syntax = "weird\n%$@symb\nols"; static const char file_bad_syntax[] = "./temp.bad_syntax.lua"; const static std::string bad_runtime = "bad.code = 20"; @@ -311,13 +311,13 @@ TEST_CASE("state/script-do-load", "test success and failure cases for loading an SECTION("script-handler") { sol::state lua; sol::stack_guard sg(lua); - auto errbs = lua.script(bad_syntax, sol::simple_on_error); + auto errbs = lua.script(bad_syntax, sol::script_pass_on_error); REQUIRE(!errbs.valid()); - auto errbr = lua.script(bad_runtime, sol::simple_on_error); + auto errbr = lua.script(bad_runtime, sol::script_pass_on_error); REQUIRE(!errbr.valid()); - auto result = lua.script(good, sol::simple_on_error); + auto result = lua.script(good, sol::script_pass_on_error); int a = lua["a"]; int ar = result; REQUIRE(result.valid()); @@ -381,13 +381,13 @@ TEST_CASE("state/script-do-load", "test success and failure cases for loading an SECTION("script_file-handler") { sol::state lua; sol::stack_guard sg(lua); - auto errbs = lua.script_file(file_bad_syntax, sol::simple_on_error); + auto errbs = lua.script_file(file_bad_syntax, sol::script_pass_on_error); REQUIRE(!errbs.valid()); - auto errbr = lua.script_file(file_bad_runtime, sol::simple_on_error); + auto errbr = lua.script_file(file_bad_runtime, sol::script_pass_on_error); REQUIRE(!errbr.valid()); - auto result = lua.script_file(file_good, sol::simple_on_error); + auto result = lua.script_file(file_good, sol::script_pass_on_error); int a = lua["a"]; int ar = result; REQUIRE(result.valid()); diff --git a/test_tables.cpp b/test_tables.cpp index cb923c93..83152f37 100644 --- a/test_tables.cpp +++ b/test_tables.cpp @@ -27,7 +27,7 @@ int plop_xyz(int x, int y, std::string z) { return 11; } -TEST_CASE("tables/as-enums", "Making sure enums can be put in and gotten out as values") { +TEST_CASE("tables/as enums", "Making sure enums can be put in and gotten out as values") { enum direction { up, down, @@ -52,7 +52,7 @@ TEST_CASE("tables/as-enums", "Making sure enums can be put in and gotten out as REQUIRE(dir == direction::up); } -TEST_CASE("tables/as-enum-classes", "Making sure enums can be put in and gotten out as values") { +TEST_CASE("tables/as enum classes", "Making sure enums can be put in and gotten out as values") { enum class direction { up, down, @@ -93,7 +93,7 @@ TEST_CASE("tables/cleanup", "make sure tables leave the stack balanced") { } } -TEST_CASE("tables/nested-cleanup", "make sure tables leave the stack balanced") { +TEST_CASE("tables/nested cleanup", "make sure tables leave the stack balanced") { sol::state lua; lua.open_libraries(); @@ -138,7 +138,7 @@ TEST_CASE("tables/new_enum", "Making sure enums can be put in and gotten out as 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") { sol::state lua; lua.open_libraries(sol::lib::base); @@ -195,7 +195,7 @@ TEST_CASE("tables/for-each", "Testing the use of for_each to get values from a l REQUIRE(iterations == tablesize); } -TEST_CASE("tables/for-each-empty", "empty tables should not crash") { +TEST_CASE("tables/for_each empty", "empty tables should not crash") { sol::state lua; lua.open_libraries(sol::lib::base); @@ -329,7 +329,7 @@ TEST_CASE("tables/create", "Check if creating a table is kosher") { REQUIRE((testtable[3] == 4)); } -TEST_CASE("tables/create-local", "Check if creating a table is kosher") { +TEST_CASE("tables/create local", "Check if creating a table is kosher") { sol::state lua; lua["testtable"] = lua.create_table(0, 0, "Woof", "Bark", 1, 2, 3, 4); sol::object testobj = lua["testtable"]; @@ -340,7 +340,7 @@ TEST_CASE("tables/create-local", "Check if creating a table is kosher") { REQUIRE((testtable[3] == 4)); } -TEST_CASE("tables/create-local-named", "Check if creating a table is kosher") { +TEST_CASE("tables/create local named", "Check if creating a table is kosher") { sol::state lua; sol::table testtable = lua.create_table("testtable", 0, 0, "Woof", "Bark", 1, 2, 3, 4); sol::object testobj = lua["testtable"]; @@ -363,7 +363,7 @@ TEST_CASE("tables/create-with-local", "Check if creating a table is kosher") { REQUIRE((testtable[3] == 4)); } -TEST_CASE("tables/functions-variables", "Check if tables and function calls work as intended") { +TEST_CASE("tables/function variables", "Check if tables and function calls work as intended") { sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::os); auto run_script = [](sol::state& lua) -> void { @@ -466,7 +466,7 @@ TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works REQUIRE_NOTHROW(assert1(lua.globals())); } -TEST_CASE("tables/operator[]-valid", "Test if proxies on tables can lazily evaluate validity") { +TEST_CASE("tables/operator[] valid", "Test if proxies on tables can lazily evaluate validity") { sol::state lua; bool isFullScreen = false; auto fullscreennopers = lua["fullscreen"]["nopers"]; @@ -488,7 +488,7 @@ TEST_CASE("tables/operator[]-valid", "Test if proxies on tables can lazily evalu REQUIRE_FALSE(isFullScreen); } -TEST_CASE("tables/operator[]-optional", "Test if proxies on tables can lazily evaluate validity") { +TEST_CASE("tables/operator[] optional", "Test if proxies on tables can lazily evaluate validity") { sol::state lua; sol::optional test1 = lua["no_exist_yet"]; @@ -547,7 +547,7 @@ TEST_CASE("tables/add", "Basic test to make sure the 'add' feature works") { } } -TEST_CASE("tables/bool-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; lua.open_libraries(sol::lib::base); @@ -573,13 +573,13 @@ print(tbl[1]) REQUIRE(v2 == 40); } -TEST_CASE("tables/optional-move", "ensure pushing a sol::optional rvalue correctly moves the contained object"){ +TEST_CASE("tables/optional move", "ensure pushing a sol::optional rvalue correctly moves the contained object into tables") { sol::state sol_state; - struct move_only{ + struct move_only { int secret_code; move_only(const move_only&) = delete; move_only(move_only&&) = default; }; - sol_state["requires_move"] = sol::optional{move_only{0x4D}}; + sol_state["requires_move"] = sol::optional{ move_only{ 0x4D } }; REQUIRE(sol_state["requires_move"].get().secret_code == 0x4D); } diff --git a/test_utility.cpp b/test_utility.cpp index adf363ef..9d761446 100644 --- a/test_utility.cpp +++ b/test_utility.cpp @@ -3,11 +3,30 @@ #include #include +#include + #ifdef SOL_CXX17_FEATURES #include #include #endif +void basic_initialization_and_lib_open() { + sol::state lua; + try { + lua.open_libraries(); + lua["a"] = 24; + int a = lua["a"]; + REQUIRE(a == 24); + } + catch (const sol::error& e) { + INFO(e.what()); + REQUIRE(false); + } + catch (...) { + REQUIRE(false); + } +} + TEST_CASE("utility/variant", "test that variant can be round-tripped") { #ifdef SOL_CXX17_FEATURES @@ -66,4 +85,65 @@ TEST_CASE("utility/string_view", "test that string_view can be taken as an argum #else REQUIRE(true); #endif // C++17 -} \ No newline at end of file +} + +TEST_CASE("utility/thread", "fire up lots of threads at the same time to make sure the initialization changes do not cause horrible crashing data races") { + REQUIRE_NOTHROW([]() { + std::thread thrds[16]; + for (int i = 0; i < 16; i++) { + thrds[i] = std::thread(&basic_initialization_and_lib_open); + } + + for (int i = 0; i < 16; i++) { + thrds[i].join(); + } + }()); +} + +TEST_CASE("utility/this_state", "Ensure this_state argument can be gotten anywhere in the function.") { + struct bark { + int with_state(sol::this_state l, int a, int b) { + lua_State* L = l; + int c = lua_gettop(L); + return a + b + (c - c); + } + + static int with_state_2(int a, sol::this_state l, int b) { + INFO("inside with_state_2"); + lua_State* L = l; + INFO("L is" << (void*)L); + int c = lua_gettop(L); + return a * b + (c - c); + } + }; + + sol::state lua; + INFO("created lua state"); + lua.open_libraries(sol::lib::base); + lua.new_usertype("bark", + "with_state", &bark::with_state + ); + + INFO("setting b and with_state_2"); + bark b; + lua.set("b", &b); + lua.set("with_state_2", bark::with_state_2); + INFO("finished setting"); + INFO("getting fx"); + sol::function fx = lua["with_state_2"]; + INFO("calling fx"); + int a = fx(25, 25); + INFO("finished setting fx"); + INFO("calling a script"); + lua.script("a = with_state_2(25, 25)"); + INFO("calling c script"); + lua.script("c = b:with_state(25, 25)"); + INFO("getting a"); + int la = lua["a"]; + INFO("getting b"); + int lc = lua["c"]; + + REQUIRE(lc == 50); + REQUIRE(a == 625); + REQUIRE(la == 625); +} diff --git a/test_variadics.cpp b/test_variadics.cpp index 678b0832..75b0ff11 100644 --- a/test_variadics.cpp +++ b/test_variadics.cpp @@ -180,3 +180,52 @@ TEST_CASE("variadics/variadic_results", "returning a variable amount of argument REQUIRE(v7 == "borf"); } } + +TEST_CASE("variadics/fallback_constructor", "ensure constructor matching behaves properly in the presence of variadic fallbacks") { + struct vec2 { float x, y; }; + + sol::state lua; + + lua.new_simple_usertype("vec2", + sol::call_constructor, sol::factories([]() { + return vec2{}; + }, [](vec2 const& v) { + return vec2{ v }; + }, [](sol::variadic_args va) { + vec2 res{}; + if (va.size() == 1) { + res.x = va[0].get(); + res.y = va[0].get(); + } + else if (va.size() == 2) { + res.x = va[0].get(); + res.y = va[1].get(); + } + else { + throw sol::error("invalid args"); + } + return res; + }) + ); + + REQUIRE_NOTHROW([&]() { + lua.script("v0 = vec2();"); + lua.script("v1 = vec2(1);"); + lua.script("v2 = vec2(1, 2);"); + lua.script("v3 = vec2(v2)"); + }()); + + vec2& v0 = lua["v0"]; + vec2& v1 = lua["v1"]; + vec2& v2 = lua["v2"]; + vec2& v3 = lua["v3"]; + + REQUIRE(v0.x == 0); + REQUIRE(v0.y == 0); + REQUIRE(v1.x == 1); + REQUIRE(v1.y == 1); + REQUIRE(v2.x == 1); + REQUIRE(v2.y == 2); + REQUIRE(v3.x == v2.x); + REQUIRE(v3.y == v2.y); +} diff --git a/tests.cpp b/tests.cpp index a2d80f4f..523d9440 100644 --- a/tests.cpp +++ b/tests.cpp @@ -121,7 +121,7 @@ TEST_CASE("simple/get", "Tests if the get function works properly.") { } REQUIRE(begintop == endtop); } -TEST_CASE("simple/set-get-global-integer", "Tests if the get function works properly with global integers") { +TEST_CASE("simple/set and get global integer", "Tests if the get function works properly with global integers") { sol::state lua; lua[1] = 25.4; lua.script("b = 1"); @@ -148,7 +148,7 @@ TEST_CASE("simple/get_or", "check if table.get_or works correctly") { REQUIRE(bark == 55.6); } -TEST_CASE("simple/proxy_get_or", "check if proxy.get_or works correctly") { +TEST_CASE("simple/proxy get_or", "check if proxy.get_or works correctly") { sol::state lua; auto bob_table = lua.create_table("bob"); @@ -186,7 +186,7 @@ TEST_CASE("simple/if", "check if if statements work through lua") { REQUIRE((f == lua["f"])); } -TEST_CASE("negative/basic_errors", "Check if error handling works correctly") { +TEST_CASE("negative/basic errors", "Check if error handling works correctly") { sol::state lua; REQUIRE_THROWS(lua.script("nil[5]")); @@ -227,54 +227,6 @@ TEST_CASE("interop/null-to-nil-and-back", "nil should be the given type when a p "assert(x == nil)")); } -TEST_CASE("utilities/this_state", "Ensure this_state argument can be gotten anywhere in the function.") { - struct bark { - int with_state(sol::this_state l, int a, int b) { - lua_State* L = l; - int c = lua_gettop(L); - return a + b + (c - c); - } - - static int with_state_2(int a, sol::this_state l, int b) { - INFO("inside with_state_2"); - lua_State* L = l; - INFO("L is" << (void*)L); - int c = lua_gettop(L); - return a * b + (c - c); - } - }; - - sol::state lua; - INFO("created lua state"); - lua.open_libraries(sol::lib::base); - lua.new_usertype("bark", - "with_state", &bark::with_state - ); - - INFO("setting b and with_state_2"); - bark b; - lua.set("b", &b); - lua.set("with_state_2", bark::with_state_2); - INFO("finished setting"); - INFO("getting fx"); - sol::function fx = lua["with_state_2"]; - INFO("calling fx"); - int a = fx(25, 25); - INFO("finished setting fx"); - INFO("calling a script"); - lua.script("a = with_state_2(25, 25)"); - INFO("calling c script"); - lua.script("c = b:with_state(25, 25)"); - INFO("getting a"); - int la = lua["a"]; - INFO("getting b"); - int lc = lua["c"]; - - REQUIRE(lc == 50); - REQUIRE(a == 625); - REQUIRE(la == 625); -} - TEST_CASE("object/conversions", "make sure all basic reference types can be made into objects") { sol::state lua; lua.open_libraries(sol::lib::base); @@ -329,7 +281,7 @@ TEST_CASE("object/conversions", "make sure all basic reference types can be made REQUIRE(oenv.get_type() == sol::type::table); } -TEST_CASE("feature/indexing-overrides", "make sure index functions can be overridden on types") { +TEST_CASE("feature/indexing overrides", "make sure index functions can be overridden on types") { struct PropertySet { sol::object get_property_lua(const char* name, sol::this_state s) { @@ -374,7 +326,7 @@ print('name = ' .. obj.props.name) REQUIRE(name == "test name"); } -TEST_CASE("features/indexing-numbers", "make sure indexing functions can be override on usertypes") { +TEST_CASE("features/indexing numbers", "make sure indexing functions can be override on usertypes") { class vector { public: double data[3]; @@ -415,7 +367,7 @@ TEST_CASE("features/indexing-numbers", "make sure indexing functions can be over REQUIRE(v[2] == 3.0); } -TEST_CASE("features/multiple-inheritance", "Ensure that multiple inheritance works as advertised") { +TEST_CASE("features/multiple inheritance", "Ensure that multiple inheritance works as advertised") { struct base1 { int a1 = 250; }; @@ -494,7 +446,7 @@ TEST_CASE("regressions/std::ref", "Ensure that std::reference_wrapper<> isn't co REQUIRE(vr.a1 == 568); } -TEST_CASE("optional/left-out-args", "Make sure arguments can be left out of optional without tanking miserably") { +TEST_CASE("optional/left out args", "Make sure arguments can be left out of optional without tanking miserably") { sol::state lua; lua.open_libraries(sol::lib::base); @@ -563,7 +515,7 @@ TEST_CASE("proxy/equality", "check to make sure equality tests work") { REQUIRE((lua["a"] == 2)); //1 } -TEST_CASE("compilation/const-regression", "make sure constness in tables is respected all the way down") { +TEST_CASE("compilation/const regression", "make sure constness in tables is respected all the way down") { struct State { public: State() { @@ -601,7 +553,7 @@ TEST_CASE("numbers/integers", "make sure integers are detectable on most platfor REQUIRE(b_is_double); } -TEST_CASE("object/is-method", "test whether or not the is abstraction works properly for a user-defined type") { +TEST_CASE("object/is", "test whether or not the is abstraction works properly for a user-defined type") { struct thing {}; SECTION("stack_object")