Documentation fixes, new tests, unique/shared_ptr support.

Closes #32
This commit is contained in:
ThePhD 2016-03-13 08:30:14 -04:00
parent ce4ebb367e
commit c42c1bafe5
24 changed files with 381 additions and 242 deletions

View File

@ -10,6 +10,6 @@ the single exception type
error(const std::string& str): std::runtime_error("Lua: error: " + str) {} error(const std::string& str): std::runtime_error("Lua: error: " + str) {}
}; };
If an eror is thrown by Sol, it is going to be of this type. We use this in a single place: the default ``at_panic`` function we bind on construction of a :ref:`sol::state<state#set-panic>`. If you turn :doc:`off exceptions<../exceptions>`, the chances of you seeing this error are :doc:`nil<types>`. If an eror is thrown by Sol, it is going to be of this type. We use this in a single place: the default ``at_panic`` function we bind on construction of a :ref:`sol::state<set-panic>`. If you turn :doc:`off exceptions<../exceptions>`, the chances of you seeing this error are :doc:`nil<types>`.
As it derives from ``std::runtime_error``, which derives from ``std::exception``, you can catch it with a ``catch (const std::exception& )`` clause in your try/catch blocks. As it derives from ``std::runtime_error``, which derives from ``std::exception``, you can catch it with a ``catch (const std::exception& )`` clause in your try/catch blocks.

View File

@ -7,7 +7,7 @@ calling functions bound to Lua
class function : public reference; class function : public reference;
Function is a correct-assuming version of :doc:`protected_function<protected_function>`, omitting the need for typechecks and error handling. It is the default function type of Sol. When called without the return types being specified by either a ``sol::types<...>`` list or a ``call<Ret...>( ... )`` template type list, it generates a :ref:`function_result<proxy#function-result>` class that gets implicitly converted to the requested return type. For example: Function is a correct-assuming version of :doc:`protected_function<protected_function>`, omitting the need for typechecks and error handling. It is the default function type of Sol. When called without the return types being specified by either a ``sol::types<...>`` list or a ``call<Ret...>( ... )`` template type list, it generates a :ref:`function_result<function-result>` class that gets implicitly converted to the requested return type. For example:
.. code-block:: lua .. code-block:: lua
:caption: func_barks.lua :caption: func_barks.lua
@ -31,11 +31,11 @@ The following C++ code will call this function from this file and retrieve the r
sol::function woof = lua["woof"]; sol::function woof = lua["woof"];
double numwoof = woof(20); double numwoof = woof(20);
The call ``woof(20)`` generates a :doc:`function_result<proxy>`, which is then implicitly converted to an ``double`` after being called. The intermediate temporary ``function_result`` is then destructed, popping the Lua function call results off the Lua stack. The call ``woof(20)`` generates a :ref:`function_result<function-result>`, which is then implicitly converted to an ``double`` after being called. The intermediate temporary ``function_result`` is then destructed, popping the Lua function call results off the Lua stack.
.. warning:: .. warning::
Do NOT save the return type of a :doc:`function_result<proxy>` with ``auto``, as in ``auto numwoof = woof(20);``, and do NOT store it anywhere. Unlike its counterpart :doc:`protected_function_result<proxy>`, ``function_result`` is NOT safe to store as it assumes that its return types are still at the top of the stack and when its destructor is called will pop the number of results the function was supposed to return off the top of the stack. If you mess with the Lua stack between saving ``function_result`` and it being destructed, you will be subject to an incredible number of surprising and hard-to-track bugs. Don't do it. Do NOT save the return type of a :ref:`function_result<function-result>` with ``auto``, as in ``auto numwoof = woof(20);``, and do NOT store it anywhere. Unlike its counterpart :ref:`protected_function_result<protected-function-result>`, ``function_result`` is NOT safe to store as it assumes that its return types are still at the top of the stack and when its destructor is called will pop the number of results the function was supposed to return off the top of the stack. If you mess with the Lua stack between saving ``function_result`` and it being destructed, you will be subject to an incredible number of surprising and hard-to-track bugs. Don't do it.
.. code-block:: cpp .. code-block:: cpp
:caption: function: call operator / function call :caption: function: call operator / function call

View File

@ -8,7 +8,7 @@ general-purpose safety reference to an existing object
class object : reference; class object : reference;
``object``'s goal is to allow someone to pass around the most generic form of a reference to something in Lua (or propogate a ``nil``). It is the logical extension of :doc:`sol::reference<reference>`, and is used in :doc:`sol::table<table>`'s iterators. ``object``'s goal is to allow someone to pass around the most generic form of a reference to something in Lua (or propogate a ``nil``). It is the logical extension of :doc:`sol::reference<reference>`, and is used in :ref:`sol::table's iterators<table-iterators>`.
members members
@ -28,7 +28,7 @@ Performs a cast of the item this reference refers to into the type ``T`` and ret
template<typename T> template<typename T>
bool is() const; bool is() const;
Performs a type check using the :doc:`sol::stack::check<stack>` api, after checking if the reference is valid. Performs a type check using the :ref:`sol::stack::check<checker>` api, after checking if the reference is valid.
non-members non-members
@ -42,4 +42,4 @@ non-members
bool operator!=(const object& lhs, const nil_t&); bool operator!=(const object& lhs, const nil_t&);
bool operator!=(const nil_t&, const object& rhs); bool operator!=(const nil_t&, const object& rhs);
These allow a person to compare an ``sol::object`` against ``nil``, which essentially checks if an object references a non-nil value, like so: These allow a person to compare an ``sol::object`` against :ref:`nil<nil>`, which essentially checks if an object references a non-nil value, like so:

View File

@ -3,7 +3,7 @@ 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 statemetns and switches on what version of a function to call based on `luaL_check{number/udata/string}<http://www.Lua.org/manual/5.3/manual.html#luaL_checkinteger>`. Its use is simple: whereever you can pass a function type to Lua, whether its on a :doc:`usertype<usertype>` or if you are just setting any kind of function with ``set`` or ``set_function`` (for :doc:`table<table>` or :doc:`state(_view)<state>`), simply wrap up the functions you wish to be considered for overload resolution on one function like so: 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 statemetns and switches on what version of a function to call based on `luaL_check{number/udata/string}`_. Its use is simple: whereever you can pass a function type to Lua, whether its on a :doc:`usertype<usertype>` or if you are just setting any kind of function with ``set`` or ``set_function`` (for :doc:`table<table>` or :doc:`state(_view)<state>`), simply wrap up the functions you wish to be considered for overload resolution on one function like so:
.. code-block:: cpp .. code-block:: cpp
@ -85,3 +85,5 @@ The actual class produced by ``sol::overload`` is essentially a type-wrapper aro
.. note:: .. 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). 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

View File

@ -18,6 +18,22 @@ proxy, (protected\_)function_result - proxy_base derivatives
These classes provide implicit ``operator=`` (``set``) and ``operator T`` (``get``) support for items retrieved from the underlying Lua implementation in Sol, specifically :doc:`sol::table<table>` and the results of function calls on :doc:`sol::function<function>` and :doc:`sol::protected_function<protected_function>`. These classes provide implicit ``operator=`` (``set``) and ``operator T`` (``get``) support for items retrieved from the underlying Lua implementation in Sol, specifically :doc:`sol::table<table>` and the results of function calls on :doc:`sol::function<function>` and :doc:`sol::protected_function<protected_function>`.
.. _function-result:
function_result
---------------
``function_result`` is a temporary-only, intermediate-only implicit conversion worker for when :doc:`function<function>` is called. It is *NOT* meant to be stored or captured with ``auto``. It provides fast access to the desired underlying value. It does not implement ``set`` / ``set_function`` / templated ``operator=``, as show below.
.. _protected-function-result:
protected_function_result
-------------------------
``protected_function_result`` is a nicer version of ``function_result`` that can be used to detect errors. Its gives safe access to the desired underlying value. It does not implement ``set`` / ``set_function`` / templated ``operator=``, as shown below.
proxy proxy
----- -----
@ -45,7 +61,6 @@ After loading that file in or putting it in a string and reading the string dire
std::string x = lua["bark"]["woof"][2]; std::string x = lua["bark"]["woof"][2];
``proxy`` lazy evaluation: ``proxy`` lazy evaluation:
.. code-block:: c++ .. code-block:: c++
@ -89,6 +104,18 @@ members
Gets the value associated with the keys the proxy was generated and convers it to the type ``T``. Note that this function will always return ``T&``, a non-const reference, to types which are not based on :doc:`sol::reference<reference>` and not a :doc:`primitive lua type<types>` Gets the value associated with the keys the proxy was generated and convers it to the type ``T``. Note that this function will always return ``T&``, a non-const reference, to types which are not based on :doc:`sol::reference<reference>` and not a :doc:`primitive lua type<types>`
.. code-block:: c++
:caption: function: get a value
:name: regular-get
template <typename T>
T get( ) const;
Gets the value associated with the keys the proxy was generated and convers it to the type ``T``.
proxy-only members
------------------
.. code-block:: c++ .. code-block:: c++
:caption: functions: [overloaded] implicit set :caption: functions: [overloaded] implicit set
:name: implicit-set :name: implicit-set
@ -101,28 +128,31 @@ Gets the value associated with the keys the proxy was generated and convers it t
template <typename Fx> template <typename Fx>
proxy& operator=( Fx&& function ); proxy& operator=( Fx&& function );
Sets the value associated with the keys the proxy was generated with to ``value``. If this is a function, calls ``set_function``. If it is not, just calls ``set``. See :ref:`note<note 1>` Sets the value associated with the keys the proxy was generated with to ``value``. If this is a function, calls ``set_function``. If it is not, just calls ``set``. Does not exist on :ref:`function_result<function-result>` or :ref:`protected_function_result<protected-function-result>`. See :ref:`note<note 1>` for caveats.
.. code-block:: c++ .. code-block:: c++
:caption: function: set a callable :caption: function: set a callable
:name: regular-set-function
template <typename Fx> template <typename Fx>
proxy& set_function( Fx&& fx ); proxy& set_function( Fx&& fx );
Sets the value associated with the keys the proxy was generated with to a function ``fx``. Sets the value associated with the keys the proxy was generated with to a function ``fx``. Does not exist on :ref:`function_result<function-result>` or :ref:`protected_function_result<protected-function-result>`.
.. code-block:: c++ .. code-block:: c++
:caption: function: get a value :caption: function: set a value
:name: regular-set
template <typename T> template <typename T>
T get( ) const; proxy& set( T&& value );
Gets the value associated with the keys the proxy was generated and convers it to the type ``T``. Sets the value associated with the keys the proxy was generated with to ``value``. Does not exist on :ref:`function_result<function-result>` or :ref:`protected_function_result<protected-function-result>`.
.. _note 1: .. _note 1:
Note: Function Objects and proxies On Function Objects and proxies
---------------------------------- ----------------------------------
Consider the following: Consider the following:
@ -144,18 +174,3 @@ Consider the following:
// but it binds as a function // but it binds as a function
When you use the ``lua["object"] = doge{};`` from above, keep in mind that Sol detects if this is a function *callable with any kind of arguments*. If ``doge`` has overriden ``return_type operator()( argument_types... )`` on itself, it may result in satisfying the ``requires`` constraint from above. This means that if you have a user-defined type you want to bind as a :doc:`userdata with usertype semantics<usertype>` with this syntax, it might get bound as a function and not as a user-defined type. use ``lua["object"].set(doge)`` directly to avoid this, or ``lua["object"].set_function(doge{})`` to perform this explicitly. When you use the ``lua["object"] = doge{};`` from above, keep in mind that Sol detects if this is a function *callable with any kind of arguments*. If ``doge`` has overriden ``return_type operator()( argument_types... )`` on itself, it may result in satisfying the ``requires`` constraint from above. This means that if you have a user-defined type you want to bind as a :doc:`userdata with usertype semantics<usertype>` with this syntax, it might get bound as a function and not as a user-defined type. use ``lua["object"].set(doge)`` directly to avoid this, or ``lua["object"].set_function(doge{})`` to perform this explicitly.
.. _function-result:
function_result
---------------
``function_result`` is a temporary-only, intermediate-only implicit conversion worker for when :doc:`function<function>` is called. It is *NOT* meant to be stored or captured with ``auto``. It provides fast access to the desired underlying value. It does not implement ``set`` / templated ``operator=``.
.. _protected-function-result:
protected_function_result
-------------------------
``protected_function_result`` is a nicer version of ``function_result`` that can be used to detect errors. It sag\fe access to the desired underlying value. It does not implement ``set`` / templated ``operator=``.

View File

@ -79,10 +79,9 @@ objects (extension points)
The structs below are already overriden for a handful of types. If you try to mess with them for the types ``sol`` has already overriden them for, you're in for a world of thick template error traces and headaches. Overriding them for your own user defined types should be just fine, however. The structs below are already overriden for a handful of types. If you try to mess with them for the types ``sol`` has already overriden them for, you're in for a world of thick template error traces and headaches. Overriding them for your own user defined types should be just fine, however.
.. _getter:
.. code-block:: cpp .. code-block:: cpp
:caption: struct: getter :caption: struct: getter
:name: getter
template <typename T, typename = void> template <typename T, typename = void>
struct getter { struct getter {
@ -94,10 +93,9 @@ The structs below are already overriden for a handful of types. If you try to me
This is an SFINAE-friendly struct that is meant to expose static function ``get`` that returns a ``T``, or something convertible to it. The default implementation assumes ``T`` is a usertype and pulls out a userdata from Lua before attempting to cast it to the desired ``T``. There are implementations for getting numbers (``std::is_floating``, ``std::is_integral``-matching types), getting ``std::string`` and ``const char*``, getting raw userdata with :doc:`userdata_value<types>` and anything as upvalues with :doc:`upvalue_index<types>`, getting raw `lua_CFunction`_ s, and finally pulling out Lua functions into ``std::function<R(Args...)>``. It is also defined for anything that derives from :doc:`sol::reference<reference>`. This is an SFINAE-friendly struct that is meant to expose static function ``get`` that returns a ``T``, or something convertible to it. The default implementation assumes ``T`` is a usertype and pulls out a userdata from Lua before attempting to cast it to the desired ``T``. There are implementations for getting numbers (``std::is_floating``, ``std::is_integral``-matching types), getting ``std::string`` and ``const char*``, getting raw userdata with :doc:`userdata_value<types>` and anything as upvalues with :doc:`upvalue_index<types>`, getting raw `lua_CFunction`_ s, and finally pulling out Lua functions into ``std::function<R(Args...)>``. It is also defined for anything that derives from :doc:`sol::reference<reference>`.
.. _pusher:
.. code-block:: cpp .. code-block:: cpp
:caption: struct: pusher :caption: struct: pusher
:name: pusher
template <typename X, typename = void> template <typename X, typename = void>
struct pusher { struct pusher {
@ -109,10 +107,11 @@ This is an SFINAE-friendly struct that is meant to expose static function ``get`
} }
}; };
This is an SFINAE-friendly struct that is meant to expose static function ``push`` that returns the number of things pushed onto the stack. The default implementation assumes ``T`` is a usertype and pushes a userdata into Lua with a :doc:`usertype_traits\<T><usertype>` metatable associated with it. There are implementations for pushing numbers (``std::is_floating``, ``std::is_integral``-matching types), getting ``std::string`` and ``const char*``, getting raw userdata with :doc:`userdata<types>` and raw upvalues with :doc:`upvalue<types>`, getting raw `lua_CFunction`_ s, and finally pulling out Lua functions into ``sol::function``. It is also defined for anything that derives from :doc:`sol::reference<reference>`. This is an SFINAE-friendly struct that is meant to expose static function ``push`` that returns the number of things pushed onto the stack. The default implementation assumes ``T`` is a usertype and pushes a userdata into Lua with a :ref:`usertype_traits\<T><usertype-traits>` metatable associated with it. There are implementations for pushing numbers (``std::is_floating``, ``std::is_integral``-matching types), getting ``std::string`` and ``const char*``, getting raw userdata with :doc:`userdata<types>` and raw upvalues with :doc:`upvalue<types>`, getting raw `lua_CFunction`_ s, and finally pulling out Lua functions into ``sol::function``. It is also defined for anything that derives from :doc:`sol::reference<reference>`.
.. code-block:: cpp .. code-block:: cpp
:caption: struct: checker :caption: struct: checker
:name: checker
template <typename T, type expected = lua_type_of<T>, typename = void> template <typename T, type expected = lua_type_of<T>, typename = void>
struct checker { struct checker {
@ -127,6 +126,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. 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 mroe derived type is, unfortunately, impossible to do.
.. _lua_CFunction: http://www.Lua.org/manual/5.3/manual.html#lua_CFunction .. _lua_CFunction: http://www.Lua.org/manual/5.3/manual.html#lua_CFunction

View File

@ -69,6 +69,7 @@ Sets a previously created usertype with the specified ``key`` into the table. No
.. code-block:: cpp .. code-block:: cpp
:caption: function: begin / end for iteration :caption: function: begin / end for iteration
:name: table-iterators
table_iterator begin () const; table_iterator begin () const;
table_iterator end() const; table_iterator end() const;
@ -79,6 +80,7 @@ Provides `input iterators`_ for a table. This allows tables to work with single-
.. code-block:: cpp .. code-block:: cpp
:caption: function: iteration with a function :caption: function: iteration with a function
:name: table-for-each
template <typename Fx> template <typename Fx>
void for_each(Fx&& fx); void for_each(Fx&& fx);

View File

@ -131,7 +131,8 @@ members
------- -------
.. code-block:: cpp .. code-block:: cpp
:caption: usertype<T> constructor :caption: function: usertype<T> constructor
:name: usertype-constructor
template<typename... Args> template<typename... Args>
usertype<T>(Args&&... args); usertype<T>(Args&&... args);
@ -160,16 +161,16 @@ The constructor of usertype takes a variable number of arguments. It takes an ev
* ``"{name}", &type::function_name`` or ``"{name}", &type::member_variable`` * ``"{name}", &type::function_name`` or ``"{name}", &type::member_variable``
- Binds a typical member function or variable to ``"{name}"``. In the case of a member variable or member function, ``type`` must be ``T`` or a base of ``T``. - Binds a typical member function or variable to ``"{name}"``. In the case of a member variable or member function, ``type`` must be ``T`` or a base of ``T``.
* ``sol::base_classes, sol::bases<Bases...>`` * ``sol::base_classes, sol::bases<Bases...>``
- Tells a usertype what its base classes are. If you have exceptions turned on, this need not be necessary: if you do not then you need this to have derived-to-base conversions work properly. See :ref:`inheritance<usertype_inheritance>`. - Tells a usertype what its base classes are. If you have exceptions turned on, this need not be necessary: if you do not then you need this to have derived-to-base conversions work properly. See :ref:`inheritance<usertype-inheritance>`.
overloading overloading
----------- -----------
Functions set here support overloading. See :doc:`here<overload>` for an example. Functions set on a usertype support overloading. See :doc:`here<overload>` for an example.
.. _usertype_inheritance: .. _usertype-inheritance:
inheritance inheritance
----------- -----------
@ -231,6 +232,7 @@ traits
.. code-block:: cpp .. code-block:: cpp
:caption: usertype_traits<T> :caption: usertype_traits<T>
:linenos: :linenos:
:name: usertype-traits
template<typename T> template<typename T>
struct usertype_traits { struct usertype_traits {

View File

@ -5,7 +5,7 @@ since somebody is going to ask about it...
Yes, you can turn off exceptions in Sol with ``#define SOL_NO_EXCEPTIONS`` before including or by passing the command line argument that defines ``SOL_NO_EXCEPTIONS``. We don't recommend it unless you're playing with a Lua distro that also doesn't play nice with exceptions (like non-x64 versions of :ref:`LuaJIT<LuaJIT and exceptions>` ). Yes, you can turn off exceptions in Sol with ``#define SOL_NO_EXCEPTIONS`` before including or by passing the command line argument that defines ``SOL_NO_EXCEPTIONS``. We don't recommend it unless you're playing with a Lua distro that also doesn't play nice with exceptions (like non-x64 versions of :ref:`LuaJIT<LuaJIT and exceptions>` ).
If you turn this off, the default `at_panic<http://www.Lua.org/manual/5.3/manual.html#4.6>` function :doc:`state<api/state>` set for you will not throw. Instead, the default Lua behavior of aborting will take place (and give you no chance of escape unless you implement your own at_panic function and decide to try ``longjmp`` out). If you turn this off, the default `at_panic`_ function :doc:`state<api/state>` set for you will not throw. Instead, the default Lua behavior of aborting will take place (and give you no chance of escape unless you implement your own at_panic function and decide to try ``longjmp`` out).
Note that this will also disable :doc:`protected_function<api/protected_function>`'s ability to catch C++ errors you throw from C++ functions bound to Lua that you are calling through that API. So, only turn off exceptions in Sol if you're sure you're never going to use them ever. Of course, if you are ALREADY not using Exceptions, you don't have to particularly worry about this and now you can use Sol! Note that this will also disable :doc:`protected_function<api/protected_function>`'s ability to catch C++ errors you throw from C++ functions bound to Lua that you are calling through that API. So, only turn off exceptions in Sol if you're sure you're never going to use them ever. Of course, if you are ALREADY not using Exceptions, you don't have to particularly worry about this and now you can use Sol!
@ -15,7 +15,7 @@ If there is a place where a throw statement is called or a try/catch is used and
inheritance inheritance
----------- -----------
Sol uses a nifty feature of exceptions to perform infinitely scaling, compiler-correct casting of derived pointers to their base classes. If you disable this and you want to have a :doc:`user-defined type<api/usertype>` that can be used with functions that take its base class or similar, you must specify those base classes manually using the :doc:`base_classes<api/usertype>` tag with the :doc:`bases\<Types...><api/usertype>` arguments when you create the usertype. If you turn exceptions off and are also completely mad and turn off :doc:`run-time type information<rtti>` as well, we fallback to a id-based base class system that still requires you to specifically list the base classes using the method mentioned previously. But if you have already turned off rtti and exceptions, you must really know what you're doing, so you should be just fine! Sol uses a nifty feature of exceptions to perform infinitely scaling, compiler-correct casting of derived pointers to their base classes. If you disable this and you want to have a :doc:`user-defined type<api/usertype>` that can be used with functions that take its base class or similar, you must specify those base classes manually using the :ref:`base_classes<usertype-inheritance>` tag with the :ref:`bases\<Types...><usertype-inheritance>` arguments when you create the usertype. If you turn exceptions off and are also completely mad and turn off :doc:`run-time type information<rtti>` as well, we fallback to a id-based base class system that still requires you to specifically list the base classes using the method mentioned previously. But if you have already turned off rtti and exceptions, you must really know what you're doing, so you should be just fine!
.. _LuaJIT and exceptions: .. _LuaJIT and exceptions:
@ -23,8 +23,11 @@ Sol uses a nifty feature of exceptions to perform infinitely scaling, compiler-c
LuaJIT and exceptions LuaJIT and exceptions
--------------------- ---------------------
It is important to note that a popular 5.1 distribution of Lua, LuaJIT, has some serious `caveats regarding exceptions<http://luajit.org/extensions.html#exceptions>`. LuaJIT's exception promises are flaky at best on x64 (64-bit) platforms, and entirely terrible on non-x64 (32-bit, ARM, etc.) platorms. The trampolines we have in place for all functions bound through conventional means in Sol will catch exceptions and turn them into Lua errors so that LuaJIT remainds unperturbed, but if you link up a C function directly yourself and throw, chances are you might have screwed the pooch. It is important to note that a popular 5.1 distribution of Lua, LuaJIT, has some serious `caveats regarding exceptions`_. LuaJIT's exception promises are flaky at best on x64 (64-bit) platforms, and entirely terrible on non-x64 (32-bit, ARM, etc.) platorms. The trampolines we have in place for all functions bound through conventional means in Sol will catch exceptions and turn them into Lua errors so that LuaJIT remainds unperturbed, but if you link up a C function directly yourself and throw, chances are you might have screwed the pooch.
Testing in `this closed issue<https://github.com/ThePhD/sol2/issues/28>` reveals that it doesn't play nice on 64-bit Linux in many cases either, especially when it hits an error internal to the interpreter (and does not go through Sol). We do have tests, however, that compile for our continuous integration check-ins that check this functionality across several compilers and platforms to keep you protected and given hard, strong guarantees for what happens if you throw in a function bound by Sol. If you stray outside the realm of Sol's protection, however... Good luck. Testing in `this closed issue`_ that it doesn't play nice on 64-bit Linux in many cases either, especially when it hits an error internal to the interpreter (and does not go through Sol). We do have tests, however, that compile for our continuous integration check-ins that check this functionality across several compilers and platforms to keep you protected and given hard, strong guarantees for what happens if you throw in a function bound by Sol. If you stray outside the realm of Sol's protection, however... Good luck.
.. _issue: https://github.com/ThePhD/sol2/issues/ .. _issue: https://github.com/ThePhD/sol2/issues/
.. _at_panic: http://www.Lua.org/manual/5.3/manual.html#4.6
.. _caveats regarding exceptions: http://luajit.org/extensions.html#exceptions
.. _this closed issue: https://github.com/ThePhD/sol2/issues/28

View File

@ -40,7 +40,7 @@ what Sol supports
``my_class a = table["a"];`` ``my_class a = table["a"];``
* Thread/Coroutine support * Thread/Coroutine support
- Use, resume, and play with :doc:`<coroutines<api/coroutine>` like regular functions - Use, resume, and play with :doc:`coroutines<api/coroutine>` like regular functions
- Get and use them even on a separate Lua :doc:`thread<api/thread>` - Get and use them even on a separate Lua :doc:`thread<api/thread>`
- Monitor status and get check errors - Monitor status and get check errors

View File

@ -3,7 +3,7 @@ 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...
--------------------------------------------------------- ---------------------------------------------------------
Not compiling with C++'s run-time type information? Do a ``#define SOL_NO_RTII`` before you include ``sol.hpp`` or define ``SOL_NO_RTTI`` on your command line. Be sure to understand the :doc:`implications<api/usertype>` of doing so if you also turn off exceptions. Not compiling with C++'s run-time type information? Do a ``#define SOL_NO_RTII`` before you include ``sol.hpp`` or define ``SOL_NO_RTTI`` on your command line. Be sure to understand the :ref:`implications<usertype-inheritance>` of doing so if you also turn off exceptions.
If you come across bugs or can't compile because there's a stray `typeid` or `typeinfo` that wasn't hidden behind a ``#ifndef SOL_NO_RTTI``, please file `an issue`_ or even make a pull request so it can be fixed for everyone. If you come across bugs or can't compile because there's a stray `typeid` or `typeinfo` that wasn't hidden behind a ``#ifndef SOL_NO_RTTI``, please file `an issue`_ or even make a pull request so it can be fixed for everyone.

View File

@ -56,14 +56,16 @@ inline std::string get_type_name(const std::type_info& id) {
template <typename T> template <typename T>
inline std::string get_type_name() { inline std::string get_type_name() {
std::string name = __FUNCSIG__; std::string name = __FUNCSIG__;
std::size_t start = name.find_last_of('<'); std::size_t start = name.find("get_type_name");
if (start == std::string::npos)
start = 0;
else
start += 13;
if (start < name.size() - 1)
start += 1;
std::size_t end = name.find_last_of('>'); std::size_t end = name.find_last_of('>');
if (end == std::string::npos) if (end == std::string::npos)
end = name.size(); end = name.size();
if (start == std::string::npos)
start = 0;
if (start < name.size() - 1)
start += 1;
name = name.substr(start, end - start); name = name.substr(start, end - start);
if (name.find("struct", 0) == 0) if (name.find("struct", 0) == 0)
name.replace(0, 6, "", 0); name.replace(0, 6, "", 0);

View File

@ -32,8 +32,12 @@
#include <memory> #include <memory>
namespace sol { namespace sol {
template <typename Sig, typename... Args> template <typename Sig, typename... Functions>
struct function_packer : std::tuple<Args...> { using std::tuple<Args...>::tuple; }; struct function_packer {
std::tuple<Functions...> set;
template <typename... Args>
function_packer(Args&&... args) : set(std::forward<Args>(args)...) {}
};
template <typename Sig, typename... Args> template <typename Sig, typename... Args>
function_packer<Sig, Args...> function_pack( Args&&... args ) { function_packer<Sig, Args...> function_pack( Args&&... args ) {
@ -153,7 +157,7 @@ struct pusher<function_sig<Sigs...>> {
static void set_isconvertible_fx(std::false_type, types<R(Args...)>, lua_State* L, Fx&& fx) { static void set_isconvertible_fx(std::false_type, types<R(Args...)>, lua_State* L, Fx&& fx) {
typedef meta::Unwrapped<std::decay_t<Fx>> fx_t; typedef meta::Unwrapped<std::decay_t<Fx>> fx_t;
std::unique_ptr<function_detail::base_function> sptr = std::make_unique<function_detail::functor_function<fx_t>>(std::forward<Fx>(fx)); std::unique_ptr<function_detail::base_function> sptr = std::make_unique<function_detail::functor_function<fx_t>>(std::forward<Fx>(fx));
set_fx<Fx>(L, std::move(sptr)); set_fx(L, std::move(sptr));
} }
template<typename Fx, typename T> template<typename Fx, typename T>
@ -165,7 +169,7 @@ struct pusher<function_sig<Sigs...>> {
static void set_reference_fx(std::false_type, lua_State* L, Fx&& fx, T&& obj) { static void set_reference_fx(std::false_type, lua_State* L, Fx&& fx, T&& obj) {
typedef std::remove_pointer_t<std::decay_t<Fx>> clean_fx; typedef std::remove_pointer_t<std::decay_t<Fx>> clean_fx;
std::unique_ptr<function_detail::base_function> sptr = std::make_unique<function_detail::member_function<clean_fx, meta::Unqualified<T>>>(std::forward<T>(obj), std::forward<Fx>(fx)); std::unique_ptr<function_detail::base_function> sptr = std::make_unique<function_detail::member_function<clean_fx, meta::Unqualified<T>>>(std::forward<T>(obj), std::forward<Fx>(fx));
return set_fx<Fx>(L, std::move(sptr)); return set_fx(L, std::move(sptr));
} }
template<typename Fx, typename T> template<typename Fx, typename T>
@ -197,24 +201,15 @@ struct pusher<function_sig<Sigs...>> {
stack::push(L, freefunc, upvalues); stack::push(L, freefunc, upvalues);
} }
template<typename Fx>
static void set_fx(lua_State* L, std::unique_ptr<function_detail::base_function> luafunc) { static void set_fx(lua_State* L, std::unique_ptr<function_detail::base_function> luafunc) {
const static auto& metakey = u8"sol.ƒ.♲.🗑.(/¯◡ ‿ ◡)/¯ ~ ┻━┻ (ノ◕ヮ◕)ノ*:・゚✧";
const static char* metatablename = &metakey[0];
function_detail::base_function* target = luafunc.release(); function_detail::base_function* target = luafunc.release();
void* userdata = reinterpret_cast<void*>(target); void* targetdata = reinterpret_cast<void*>(target);
lua_CFunction freefunc = function_detail::call; lua_CFunction freefunc = function_detail::call;
int metapushed = luaL_newmetatable(L, metatablename); stack::push(L, userdata_value(targetdata));
if(metapushed == 1) { function_detail::free_function_cleanup(L);
lua_pushstring(L, "__gc"); lua_setmetatable(L, -2);
stack::push(L, function_detail::gc); stack::push(L, freefunc, 1);
lua_settable(L, -3);
lua_pop(L, 1);
}
stack::stack_detail::push_userdata<void*>(L, metatablename, userdata);
stack::push(L, freefunc, 1);
} }
template<typename... Args> template<typename... Args>
@ -229,7 +224,7 @@ template<typename T, typename... Args>
struct pusher<function_packer<T, Args...>> { struct pusher<function_packer<T, Args...>> {
template <std::size_t... I, typename FP> template <std::size_t... I, typename FP>
static int push_func(std::index_sequence<I...>, lua_State* L, FP&& fp) { static int push_func(std::index_sequence<I...>, lua_State* L, FP&& fp) {
return stack::push<T>(L, std::get<I>(fp)...); return stack::push<T>(L, detail::forward_get<I>(fp.set)...);
} }
template <typename FP> template <typename FP>
@ -247,15 +242,14 @@ struct pusher<std::function<Signature>> {
template<typename... Functions> template<typename... Functions>
struct pusher<overload_set<Functions...>> { struct pusher<overload_set<Functions...>> {
template<std::size_t... I, typename Set> static int push(lua_State* L, overload_set<Functions...>&& set) {
static int push(std::index_sequence<I...>, lua_State* L, Set&& set) { pusher<function_sig<>>{}.set_fx(L, std::make_unique<function_detail::overloaded_function<Functions...>>(std::move(set.set)));
pusher<function_sig<>>{}.set_fx<Set>(L, std::make_unique<function_detail::overloaded_function<Functions...>>(std::get<I>(set)...));
return 1; return 1;
} }
template<typename Set> static int push(lua_State* L, const overload_set<Functions...>& set) {
static int push(lua_State* L, Set&& set) { pusher<function_sig<>>{}.set_fx(L, std::make_unique<function_detail::overloaded_function<Functions...>>(set.set));
return push(std::index_sequence_for<Functions...>(), L, std::forward<Set>(set)); return 1;
} }
}; };

View File

@ -75,9 +75,7 @@ inline int construct(lua_State* L) {
luaL_getmetatable(L, &meta[0]); luaL_getmetatable(L, &meta[0]);
if (stack::get<type>(L) == type::nil) { if (stack::get<type>(L) == type::nil) {
lua_pop(L, 1); lua_pop(L, 1);
std::string err = "sol: unable to get usertype metatable for "; return luaL_error(L, "sol: unable to get usertype metatable");
err += usertype_traits<T>::name;
return luaL_error(L, err.c_str());
} }
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
@ -98,10 +96,10 @@ struct usertype_constructor_function : base_function {
typedef std::index_sequence_for<Functions...> indices; typedef std::index_sequence_for<Functions...> indices;
overload_list overloads; overload_list overloads;
usertype_constructor_function(constructor_wrapper<Functions...> set) : usertype_constructor_function(indices(), set) {} usertype_constructor_function(overload_list set) : overloads(std::move(set)) {}
template <std::size_t... I> template <std::size_t... I>
usertype_constructor_function(std::index_sequence<I...>, constructor_wrapper<Functions...> set) : usertype_constructor_function(std::get<I>(set)...) {} usertype_constructor_function(std::index_sequence<I...>, constructor_wrapper<Functions...> set) : usertype_constructor_function(detail::forward_get<I>(set.set)...) {}
usertype_constructor_function(Functions... fxs) : overloads(fxs...) {} usertype_constructor_function(Functions... fxs) : overloads(fxs...) {}

View File

@ -1,4 +1,4 @@
// The MIT License (MIT) // The MIT License (MIT)
// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors // Copyright (c) 2013-2016 Rapptz, ThePhD and contributors
@ -40,6 +40,8 @@ struct implicit_wrapper {
} }
}; };
const static auto& cleanup_key = u8"sol.ƒ.♲.🗑.(/¯◡ ‿ ◡)/¯ ~ ┻━┻ (ノ◕ヮ◕)ノ*:・゚✧";
template<typename T, typename Func, typename = void> template<typename T, typename Func, typename = void>
struct functor { struct functor {
typedef meta::callable_traits<Func> traits_type; typedef meta::callable_traits<Func> traits_type;
@ -114,7 +116,7 @@ struct functor<T, Func, std::enable_if_t<std::is_function<Func>::value || std::i
typedef meta::pop_front_type_t<typename traits_type::args_type> args_type; typedef meta::pop_front_type_t<typename traits_type::args_type> args_type;
typedef typename traits_type::return_type return_type; typedef typename traits_type::return_type return_type;
static const std::size_t arity = traits_type::arity; static const std::size_t arity = traits_type::arity;
typedef std::tuple_element_t<0, typename traits_type::args_tuple_type> Arg0; typedef meta::tuple_element_t<0, typename traits_type::args_tuple_type> Arg0;
typedef std::conditional_t<std::is_pointer<Func>::value || std::is_class<Func>::value, Func, std::add_pointer_t<Func>> function_type; typedef std::conditional_t<std::is_pointer<Func>::value || std::is_class<Func>::value, Func, std::add_pointer_t<Func>> function_type;
static_assert(std::is_base_of<meta::Unqualified<std::remove_pointer_t<Arg0>>, T>::value, "Any non-member-function must have a first argument which is covariant with the desired userdata type."); static_assert(std::is_base_of<meta::Unqualified<std::remove_pointer_t<Arg0>>, T>::value, "Any non-member-function must have a first argument which is covariant with the desired userdata type.");
T* item; T* item;
@ -226,6 +228,14 @@ inline int usertype_gc(lua_State* L) {
func_gc<I>(meta::Bool<(I < 1)>(), L); func_gc<I>(meta::Bool<(I < 1)>(), L);
return 0; return 0;
} }
void free_function_cleanup(lua_State* L) {
const static char* metatablename = &cleanup_key[0];
int metapushed = luaL_newmetatable(L, metatablename);
if (metapushed == 1) {
stack::set_field(L, "__gc", function_detail::gc);
}
}
} // function_detail } // function_detail
} // sol } // sol

View File

@ -81,12 +81,8 @@ struct overloaded_function : base_function {
typedef std::index_sequence_for<Functions...> indices; typedef std::index_sequence_for<Functions...> indices;
overload_list overloads; overload_list overloads;
overloaded_function(overload_set<Functions...> set) overloaded_function(overload_list set)
: overloaded_function(indices(), set) {} : overloads(std::move(set)) {}
template <std::size_t... I>
overloaded_function(std::index_sequence<I...>, overload_set<Functions...> set)
: overloaded_function(std::get<I>(set)...) {}
overloaded_function(Functions... fxs) overloaded_function(Functions... fxs)
: overloads(fxs...) { : overloads(fxs...) {

View File

@ -26,8 +26,10 @@
namespace sol { namespace sol {
template <typename... Functions> template <typename... Functions>
struct overload_set : std::tuple<Functions...> { struct overload_set {
using std::tuple<Functions...>::tuple; std::tuple<Functions...> set;
template <typename... Args>
overload_set (Args&&... args) : set(std::forward<Args>(args)...) {}
}; };
template <typename... Args> template <typename... Args>

View File

@ -29,10 +29,28 @@ namespace sol {
namespace detail { namespace detail {
struct default_construct { struct default_construct {
template<typename T, typename... Args> template<typename T, typename... Args>
void operator()(T&& obj, Args&&... args) const { static void construct(T&& obj, Args&&... args) {
std::allocator<meta::Unqualified<T>> alloc{}; std::allocator<meta::Unqualified<T>> alloc{};
alloc.construct(obj, std::forward<Args>(args)...); alloc.construct(obj, std::forward<Args>(args)...);
} }
template<typename T, typename... Args>
void operator()(T&& obj, Args&&... args) const {
construct(std::forward<T>(obj), std::forward<Args>(args)...);
}
};
struct default_destruct {
template<typename T>
static void destroy(T&& obj) {
std::allocator<meta::Unqualified<T>> alloc{};
alloc.destroy(obj);
}
template<typename T>
void operator()(T&& obj) const {
destroy(std::forward<T>(obj));
}
}; };
} // detail } // detail
@ -45,8 +63,10 @@ using constructors = constructor_list<Args...>;
const auto default_constructor = constructors<types<>>{}; const auto default_constructor = constructors<types<>>{};
template <typename... Functions> template <typename... Functions>
struct constructor_wrapper : std::tuple<Functions...> { struct constructor_wrapper {
using std::tuple<Functions...>::tuple; std::tuple<Functions...> set;
template <typename... Args>
constructor_wrapper(Args&&... args) : set(std::forward<Args>(args)...) {}
}; };
template <typename... Functions> template <typename... Functions>

View File

@ -30,6 +30,7 @@
#include "usertype_traits.hpp" #include "usertype_traits.hpp"
#include "inheritance.hpp" #include "inheritance.hpp"
#include "overload.hpp" #include "overload.hpp"
#include "raii.hpp"
#include <utility> #include <utility>
#include <array> #include <array>
#include <cstring> #include <cstring>
@ -117,7 +118,28 @@ template <bool global = false, typename Key, typename Value>
void set_field(lua_State* L, Key&& key, Value&& value, int tableindex) { void set_field(lua_State* L, Key&& key, Value&& value, int tableindex) {
field_setter<meta::Unqualified<Key>, global>{}.set(L, std::forward<Key>(key), std::forward<Value>(value), tableindex); field_setter<meta::Unqualified<Key>, global>{}.set(L, std::forward<Key>(key), std::forward<Value>(value), tableindex);
} }
} // stack
namespace detail {
using special_destruct_func = void(*)(void*);
template <typename T, typename Real>
inline void special_destruct(void* memory) {
T** pointerpointer = static_cast<T**>(memory);
special_destruct_func* dx = static_cast<special_destruct_func*>(static_cast<void*>(pointerpointer + 1));
Real* target = static_cast<Real*>(static_cast<void*>(dx + 1));
target->~Real();
}
template <typename T>
inline int unique_destruct(lua_State* L) {
void* memory = stack::get<userdata_value>(L, 1);
T** pointerpointer = static_cast<T**>(memory);
special_destruct_func& dx = *static_cast<special_destruct_func*>( static_cast<void*>( pointerpointer + 1 ) );
(dx)(memory);
return 0;
}
} // detail
namespace stack {
namespace stack_detail { namespace stack_detail {
const bool default_check_arguments = const bool default_check_arguments =
#ifdef SOL_CHECK_ARGUMENTS #ifdef SOL_CHECK_ARGUMENTS
@ -140,84 +162,6 @@ static int push_upvalues(lua_State* L, TCont&& cont) {
} }
return n; return n;
} }
template <typename T>
struct userdata_pusher {
template <typename Key, typename... Args>
static void push (lua_State* L, Key&& metatablekey, Args&&... args) {
// Basically, we store all user-data like this:
// If it's a movable/copyable value (no std::ref(x)), then we store the pointer to the new
// data in the first sizeof(T*) bytes, and then however many bytes it takes to
// do the actual object. Things that are std::ref or plain T* are stored as
// just the sizeof(T*), and nothing else.
T** pdatum = static_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
T** referencepointer = pdatum;
T*& referencereference = *pdatum;
T* allocationtarget = reinterpret_cast<T*>(pdatum + 1);
referencereference = allocationtarget;
std::allocator<T> alloc{};
alloc.construct(allocationtarget, std::forward<Args>(args)...);
luaL_getmetatable(L, &metatablekey[0]);
lua_setmetatable(L, -2);
}
};
template <typename T>
struct userdata_pusher<T*> {
template <typename Key, typename... Args>
static void push (lua_State* L, Key&& metatablekey, Args&&... args) {
T** pdatum = static_cast<T**>(lua_newuserdata(L, sizeof(T*)));
std::allocator<T*> alloc{};
alloc.construct(pdatum, std::forward<Args>(args)...);
luaL_getmetatable(L, &metatablekey[0]);
lua_setmetatable(L, -2);
}
};
template<typename T, std::size_t... I>
inline int push_tuple(std::index_sequence<I...>, lua_State* L, T&& tuplen) {
int pushcount = 0;
void(detail::swallow{(pushcount += sol::stack::push(L, std::get<I>(tuplen)), '\0')... });
return pushcount;
}
template <typename T, typename Key, typename... Args>
inline int push_confirmed_userdata(lua_State* L, Key&& metatablekey, Args&&... args) {
userdata_pusher<T>{}.push(L, std::forward<Key>(metatablekey), std::forward<Args>(args)...);
return 1;
}
template <typename T, typename Key>
inline int push_userdata_pointer(lua_State* L, Key&& metatablekey) {
return push_confirmed_userdata<T>(L, std::forward<Key>(metatablekey));
}
template <typename T, typename Key, typename Arg, meta::EnableIf<std::is_same<T, meta::Unqualified<Arg>>> = 0>
inline int push_userdata_pointer(lua_State* L, Key&& metatablekey, Arg&& arg) {
if (arg == nullptr)
return push(L, nil);
return push_confirmed_userdata<T>(L, std::forward<Key>(metatablekey), std::forward<Arg>(arg));
}
template <typename T, typename Key, typename Arg, meta::DisableIf<std::is_same<T, meta::Unqualified<Arg>>> = 0>
inline int push_userdata_pointer(lua_State* L, Key&& metatablekey, Arg&& arg) {
return push_confirmed_userdata<T>(L, std::forward<Key>(metatablekey), std::forward<Arg>(arg));
}
template <typename T, typename Key, typename Arg0, typename Arg1, typename... Args>
inline int push_userdata_pointer(lua_State* L, Key&& metatablekey, Arg0&& arg0, Arg1&& arg1, Args&&... args) {
return push_confirmed_userdata<T>(L, std::forward<Key>(metatablekey), std::forward<Arg0>(arg0), std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}
template<typename T, typename Key, typename... Args, meta::DisableIf<std::is_pointer<T>> = 0>
inline int push_userdata(lua_State* L, Key&& metatablekey, Args&&... args) {
return push_confirmed_userdata<T>(L, std::forward<Key>(metatablekey), std::forward<Args>(args)...);
}
template<typename T, typename Key, typename... Args, meta::EnableIf<std::is_pointer<T>> = 0>
inline int push_userdata(lua_State* L, Key&& metatablekey, Args&&... args) {
return push_userdata_pointer<T>(L, std::forward<Key>(metatablekey), std::forward<Args>(args)...);
}
} // stack_detail } // stack_detail
template<typename T, typename> template<typename T, typename>
@ -323,6 +267,16 @@ struct getter<T*> {
} }
}; };
template<typename T, typename Real>
struct getter<unique_usertype<T, Real>> {
static Real& get(lua_State* L, int index = -1) {
T** pref = static_cast<T**>(lua_touserdata(L, index));
detail::special_destruct_func* fx = static_cast<detail::special_destruct_func*>(static_cast<void*>(pref + 1));
Real* mem = static_cast<Real*>(static_cast<void*>(fx + 1));
return *mem;
}
};
template<typename T> template<typename T>
struct getter<non_null<T*>> { struct getter<non_null<T*>> {
static T* get(lua_State* L, int index = -1) { static T* get(lua_State* L, int index = -1) {
@ -337,6 +291,20 @@ struct getter<T&> {
} }
}; };
template<typename T>
struct getter<std::shared_ptr<T>> {
static std::shared_ptr<T>& get(lua_State* L, int index = -1) {
return getter<unique_usertype<T, std::shared_ptr<T>>>::get(L, index);
}
};
template<typename T, typename D>
struct getter<std::unique_ptr<T, D>> {
static std::unique_ptr<T, D>& get(lua_State* L, int index = -1) {
return getter<unique_usertype<T, std::unique_ptr<T, D>>>::get(L, index);
}
};
template<typename T> template<typename T>
struct getter<std::reference_wrapper<T>> { struct getter<std::reference_wrapper<T>> {
static T& get(lua_State* L, int index = -1) { static T& get(lua_State* L, int index = -1) {
@ -455,8 +423,8 @@ struct checker<T, type::userdata, C> {
lua_pop(L, 2); lua_pop(L, 2);
return true; return true;
} }
} }
lua_pop(L, 1); lua_pop(L, 1);
#ifndef SOL_NO_EXCEPTIONS #ifndef SOL_NO_EXCEPTIONS
lua_getfield(L, -1, &detail::base_class_check_key[0]); lua_getfield(L, -1, &detail::base_class_check_key[0]);
void* basecastdata = stack::get<light_userdata_value>(L); void* basecastdata = stack::get<light_userdata_value>(L);
@ -483,7 +451,7 @@ struct checker<T, type::userdata, C> {
bool success = ic(detail::id_for<T>::value); bool success = ic(detail::id_for<T>::value);
#endif // No Runtime Type Information || Exceptions #endif // No Runtime Type Information || Exceptions
lua_pop(L, 2); lua_pop(L, 2);
if (!success) { if (!success) {
handler(L, index, type::userdata, indextype); handler(L, index, type::userdata, indextype);
return false; return false;
} }
@ -517,12 +485,81 @@ struct popper<std::tuple<Args...>> {
template<typename T, typename> template<typename T, typename>
struct pusher { struct pusher {
static int push(lua_State* L, T& t) { template <typename... Args>
return stack_detail::push_userdata<T>(L, usertype_traits<T>::metatable, t); static int push(lua_State* L, Args&&... args) {
// Basically, we store all user-data like this:
// If it's a movable/copyable value (no std::ref(x)), then we store the pointer to the new
// data in the first sizeof(T*) bytes, and then however many bytes it takes to
// do the actual object. Things that are std::ref or plain T* are stored as
// just the sizeof(T*), and nothing else.
T** pointerpointer = static_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
T*& referencereference = *pointerpointer;
T* allocationtarget = reinterpret_cast<T*>(pointerpointer + 1);
referencereference = allocationtarget;
std::allocator<T> alloc{};
alloc.construct(allocationtarget, std::forward<Args>(args)...);
luaL_getmetatable(L, &usertype_traits<T>::metatable[0]);
lua_setmetatable(L, -2);
return 1;
} }
};
static int push(lua_State* L, T&& t) { template<typename T>
return stack_detail::push_userdata<T>(L, usertype_traits<T>::metatable, std::move(t)); struct pusher<T*> {
static int push(lua_State* L, T* obj) {
if (obj == nullptr)
return stack::push(L, nil);
T** pref = static_cast<T**>(lua_newuserdata(L, sizeof(T*)));
*pref = obj;
luaL_getmetatable(L, &usertype_traits<T*>::metatable[0]);
lua_setmetatable(L, -2);
return 1;
}
};
template<typename T, typename Real>
struct pusher<unique_usertype<T, Real>> {
template <typename... Args>
static int push(lua_State* L, Args&&... args) {
T** pref = static_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(detail::special_destruct_func) + sizeof(Real)));
detail::special_destruct_func* fx = static_cast<detail::special_destruct_func*>(static_cast<void*>(pref + 1));
Real* mem = static_cast<Real*>(static_cast<void*>(fx + 1));
*fx = detail::special_destruct<T, Real>;
detail::default_construct::construct(mem, std::forward<Args>(args)...);
*pref = mem->get();
if (luaL_newmetatable(L, &usertype_traits<unique_usertype<T>>::metatable[0]) == 1) {
set_field(L, "__gc", detail::unique_destruct<T>);
}
lua_setmetatable(L, -2);
return 1;
}
};
template<typename T, typename D>
struct pusher<std::unique_ptr<T, D>> {
static int push(lua_State* L, std::unique_ptr<T, D> obj) {
typedef std::unique_ptr<T, D> ptr_t;
if (obj == nullptr)
return stack::push(L, nil);
return stack::push<unique_usertype<T, ptr_t>>(L, std::move(obj));
}
};
template<typename T>
struct pusher<std::shared_ptr<T>> {
template <typename S>
static int push(lua_State* L, S&& s) {
if (s == nullptr)
return stack::push(L, nil);
return stack::push<unique_usertype<T, std::shared_ptr<T>>>(L, std::forward<S>(s));
}
};
template<typename T>
struct pusher<std::reference_wrapper<T>> {
static int push(lua_State* L, const std::reference_wrapper<T>& t) {
return stack::push(L, std::addressof(detail::deref(t.get())));
} }
}; };
@ -586,20 +623,6 @@ struct pusher<T, std::enable_if_t<std::is_base_of<reference, T>::value>> {
} }
}; };
template<typename T>
struct pusher<T*> {
static int push(lua_State* L, T* obj) {
return stack_detail::push_userdata<T*>(L, usertype_traits<T*>::metatable, obj);
}
};
template<typename T>
struct pusher<std::reference_wrapper<T>> {
static int push(lua_State* L, const std::reference_wrapper<T>& t) {
return stack::push(L, std::addressof(t.get()));
}
};
template<> template<>
struct pusher<bool> { struct pusher<bool> {
static int push(lua_State* L, bool b) { static int push(lua_State* L, bool b) {
@ -691,9 +714,18 @@ struct pusher<std::string> {
template<typename... Args> template<typename... Args>
struct pusher<std::tuple<Args...>> { struct pusher<std::tuple<Args...>> {
template <typename Tuple> template <std::size_t... I, typename T>
static int push(lua_State* L, Tuple&& tuplen) { static int push(std::index_sequence<I...>, lua_State* L, T&& t) {
return stack_detail::push_tuple(std::index_sequence_for<Args...>(), L, std::forward<Tuple>(tuplen)); int pushcount = 0;
(void)detail::swallow{ 0, (pushcount += stack::push(L,
detail::forward_get<I>(t)
), 0)... };
return pushcount;
}
template <typename T>
static int push(lua_State* L, T&& t) {
return push(std::index_sequence_for<Args...>(), L, std::forward<T>(t));
} }
}; };
@ -708,18 +740,17 @@ struct field_getter {
template <typename... Args, bool b, typename C> template <typename... Args, bool b, typename C>
struct field_getter<std::tuple<Args...>, b, C> { struct field_getter<std::tuple<Args...>, b, C> {
template <std::size_t I0, std::size_t... I, typename Key> template <std::size_t... I, typename Keys>
void apply(std::index_sequence<I0, I...>, lua_State* L, Key&& key, int tableindex) { void apply(std::index_sequence<I...>, lua_State* L, Keys&& keys, int tableindex) {
get_field<b>(L, std::get<I0>(key), tableindex); void(detail::swallow{ (get_field<I < 1 && b>(L, detail::forward_get<I>(keys), tableindex), 0)... });
void(detail::swallow{ (get_field(L, std::get<I>(key)), 0)... });
reference saved(L, -1); reference saved(L, -1);
lua_pop(L, static_cast<int>(sizeof...(I) + 1)); lua_pop(L, static_cast<int>(sizeof...(I) + 1));
saved.push(); saved.push();
} }
template <typename Key> template <typename Keys>
void get(lua_State* L, Key&& key, int tableindex = -2) { void get(lua_State* L, Keys&& keys, int tableindex = -2) {
apply(std::index_sequence_for<Args...>(), L, std::forward<Key>(key), tableindex); apply(std::index_sequence_for<Args...>(), L, std::forward<Keys>(keys), tableindex);
} }
}; };

View File

@ -65,23 +65,26 @@ class table_core : public reference {
template<typename... Ret, std::size_t... I, typename Keys> template<typename... Ret, std::size_t... I, typename Keys>
auto tuple_get( types<Ret...>, std::index_sequence<I...>, Keys&& keys ) const auto tuple_get( types<Ret...>, std::index_sequence<I...>, Keys&& keys ) const
-> decltype(stack::pop<std::tuple<Ret...>>(nullptr)){ -> decltype(stack::pop<std::tuple<Ret...>>(nullptr)){
auto pp = stack::push_pop<is_global<decltype(std::get<I>(keys))...>::value>(*this); auto pp = stack::push_pop<is_global<meta::tuple_element_t<I, Keys>...>::value>(*this);
int tableindex = lua_gettop(lua_state()); int tableindex = lua_gettop(lua_state());
void(detail::swallow{ ( stack::get_field<top_level>(lua_state(), std::get<I>(keys), tableindex), 0)... }); void(detail::swallow{ ( stack::get_field<top_level>(lua_state(), detail::forward_get<I>(keys), tableindex), 0)... });
return stack::pop<std::tuple<Ret...>>( lua_state() ); return stack::pop<std::tuple<Ret...>>( lua_state() );
} }
template<typename Ret, std::size_t I, typename Keys> template<typename Ret, std::size_t I, typename Keys>
decltype(auto) tuple_get( types<Ret>, std::index_sequence<I>, Keys&& keys ) const { decltype(auto) tuple_get( types<Ret>, std::index_sequence<I>, Keys&& keys ) const {
auto pp = stack::push_pop<is_global<decltype(std::get<I>(keys))>::value>(*this); auto pp = stack::push_pop<is_global<meta::tuple_element_t<I, Keys>>::value>(*this);
stack::get_field<top_level>( lua_state( ), std::get<I>( keys ) ); stack::get_field<top_level>( lua_state( ), detail::forward_get<I>(keys));
return stack::pop<Ret>( lua_state( ) ); return stack::pop<Ret>( lua_state( ) );
} }
template<typename Pairs, std::size_t... I> template<typename Pairs, std::size_t... I>
void tuple_set( std::index_sequence<I...>, Pairs&& pairs ) { void tuple_set( std::index_sequence<I...>, Pairs&& pairs ) {
auto pp = stack::push_pop<is_global<decltype(std::get<I * 2>(pairs))...>::value>(*this); auto pp = stack::push_pop<is_global<decltype(detail::forward_get<I>(pairs))...>::value>(*this);
void(detail::swallow{ (stack::set_field<top_level>(lua_state(), std::get<I * 2>(pairs), std::get<I * 2 + 1>(pairs)), 0)... }); void(detail::swallow{ (stack::set_field<top_level>(lua_state(),
detail::forward_get<I * 2>(pairs),
detail::forward_get<I * 2 + 1>(pairs)
), 0)... });
} }
template <bool global, typename T, typename Key> template <bool global, typename T, typename Key>

View File

@ -120,6 +120,9 @@ using Unqualified = std::remove_cv_t<std::remove_reference_t<T>>;
template<typename T> template<typename T>
using Unwrapped = typename unwrapped<T>::type; using Unwrapped = typename unwrapped<T>::type;
template <std::size_t N, typename Tuple>
using tuple_element_t = std::tuple_element_t<N, Unqualified<Tuple>>;
template<typename V, typename... Vs> template<typename V, typename... Vs>
struct find_in_pack_v : Bool<false> { }; struct find_in_pack_v : Bool<false> { };
@ -240,7 +243,7 @@ struct fx_traits<R(T::*)(Args...), false> {
typedef R return_type; typedef R return_type;
typedef std::remove_pointer_t<free_function_pointer_type> signature_type; typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
template<std::size_t i> template<std::size_t i>
using arg = std::tuple_element_t<i, args_tuple_type>; using arg = meta::tuple_element_t<i, args_tuple_type>;
}; };
template<typename T, typename R, typename... Args> template<typename T, typename R, typename... Args>
@ -255,7 +258,7 @@ struct fx_traits<R(T::*)(Args...) const, false> {
typedef R return_type; typedef R return_type;
typedef std::remove_pointer_t<free_function_pointer_type> signature_type; typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
template<std::size_t i> template<std::size_t i>
using arg = std::tuple_element_t<i, args_tuple_type>; using arg = meta::tuple_element_t<i, args_tuple_type>;
}; };
template<typename R, typename... Args> template<typename R, typename... Args>
@ -270,7 +273,7 @@ struct fx_traits<R(Args...), false> {
typedef R return_type; typedef R return_type;
typedef std::remove_pointer_t<free_function_pointer_type> signature_type; typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
template<std::size_t i> template<std::size_t i>
using arg = std::tuple_element_t<i, args_tuple_type>; using arg = meta::tuple_element_t<i, args_tuple_type>;
}; };
template<typename R, typename... Args> template<typename R, typename... Args>
@ -285,7 +288,7 @@ struct fx_traits<R(*)(Args...), false> {
typedef R return_type; typedef R return_type;
typedef std::remove_pointer_t<free_function_pointer_type> signature_type; typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
template<std::size_t i> template<std::size_t i>
using arg = std::tuple_element_t<i, args_tuple_type>; using arg = meta::tuple_element_t<i, args_tuple_type>;
}; };
} // meta_detail } // meta_detail
@ -322,7 +325,7 @@ struct callable_traits<Signature, true> {
typedef R(*function_pointer_type)(Arg); typedef R(*function_pointer_type)(Arg);
typedef R(*free_function_pointer_type)(Arg); typedef R(*free_function_pointer_type)(Arg);
template<std::size_t i> template<std::size_t i>
using arg = std::tuple_element_t<i, args_tuple_type>; using arg = meta::tuple_element_t<i, args_tuple_type>;
}; };
} // meta_detail } // meta_detail
@ -382,6 +385,11 @@ decltype(auto) tuplefy(X&&... x ) {
} }
} // meta } // meta
namespace detail { namespace detail {
template <std::size_t I, typename Tuple>
decltype(auto) forward_get( Tuple&& tuple ) {
return std::forward<meta::tuple_element_t<I, Tuple>>(std::get<I>(tuple));
}
template<typename T> template<typename T>
auto unwrap(T&& item) -> decltype(std::forward<T>(item)) { auto unwrap(T&& item) -> decltype(std::forward<T>(item)) {
return std::forward<T>(item); return std::forward<T>(item);
@ -394,7 +402,7 @@ T& unwrap(std::reference_wrapper<T> arg) {
template<typename T> template<typename T>
decltype(auto) deref(T&& item) { decltype(auto) deref(T&& item) {
return item; return std::forward<T>(item);
} }
template<typename T> template<typename T>
@ -436,7 +444,7 @@ template<typename T>
inline T* ptr(T* val) { inline T* ptr(T* val) {
return val; return val;
} }
} // meta_detail } // detail
} // sol } // sol
#endif // SOL_TRAITS_HPP #endif // SOL_TRAITS_HPP

View File

@ -87,6 +87,9 @@ const nil_t nil {};
inline bool operator==(nil_t, nil_t) { return true; } inline bool operator==(nil_t, nil_t) { return true; }
inline bool operator!=(nil_t, nil_t) { return false; } inline bool operator!=(nil_t, nil_t) { return false; }
template <typename T, typename = void>
struct unique_usertype {};
template <typename T> template <typename T>
struct non_null {}; struct non_null {};

View File

@ -114,8 +114,14 @@ struct is_destructor<destructor_wrapper<Fx>> : std::true_type {};
template <typename... Args> template <typename... Args>
using has_destructor = meta::Or<is_destructor<meta::Unqualified<Args>>...>; using has_destructor = meta::Or<is_destructor<meta::Unqualified<Args>>...>;
template<typename T, bool refmeta, typename Funcs, typename FuncTable, typename MetaFuncTable> enum class stage {
inline void push_metatable(lua_State* L, bool needsindexfunction, Funcs&& funcs, FuncTable&& functable, MetaFuncTable&& metafunctable, void* baseclasscheck, void* baseclasscast) { normalmeta,
refmeta,
uniquemeta,
};
template<typename T, stage metastage>
inline void push_metatable(lua_State* L, bool needsindexfunction, std::vector<std::unique_ptr<function_detail::base_function>>& funcs, std::vector<luaL_Reg>& functable, std::vector<luaL_Reg>& metafunctable, void* baseclasscheck, void* baseclasscast) {
static const auto& gcname = meta_function_names[static_cast<int>(meta_function::garbage_collect)]; static const auto& gcname = meta_function_names[static_cast<int>(meta_function::garbage_collect)];
luaL_newmetatable(L, &usertype_traits<T>::metatable[0]); luaL_newmetatable(L, &usertype_traits<T>::metatable[0]);
int metatableindex = lua_gettop(L); int metatableindex = lua_gettop(L);
@ -132,18 +138,36 @@ inline void push_metatable(lua_State* L, bool needsindexfunction, Funcs&& funcs,
} }
// Metamethods directly on the metatable itself // Metamethods directly on the metatable itself
int metaup = stack::stack_detail::push_upvalues(L, funcs); int metaup = stack::stack_detail::push_upvalues(L, funcs);
if (refmeta && gcname == metafunctable[metafunctable.size()-2].name) { switch (metastage) {
// We can just "clip" out the __gc function, case stage::uniquemeta: {
// which we always put as the last entry in the meta function table. if (gcname != metafunctable.back().name) {
luaL_Reg& target = metafunctable[metafunctable.size() - 2]; metafunctable.push_back({ "__gc", nullptr });
luaL_Reg old = target; }
target = { nullptr, nullptr }; luaL_Reg& target = metafunctable.back();
luaL_setfuncs(L, metafunctable.data(), metaup); luaL_Reg old = target;
target = old; target.func = detail::unique_destruct<T>;
} metafunctable.push_back({nullptr, nullptr});
else { luaL_setfuncs(L, metafunctable.data(), metaup);
// Otherwise, just slap it in there. metafunctable.pop_back();
luaL_setfuncs(L, metafunctable.data(), metaup); target = old;
break; }
case stage::refmeta:
if (gcname == metafunctable.back().name) {
// We can just "clip" out the __gc function,
// which we always put as the last entry in the meta function table.
luaL_Reg& target = metafunctable.back();
luaL_Reg old = target;
target = { nullptr, nullptr };
luaL_setfuncs(L, metafunctable.data(), metaup);
target = old;
}
break;
case stage::normalmeta:
default:
metafunctable.push_back({nullptr, nullptr});
luaL_setfuncs(L, metafunctable.data(), metaup);
metafunctable.pop_back();
break;
} }
if (needsindexfunction) { if (needsindexfunction) {
// We don't need to do anything more // We don't need to do anything more
@ -155,7 +179,9 @@ inline void push_metatable(lua_State* L, bool needsindexfunction, Funcs&& funcs,
// gives us performance boost in calling them // gives us performance boost in calling them
lua_createtable(L, 0, static_cast<int>(functable.size())); lua_createtable(L, 0, static_cast<int>(functable.size()));
int up = stack::stack_detail::push_upvalues(L, funcs); int up = stack::stack_detail::push_upvalues(L, funcs);
functable.push_back({nullptr, nullptr});
luaL_setfuncs(L, functable.data(), up); luaL_setfuncs(L, functable.data(), up);
functable.pop_back();
lua_setfield(L, metatableindex, "__index"); lua_setfield(L, metatableindex, "__index");
return; return;
} }
@ -172,7 +198,7 @@ inline void set_global_deleter(lua_State* L, lua_CFunction cleanup, Functions&&
// gctable name by default has ♻ part of it // gctable name by default has ♻ part of it
lua_setglobal(L, &usertype_traits<T>::gc_table[0]); lua_setglobal(L, &usertype_traits<T>::gc_table[0]);
} }
} } // usertype_detail
template<typename T> template<typename T>
class usertype { class usertype {
@ -195,12 +221,12 @@ private:
template<typename... Functions> template<typename... Functions>
std::unique_ptr<function_detail::base_function> make_function(const std::string&, overload_set<Functions...> func) { std::unique_ptr<function_detail::base_function> make_function(const std::string&, overload_set<Functions...> func) {
return std::make_unique<function_detail::usertype_overloaded_function<T, Functions...>>(std::move(func)); return std::make_unique<function_detail::usertype_overloaded_function<T, Functions...>>(std::move(func.set));
} }
template<typename... Functions> template<typename... Functions>
std::unique_ptr<function_detail::base_function> make_function(const std::string&, constructor_wrapper<Functions...> func) { std::unique_ptr<function_detail::base_function> make_function(const std::string&, constructor_wrapper<Functions...> func) {
return std::make_unique<function_detail::usertype_constructor_function<T, Functions...>>(std::move(func)); return std::make_unique<function_detail::usertype_constructor_function<T, Functions...>>(std::move(func.set));
} }
template<typename Arg, typename... Args, typename Ret> template<typename Arg, typename... Args, typename Ret>
@ -234,7 +260,7 @@ private:
template<typename Fx> template<typename Fx>
std::unique_ptr<function_detail::base_function> make_function(const std::string&, Fx&& func) { std::unique_ptr<function_detail::base_function> make_function(const std::string&, Fx&& func) {
typedef meta::Unqualified<Fx> Fxu; typedef meta::Unqualified<Fx> Fxu;
typedef std::tuple_element_t<0, typename meta::function_traits<Fxu>::args_tuple_type> Arg0; typedef meta::tuple_element_t<0, typename meta::function_traits<Fxu>::args_tuple_type> Arg0;
typedef meta::Unqualified<std::remove_pointer_t<Arg0>> Argu; typedef meta::Unqualified<std::remove_pointer_t<Arg0>> Argu;
static_assert(std::is_base_of<Argu, T>::value, "Any non-member-function must have a first argument which is covariant with the desired usertype."); static_assert(std::is_base_of<Argu, T>::value, "Any non-member-function must have a first argument which is covariant with the desired usertype.");
typedef std::decay_t<Fxu> function_type; typedef std::decay_t<Fxu> function_type;
@ -397,8 +423,6 @@ private:
metafunctiontable.reserve(sizeof...(args)+3); metafunctiontable.reserve(sizeof...(args)+3);
build_function_tables<0>(std::forward<Args>(args)...); build_function_tables<0>(std::forward<Args>(args)...);
metafunctiontable.push_back({ nullptr, nullptr });
functiontable.push_back({ nullptr, nullptr });
} }
template<typename... Args> template<typename... Args>
@ -419,11 +443,13 @@ public:
int push(lua_State* L) { int push(lua_State* L) {
// push pointer tables first, // push pointer tables first,
usertype_detail::push_metatable<T*, true>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast); usertype_detail::push_metatable<T*, usertype_detail::stage::refmeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast);
lua_pop(L, 1);
usertype_detail::push_metatable<unique_usertype<T>, usertype_detail::stage::uniquemeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast);
lua_pop(L, 1); lua_pop(L, 1);
// but leave the regular T table on last // but leave the regular T table on last
// so it can be linked to a type for usage with `.new(...)` or `:new(...)` // so it can be linked to a type for usage with `.new(...)` or `:new(...)`
usertype_detail::push_metatable<T, false>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast); usertype_detail::push_metatable<T, usertype_detail::stage::normalmeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast);
// Make sure to drop a table in the global namespace to properly destroy the pushed functions // Make sure to drop a table in the global namespace to properly destroy the pushed functions
// at some later point in life // at some later point in life
usertype_detail::set_global_deleter<T>(L, functiongcfunc, functions); usertype_detail::set_global_deleter<T>(L, functiongcfunc, functions);

View File

@ -1181,6 +1181,31 @@ TEST_CASE("usertype/nonmember-functions", "let users set non-member functions th
REQUIRE((lua.get<giver>("t").a == 20)); REQUIRE((lua.get<giver>("t").a == 20));
} }
TEST_CASE("usertype/unique-shared-ptr", "manage the conversion and use of unique and shared pointers ('unique usertypes')") {
const int64_t unique_value = 0x7125679355635963;
auto uniqueint = std::make_unique<int64_t>(unique_value);
auto sharedint = std::make_shared<int64_t>(unique_value);
long preusecount = sharedint.use_count();
{ sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set("uniqueint", std::move(uniqueint));
lua.set("sharedint", sharedint);
std::unique_ptr<int64_t>& uniqueintref = lua["uniqueint"];
std::shared_ptr<int64_t>& sharedintref = lua["sharedint"];
int siusecount = sharedintref.use_count();
REQUIRE(uniqueintref != nullptr);
REQUIRE(sharedintref != nullptr);
REQUIRE(unique_value == *uniqueintref.get());
REQUIRE(unique_value == *sharedintref.get());
REQUIRE(siusecount == sharedint.use_count());
std::shared_ptr<int64_t> moreref = sharedint;
REQUIRE(unique_value == *moreref.get());
REQUIRE(moreref.use_count() == sharedint.use_count());
REQUIRE(moreref.use_count() == sharedintref.use_count());
}
REQUIRE(preusecount == sharedint.use_count());
}
TEST_CASE("regressions/one", "issue number 48") { TEST_CASE("regressions/one", "issue number 48") {
sol::state lua; sol::state lua;
lua.new_usertype<vars>("vars", lua.new_usertype<vars>("vars",
@ -1349,7 +1374,6 @@ TEST_CASE("functions/destructor-tests", "Show that proper copies / destruction h
} }
REQUIRE(created == 2); REQUIRE(created == 2);
REQUIRE(destroyed == 2); REQUIRE(destroyed == 2);
REQUIRE(created == destroyed);
} }
// things convertible to a static function should _never_ be forced to make copies // things convertible to a static function should _never_ be forced to make copies
@ -1406,7 +1430,6 @@ TEST_CASE("usertype/destructor-tests", "Show that proper copies / destruction ha
} }
REQUIRE(created == 4); REQUIRE(created == 4);
REQUIRE(destroyed == 4); REQUIRE(destroyed == 4);
REQUIRE(created == destroyed);
} }
TEST_CASE("functions/overloading", "Check if overloading works properly for regular set function syntax") { TEST_CASE("functions/overloading", "Check if overloading works properly for regular set function syntax") {