From c42c1bafe5f4425719f030d3b4416d6f54f3d062 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sun, 13 Mar 2016 08:30:14 -0400 Subject: [PATCH] Documentation fixes, new tests, unique/shared_ptr support. Closes #32 --- docs/source/api/error.rst | 2 +- docs/source/api/function.rst | 6 +- docs/source/api/object.rst | 6 +- docs/source/api/overload.rst | 4 +- docs/source/api/proxy.rst | 61 +++++--- docs/source/api/stack.rst | 11 +- docs/source/api/table.rst | 2 + docs/source/api/usertype.rst | 10 +- docs/source/exceptions.rst | 13 +- docs/source/features.rst | 2 +- docs/source/rtti.rst | 2 +- sol/demangle.hpp | 12 +- sol/function.hpp | 44 +++--- sol/function_types_allocator.hpp | 8 +- sol/function_types_core.hpp | 14 +- sol/function_types_overload.hpp | 8 +- sol/overload.hpp | 6 +- sol/raii.hpp | 26 +++- sol/stack.hpp | 249 +++++++++++++++++-------------- sol/table_core.hpp | 15 +- sol/traits.hpp | 22 ++- sol/types.hpp | 3 + sol/usertype.hpp | 70 ++++++--- tests.cpp | 27 +++- 24 files changed, 381 insertions(+), 242 deletions(-) diff --git a/docs/source/api/error.rst b/docs/source/api/error.rst index ed7e01a4..f46bc06e 100644 --- a/docs/source/api/error.rst +++ b/docs/source/api/error.rst @@ -10,6 +10,6 @@ the single exception type 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`. If you turn :doc:`off exceptions<../exceptions>`, the chances of you seeing this error are :doc:`nil`. +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`. If you turn :doc:`off exceptions<../exceptions>`, the chances of you seeing this error are :doc:`nil`. 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. \ No newline at end of file diff --git a/docs/source/api/function.rst b/docs/source/api/function.rst index d6c69d31..cb109a63 100644 --- a/docs/source/api/function.rst +++ b/docs/source/api/function.rst @@ -7,7 +7,7 @@ calling functions bound to Lua class function : public reference; -Function is a correct-assuming version of :doc:`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( ... )`` template type list, it generates a :ref:`function_result` class that gets implicitly converted to the requested return type. For example: +Function is a correct-assuming version of :doc:`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( ... )`` template type list, it generates a :ref:`function_result` class that gets implicitly converted to the requested return type. For example: .. code-block:: 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"]; double numwoof = woof(20); -The call ``woof(20)`` generates a :doc:`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. +The call ``woof(20)`` generates a :ref:`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:: - Do NOT save the return type of a :doc:`function_result` with ``auto``, as in ``auto numwoof = woof(20);``, and do NOT store it anywhere. Unlike its counterpart :doc:`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. + Do NOT save the return type of a :ref:`function_result` with ``auto``, as in ``auto numwoof = woof(20);``, and do NOT store it anywhere. Unlike its counterpart :ref:`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 :caption: function: call operator / function call diff --git a/docs/source/api/object.rst b/docs/source/api/object.rst index a6453b33..628e189b 100644 --- a/docs/source/api/object.rst +++ b/docs/source/api/object.rst @@ -8,7 +8,7 @@ general-purpose safety reference to an existing object 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`, and is used in :doc:`sol::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`, and is used in :ref:`sol::table's iterators`. members @@ -28,7 +28,7 @@ Performs a cast of the item this reference refers to into the type ``T`` and ret template bool is() const; -Performs a type check using the :doc:`sol::stack::check` api, after checking if the reference is valid. +Performs a type check using the :ref:`sol::stack::check` api, after checking if the reference is valid. non-members @@ -42,4 +42,4 @@ non-members bool operator!=(const object& lhs, const nil_t&); 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: \ No newline at end of file +These allow a person to compare an ``sol::object`` against :ref:`nil`, which essentially checks if an object references a non-nil value, like so: \ No newline at end of file diff --git a/docs/source/api/overload.rst b/docs/source/api/overload.rst index 012017d0..f824508e 100644 --- a/docs/source/api/overload.rst +++ b/docs/source/api/overload.rst @@ -3,7 +3,7 @@ overload 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}`. Its use is simple: whereever you can pass a function type to Lua, whether its on a :doc:`usertype` or if you are just setting any kind of function with ``set`` or ``set_function`` (for :doc:`table
` or :doc:`state(_view)`), 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` or if you are just setting any kind of function with ``set`` or ``set_function`` (for :doc:`table
` or :doc:`state(_view)`), simply wrap up the functions you wish to be considered for overload resolution on one function like so: .. code-block:: cpp @@ -85,3 +85,5 @@ The actual class produced by ``sol::overload`` is essentially a type-wrapper aro .. 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 diff --git a/docs/source/api/proxy.rst b/docs/source/api/proxy.rst index 157c17d1..ea62fbb2 100644 --- a/docs/source/api/proxy.rst +++ b/docs/source/api/proxy.rst @@ -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
` and the results of function calls on :doc:`sol::function` and :doc:`sol::protected_function`. +.. _function-result: + +function_result +--------------- + +``function_result`` is a temporary-only, intermediate-only implicit conversion worker for when :doc:`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 ----- @@ -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]; - ``proxy`` lazy evaluation: .. 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` and not a :doc:`primitive lua type` +.. code-block:: c++ + :caption: function: get a value + :name: regular-get + + template + 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++ :caption: functions: [overloaded] 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 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` +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` or :ref:`protected_function_result`. See :ref:`note` for caveats. .. code-block:: c++ :caption: function: set a callable + :name: regular-set-function template 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` or :ref:`protected_function_result`. + .. code-block:: c++ - :caption: function: get a value + :caption: function: set a value + :name: regular-set template - 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` or :ref:`protected_function_result`. .. _note 1: -Note: Function Objects and proxies +On Function Objects and proxies ---------------------------------- Consider the following: @@ -143,19 +173,4 @@ Consider the following: lua["object"] = doge{}; // bind constructed doge to "object" // 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` 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` 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=``. \ No newline at end of file +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` 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. \ No newline at end of file diff --git a/docs/source/api/stack.rst b/docs/source/api/stack.rst index 77c1e56f..9aa65c6e 100644 --- a/docs/source/api/stack.rst +++ b/docs/source/api/stack.rst @@ -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. -.. _getter: - .. code-block:: cpp :caption: struct: getter + :name: getter template 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` and anything as upvalues with :doc:`upvalue_index`, getting raw `lua_CFunction`_ s, and finally pulling out Lua functions into ``std::function``. It is also defined for anything that derives from :doc:`sol::reference`. -.. _pusher: - .. code-block:: cpp :caption: struct: pusher + :name: pusher template 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\` 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` and raw upvalues with :doc:`upvalue`, 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`. +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\` 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` and raw upvalues with :doc:`upvalue`, 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`. .. code-block:: cpp :caption: struct: checker + :name: checker template , typename = void> 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 \ No newline at end of file diff --git a/docs/source/api/table.rst b/docs/source/api/table.rst index 89d8876f..4f5d75ba 100644 --- a/docs/source/api/table.rst +++ b/docs/source/api/table.rst @@ -69,6 +69,7 @@ Sets a previously created usertype with the specified ``key`` into the table. No .. code-block:: cpp :caption: function: begin / end for iteration + :name: table-iterators table_iterator begin () 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 :caption: function: iteration with a function + :name: table-for-each template void for_each(Fx&& fx); diff --git a/docs/source/api/usertype.rst b/docs/source/api/usertype.rst index 8dd842da..9e1b0d63 100644 --- a/docs/source/api/usertype.rst +++ b/docs/source/api/usertype.rst @@ -131,7 +131,8 @@ members ------- .. code-block:: cpp - :caption: usertype constructor + :caption: function: usertype constructor + :name: usertype-constructor template usertype(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`` - 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`` - - 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`. + - 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`. overloading ----------- -Functions set here support overloading. See :doc:`here` for an example. +Functions set on a usertype support overloading. See :doc:`here` for an example. -.. _usertype_inheritance: +.. _usertype-inheritance: inheritance ----------- @@ -231,6 +232,7 @@ traits .. code-block:: cpp :caption: usertype_traits :linenos: + :name: usertype-traits template struct usertype_traits { diff --git a/docs/source/exceptions.rst b/docs/source/exceptions.rst index 7968bf7f..8ca56064 100644 --- a/docs/source/exceptions.rst +++ b/docs/source/exceptions.rst @@ -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` ). -If you turn this off, the default `at_panic` function :doc:`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` 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`'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 ----------- -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` 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` tag with the :doc:`bases\` arguments when you create the usertype. If you turn exceptions off and are also completely mad and turn off :doc:`run-time type information` 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` 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` tag with the :ref:`bases\` arguments when you create the usertype. If you turn exceptions off and are also completely mad and turn off :doc:`run-time type information` 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: @@ -23,8 +23,11 @@ Sol uses a nifty feature of exceptions to perform infinitely scaling, compiler-c LuaJIT and exceptions --------------------- -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. +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` 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/ \ No newline at end of file +.. _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 \ No newline at end of file diff --git a/docs/source/features.rst b/docs/source/features.rst index 41feb77c..7566911c 100644 --- a/docs/source/features.rst +++ b/docs/source/features.rst @@ -40,7 +40,7 @@ what Sol supports ``my_class a = table["a"];`` * Thread/Coroutine support - - Use, resume, and play with :doc:`` like regular functions + - Use, resume, and play with :doc:`coroutines` like regular functions - Get and use them even on a separate Lua :doc:`thread` - Monitor status and get check errors diff --git a/docs/source/rtti.rst b/docs/source/rtti.rst index a5b6e937..58c9d3de 100644 --- a/docs/source/rtti.rst +++ b/docs/source/rtti.rst @@ -3,7 +3,7 @@ run-time type information (rtti) 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` 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` 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. diff --git a/sol/demangle.hpp b/sol/demangle.hpp index 4e71c1c2..34cbc6d7 100644 --- a/sol/demangle.hpp +++ b/sol/demangle.hpp @@ -56,14 +56,16 @@ inline std::string get_type_name(const std::type_info& id) { template inline std::string get_type_name() { 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('>'); if (end == std::string::npos) end = name.size(); - if (start == std::string::npos) - start = 0; - if (start < name.size() - 1) - start += 1; name = name.substr(start, end - start); if (name.find("struct", 0) == 0) name.replace(0, 6, "", 0); diff --git a/sol/function.hpp b/sol/function.hpp index 7d2992f0..5ab8fc46 100644 --- a/sol/function.hpp +++ b/sol/function.hpp @@ -32,8 +32,12 @@ #include namespace sol { -template -struct function_packer : std::tuple { using std::tuple::tuple; }; +template +struct function_packer { + std::tuple set; + template + function_packer(Args&&... args) : set(std::forward(args)...) {} +}; template function_packer function_pack( Args&&... args ) { @@ -153,7 +157,7 @@ struct pusher> { static void set_isconvertible_fx(std::false_type, types, lua_State* L, Fx&& fx) { typedef meta::Unwrapped> fx_t; std::unique_ptr sptr = std::make_unique>(std::forward(fx)); - set_fx(L, std::move(sptr)); + set_fx(L, std::move(sptr)); } template @@ -165,7 +169,7 @@ struct pusher> { static void set_reference_fx(std::false_type, lua_State* L, Fx&& fx, T&& obj) { typedef std::remove_pointer_t> clean_fx; std::unique_ptr sptr = std::make_unique>>(std::forward(obj), std::forward(fx)); - return set_fx(L, std::move(sptr)); + return set_fx(L, std::move(sptr)); } template @@ -197,24 +201,15 @@ struct pusher> { stack::push(L, freefunc, upvalues); } - template static void set_fx(lua_State* L, std::unique_ptr luafunc) { - const static auto& metakey = u8"sol.ƒ.♲.🗑.(/¯◡ ‿ ◡)/¯ ~ ┻━┻ (ノ◕ヮ◕)ノ*:・゚✧"; - const static char* metatablename = &metakey[0]; function_detail::base_function* target = luafunc.release(); - void* userdata = reinterpret_cast(target); + void* targetdata = reinterpret_cast(target); lua_CFunction freefunc = function_detail::call; - - int metapushed = luaL_newmetatable(L, metatablename); - if(metapushed == 1) { - lua_pushstring(L, "__gc"); - stack::push(L, function_detail::gc); - lua_settable(L, -3); - lua_pop(L, 1); - } - stack::stack_detail::push_userdata(L, metatablename, userdata); - stack::push(L, freefunc, 1); + stack::push(L, userdata_value(targetdata)); + function_detail::free_function_cleanup(L); + lua_setmetatable(L, -2); + stack::push(L, freefunc, 1); } template @@ -229,7 +224,7 @@ template struct pusher> { template static int push_func(std::index_sequence, lua_State* L, FP&& fp) { - return stack::push(L, std::get(fp)...); + return stack::push(L, detail::forward_get(fp.set)...); } template @@ -247,15 +242,14 @@ struct pusher> { template struct pusher> { - template - static int push(std::index_sequence, lua_State* L, Set&& set) { - pusher>{}.set_fx(L, std::make_unique>(std::get(set)...)); + static int push(lua_State* L, overload_set&& set) { + pusher>{}.set_fx(L, std::make_unique>(std::move(set.set))); return 1; } - template - static int push(lua_State* L, Set&& set) { - return push(std::index_sequence_for(), L, std::forward(set)); + static int push(lua_State* L, const overload_set& set) { + pusher>{}.set_fx(L, std::make_unique>(set.set)); + return 1; } }; diff --git a/sol/function_types_allocator.hpp b/sol/function_types_allocator.hpp index ffda154d..066e61ef 100644 --- a/sol/function_types_allocator.hpp +++ b/sol/function_types_allocator.hpp @@ -75,9 +75,7 @@ inline int construct(lua_State* L) { luaL_getmetatable(L, &meta[0]); if (stack::get(L) == type::nil) { lua_pop(L, 1); - std::string err = "sol: unable to get usertype metatable for "; - err += usertype_traits::name; - return luaL_error(L, err.c_str()); + return luaL_error(L, "sol: unable to get usertype metatable"); } lua_setmetatable(L, -2); @@ -98,10 +96,10 @@ struct usertype_constructor_function : base_function { typedef std::index_sequence_for indices; overload_list overloads; - usertype_constructor_function(constructor_wrapper set) : usertype_constructor_function(indices(), set) {} + usertype_constructor_function(overload_list set) : overloads(std::move(set)) {} template - usertype_constructor_function(std::index_sequence, constructor_wrapper set) : usertype_constructor_function(std::get(set)...) {} + usertype_constructor_function(std::index_sequence, constructor_wrapper set) : usertype_constructor_function(detail::forward_get(set.set)...) {} usertype_constructor_function(Functions... fxs) : overloads(fxs...) {} diff --git a/sol/function_types_core.hpp b/sol/function_types_core.hpp index 5aec28d1..e19a1f09 100644 --- a/sol/function_types_core.hpp +++ b/sol/function_types_core.hpp @@ -1,4 +1,4 @@ -// The MIT License (MIT) +// The MIT License (MIT) // Copyright (c) 2013-2016 Rapptz, ThePhD and contributors @@ -40,6 +40,8 @@ struct implicit_wrapper { } }; +const static auto& cleanup_key = u8"sol.ƒ.♲.🗑.(/¯◡ ‿ ◡)/¯ ~ ┻━┻ (ノ◕ヮ◕)ノ*:・゚✧"; + template struct functor { typedef meta::callable_traits traits_type; @@ -114,7 +116,7 @@ struct functor::value || std::i typedef meta::pop_front_type_t args_type; typedef typename traits_type::return_type return_type; 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::value || std::is_class::value, Func, std::add_pointer_t> function_type; static_assert(std::is_base_of>, T>::value, "Any non-member-function must have a first argument which is covariant with the desired userdata type."); T* item; @@ -226,6 +228,14 @@ inline int usertype_gc(lua_State* L) { func_gc(meta::Bool<(I < 1)>(), L); 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 } // sol diff --git a/sol/function_types_overload.hpp b/sol/function_types_overload.hpp index c355cd9b..b457a70f 100644 --- a/sol/function_types_overload.hpp +++ b/sol/function_types_overload.hpp @@ -81,12 +81,8 @@ struct overloaded_function : base_function { typedef std::index_sequence_for indices; overload_list overloads; - overloaded_function(overload_set set) - : overloaded_function(indices(), set) {} - - template - overloaded_function(std::index_sequence, overload_set set) - : overloaded_function(std::get(set)...) {} + overloaded_function(overload_list set) + : overloads(std::move(set)) {} overloaded_function(Functions... fxs) : overloads(fxs...) { diff --git a/sol/overload.hpp b/sol/overload.hpp index 701c4d73..9027a8b7 100644 --- a/sol/overload.hpp +++ b/sol/overload.hpp @@ -26,8 +26,10 @@ namespace sol { template - struct overload_set : std::tuple { - using std::tuple::tuple; + struct overload_set { + std::tuple set; + template + overload_set (Args&&... args) : set(std::forward(args)...) {} }; template diff --git a/sol/raii.hpp b/sol/raii.hpp index 548c46c8..18822188 100644 --- a/sol/raii.hpp +++ b/sol/raii.hpp @@ -29,10 +29,28 @@ namespace sol { namespace detail { struct default_construct { template - void operator()(T&& obj, Args&&... args) const { + static void construct(T&& obj, Args&&... args) { std::allocator> alloc{}; alloc.construct(obj, std::forward(args)...); } + + template + void operator()(T&& obj, Args&&... args) const { + construct(std::forward(obj), std::forward(args)...); + } +}; + +struct default_destruct { + template + static void destroy(T&& obj) { + std::allocator> alloc{}; + alloc.destroy(obj); + } + + template + void operator()(T&& obj) const { + destroy(std::forward(obj)); + } }; } // detail @@ -45,8 +63,10 @@ using constructors = constructor_list; const auto default_constructor = constructors>{}; template -struct constructor_wrapper : std::tuple { - using std::tuple::tuple; +struct constructor_wrapper { + std::tuple set; + template + constructor_wrapper(Args&&... args) : set(std::forward(args)...) {} }; template diff --git a/sol/stack.hpp b/sol/stack.hpp index aaac2d3f..bdeeb852 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -30,6 +30,7 @@ #include "usertype_traits.hpp" #include "inheritance.hpp" #include "overload.hpp" +#include "raii.hpp" #include #include #include @@ -117,7 +118,28 @@ template void set_field(lua_State* L, Key&& key, Value&& value, int tableindex) { field_setter, global>{}.set(L, std::forward(key), std::forward(value), tableindex); } +} // stack +namespace detail { +using special_destruct_func = void(*)(void*); +template +inline void special_destruct(void* memory) { + T** pointerpointer = static_cast(memory); + special_destruct_func* dx = static_cast(static_cast(pointerpointer + 1)); + Real* target = static_cast(static_cast(dx + 1)); + target->~Real(); +} + +template +inline int unique_destruct(lua_State* L) { + void* memory = stack::get(L, 1); + T** pointerpointer = static_cast(memory); + special_destruct_func& dx = *static_cast( static_cast( pointerpointer + 1 ) ); + (dx)(memory); + return 0; +} +} // detail +namespace stack { namespace stack_detail { const bool default_check_arguments = #ifdef SOL_CHECK_ARGUMENTS @@ -140,84 +162,6 @@ static int push_upvalues(lua_State* L, TCont&& cont) { } return n; } - -template -struct userdata_pusher { - template - 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(lua_newuserdata(L, sizeof(T*) + sizeof(T))); - T** referencepointer = pdatum; - T*& referencereference = *pdatum; - T* allocationtarget = reinterpret_cast(pdatum + 1); - referencereference = allocationtarget; - std::allocator alloc{}; - alloc.construct(allocationtarget, std::forward(args)...); - luaL_getmetatable(L, &metatablekey[0]); - lua_setmetatable(L, -2); - } -}; - -template -struct userdata_pusher { - template - static void push (lua_State* L, Key&& metatablekey, Args&&... args) { - T** pdatum = static_cast(lua_newuserdata(L, sizeof(T*))); - std::allocator alloc{}; - alloc.construct(pdatum, std::forward(args)...); - luaL_getmetatable(L, &metatablekey[0]); - lua_setmetatable(L, -2); - } -}; - -template -inline int push_tuple(std::index_sequence, lua_State* L, T&& tuplen) { - int pushcount = 0; - void(detail::swallow{(pushcount += sol::stack::push(L, std::get(tuplen)), '\0')... }); - return pushcount; -} - -template -inline int push_confirmed_userdata(lua_State* L, Key&& metatablekey, Args&&... args) { - userdata_pusher{}.push(L, std::forward(metatablekey), std::forward(args)...); - return 1; -} - -template -inline int push_userdata_pointer(lua_State* L, Key&& metatablekey) { - return push_confirmed_userdata(L, std::forward(metatablekey)); -} - -template >> = 0> -inline int push_userdata_pointer(lua_State* L, Key&& metatablekey, Arg&& arg) { - if (arg == nullptr) - return push(L, nil); - return push_confirmed_userdata(L, std::forward(metatablekey), std::forward(arg)); -} - -template >> = 0> -inline int push_userdata_pointer(lua_State* L, Key&& metatablekey, Arg&& arg) { - return push_confirmed_userdata(L, std::forward(metatablekey), std::forward(arg)); -} - -template -inline int push_userdata_pointer(lua_State* L, Key&& metatablekey, Arg0&& arg0, Arg1&& arg1, Args&&... args) { - return push_confirmed_userdata(L, std::forward(metatablekey), std::forward(arg0), std::forward(arg1), std::forward(args)...); -} - -template> = 0> -inline int push_userdata(lua_State* L, Key&& metatablekey, Args&&... args) { - return push_confirmed_userdata(L, std::forward(metatablekey), std::forward(args)...); -} - -template> = 0> -inline int push_userdata(lua_State* L, Key&& metatablekey, Args&&... args) { - return push_userdata_pointer(L, std::forward(metatablekey), std::forward(args)...); -} } // stack_detail template @@ -323,6 +267,16 @@ struct getter { } }; +template +struct getter> { + static Real& get(lua_State* L, int index = -1) { + T** pref = static_cast(lua_touserdata(L, index)); + detail::special_destruct_func* fx = static_cast(static_cast(pref + 1)); + Real* mem = static_cast(static_cast(fx + 1)); + return *mem; + } +}; + template struct getter> { static T* get(lua_State* L, int index = -1) { @@ -337,6 +291,20 @@ struct getter { } }; +template +struct getter> { + static std::shared_ptr& get(lua_State* L, int index = -1) { + return getter>>::get(L, index); + } +}; + +template +struct getter> { + static std::unique_ptr& get(lua_State* L, int index = -1) { + return getter>>::get(L, index); + } +}; + template struct getter> { static T& get(lua_State* L, int index = -1) { @@ -455,8 +423,8 @@ struct checker { lua_pop(L, 2); return true; } - } - lua_pop(L, 1); + } + lua_pop(L, 1); #ifndef SOL_NO_EXCEPTIONS lua_getfield(L, -1, &detail::base_class_check_key[0]); void* basecastdata = stack::get(L); @@ -483,7 +451,7 @@ struct checker { bool success = ic(detail::id_for::value); #endif // No Runtime Type Information || Exceptions lua_pop(L, 2); - if (!success) { + if (!success) { handler(L, index, type::userdata, indextype); return false; } @@ -517,12 +485,81 @@ struct popper> { template struct pusher { - static int push(lua_State* L, T& t) { - return stack_detail::push_userdata(L, usertype_traits::metatable, t); + template + 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(lua_newuserdata(L, sizeof(T*) + sizeof(T))); + T*& referencereference = *pointerpointer; + T* allocationtarget = reinterpret_cast(pointerpointer + 1); + referencereference = allocationtarget; + std::allocator alloc{}; + alloc.construct(allocationtarget, std::forward(args)...); + luaL_getmetatable(L, &usertype_traits::metatable[0]); + lua_setmetatable(L, -2); + return 1; } +}; - static int push(lua_State* L, T&& t) { - return stack_detail::push_userdata(L, usertype_traits::metatable, std::move(t)); +template +struct pusher { + static int push(lua_State* L, T* obj) { + if (obj == nullptr) + return stack::push(L, nil); + T** pref = static_cast(lua_newuserdata(L, sizeof(T*))); + *pref = obj; + luaL_getmetatable(L, &usertype_traits::metatable[0]); + lua_setmetatable(L, -2); + return 1; + } +}; + +template +struct pusher> { + template + static int push(lua_State* L, Args&&... args) { + T** pref = static_cast(lua_newuserdata(L, sizeof(T*) + sizeof(detail::special_destruct_func) + sizeof(Real))); + detail::special_destruct_func* fx = static_cast(static_cast(pref + 1)); + Real* mem = static_cast(static_cast(fx + 1)); + *fx = detail::special_destruct; + detail::default_construct::construct(mem, std::forward(args)...); + *pref = mem->get(); + if (luaL_newmetatable(L, &usertype_traits>::metatable[0]) == 1) { + set_field(L, "__gc", detail::unique_destruct); + } + lua_setmetatable(L, -2); + return 1; + } +}; + +template +struct pusher> { + static int push(lua_State* L, std::unique_ptr obj) { + typedef std::unique_ptr ptr_t; + if (obj == nullptr) + return stack::push(L, nil); + return stack::push>(L, std::move(obj)); + } +}; + +template +struct pusher> { + template + static int push(lua_State* L, S&& s) { + if (s == nullptr) + return stack::push(L, nil); + return stack::push>>(L, std::forward(s)); + } +}; + + +template +struct pusher> { + static int push(lua_State* L, const std::reference_wrapper& t) { + return stack::push(L, std::addressof(detail::deref(t.get()))); } }; @@ -586,20 +623,6 @@ struct pusher::value>> { } }; -template -struct pusher { - static int push(lua_State* L, T* obj) { - return stack_detail::push_userdata(L, usertype_traits::metatable, obj); - } -}; - -template -struct pusher> { - static int push(lua_State* L, const std::reference_wrapper& t) { - return stack::push(L, std::addressof(t.get())); - } -}; - template<> struct pusher { static int push(lua_State* L, bool b) { @@ -691,9 +714,18 @@ struct pusher { template struct pusher> { - template - static int push(lua_State* L, Tuple&& tuplen) { - return stack_detail::push_tuple(std::index_sequence_for(), L, std::forward(tuplen)); + template + static int push(std::index_sequence, lua_State* L, T&& t) { + int pushcount = 0; + (void)detail::swallow{ 0, (pushcount += stack::push(L, + detail::forward_get(t) + ), 0)... }; + return pushcount; + } + + template + static int push(lua_State* L, T&& t) { + return push(std::index_sequence_for(), L, std::forward(t)); } }; @@ -708,18 +740,17 @@ struct field_getter { template struct field_getter, b, C> { - template - void apply(std::index_sequence, lua_State* L, Key&& key, int tableindex) { - get_field(L, std::get(key), tableindex); - void(detail::swallow{ (get_field(L, std::get(key)), 0)... }); + template + void apply(std::index_sequence, lua_State* L, Keys&& keys, int tableindex) { + void(detail::swallow{ (get_field(L, detail::forward_get(keys), tableindex), 0)... }); reference saved(L, -1); lua_pop(L, static_cast(sizeof...(I) + 1)); saved.push(); } - template - void get(lua_State* L, Key&& key, int tableindex = -2) { - apply(std::index_sequence_for(), L, std::forward(key), tableindex); + template + void get(lua_State* L, Keys&& keys, int tableindex = -2) { + apply(std::index_sequence_for(), L, std::forward(keys), tableindex); } }; diff --git a/sol/table_core.hpp b/sol/table_core.hpp index df8d46d5..98c2b5b8 100644 --- a/sol/table_core.hpp +++ b/sol/table_core.hpp @@ -65,23 +65,26 @@ class table_core : public reference { template auto tuple_get( types, std::index_sequence, Keys&& keys ) const -> decltype(stack::pop>(nullptr)){ - auto pp = stack::push_pop(keys))...>::value>(*this); + auto pp = stack::push_pop...>::value>(*this); int tableindex = lua_gettop(lua_state()); - void(detail::swallow{ ( stack::get_field(lua_state(), std::get(keys), tableindex), 0)... }); + void(detail::swallow{ ( stack::get_field(lua_state(), detail::forward_get(keys), tableindex), 0)... }); return stack::pop>( lua_state() ); } template decltype(auto) tuple_get( types, std::index_sequence, Keys&& keys ) const { - auto pp = stack::push_pop(keys))>::value>(*this); - stack::get_field( lua_state( ), std::get( keys ) ); + auto pp = stack::push_pop>::value>(*this); + stack::get_field( lua_state( ), detail::forward_get(keys)); return stack::pop( lua_state( ) ); } template void tuple_set( std::index_sequence, Pairs&& pairs ) { - auto pp = stack::push_pop(pairs))...>::value>(*this); - void(detail::swallow{ (stack::set_field(lua_state(), std::get(pairs), std::get(pairs)), 0)... }); + auto pp = stack::push_pop(pairs))...>::value>(*this); + void(detail::swallow{ (stack::set_field(lua_state(), + detail::forward_get(pairs), + detail::forward_get(pairs) + ), 0)... }); } template diff --git a/sol/traits.hpp b/sol/traits.hpp index 548eb7ad..1f1e8db3 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -120,6 +120,9 @@ using Unqualified = std::remove_cv_t>; template using Unwrapped = typename unwrapped::type; +template +using tuple_element_t = std::tuple_element_t>; + template struct find_in_pack_v : Bool { }; @@ -240,7 +243,7 @@ struct fx_traits { typedef R return_type; typedef std::remove_pointer_t signature_type; template - using arg = std::tuple_element_t; + using arg = meta::tuple_element_t; }; template @@ -255,7 +258,7 @@ struct fx_traits { typedef R return_type; typedef std::remove_pointer_t signature_type; template - using arg = std::tuple_element_t; + using arg = meta::tuple_element_t; }; template @@ -270,7 +273,7 @@ struct fx_traits { typedef R return_type; typedef std::remove_pointer_t signature_type; template - using arg = std::tuple_element_t; + using arg = meta::tuple_element_t; }; template @@ -285,7 +288,7 @@ struct fx_traits { typedef R return_type; typedef std::remove_pointer_t signature_type; template - using arg = std::tuple_element_t; + using arg = meta::tuple_element_t; }; } // meta_detail @@ -322,7 +325,7 @@ struct callable_traits { typedef R(*function_pointer_type)(Arg); typedef R(*free_function_pointer_type)(Arg); template - using arg = std::tuple_element_t; + using arg = meta::tuple_element_t; }; } // meta_detail @@ -382,6 +385,11 @@ decltype(auto) tuplefy(X&&... x ) { } } // meta namespace detail { +template +decltype(auto) forward_get( Tuple&& tuple ) { + return std::forward>(std::get(tuple)); +} + template auto unwrap(T&& item) -> decltype(std::forward(item)) { return std::forward(item); @@ -394,7 +402,7 @@ T& unwrap(std::reference_wrapper arg) { template decltype(auto) deref(T&& item) { - return item; + return std::forward(item); } template @@ -436,7 +444,7 @@ template inline T* ptr(T* val) { return val; } -} // meta_detail +} // detail } // sol #endif // SOL_TRAITS_HPP diff --git a/sol/types.hpp b/sol/types.hpp index d9d9fc53..48bf7d6e 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -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 false; } +template +struct unique_usertype {}; + template struct non_null {}; diff --git a/sol/usertype.hpp b/sol/usertype.hpp index 8a988512..cf74c698 100644 --- a/sol/usertype.hpp +++ b/sol/usertype.hpp @@ -114,8 +114,14 @@ struct is_destructor> : std::true_type {}; template using has_destructor = meta::Or>...>; -template -inline void push_metatable(lua_State* L, bool needsindexfunction, Funcs&& funcs, FuncTable&& functable, MetaFuncTable&& metafunctable, void* baseclasscheck, void* baseclasscast) { +enum class stage { + normalmeta, + refmeta, + uniquemeta, +}; + +template +inline void push_metatable(lua_State* L, bool needsindexfunction, std::vector>& funcs, std::vector& functable, std::vector& metafunctable, void* baseclasscheck, void* baseclasscast) { static const auto& gcname = meta_function_names[static_cast(meta_function::garbage_collect)]; luaL_newmetatable(L, &usertype_traits::metatable[0]); 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 int metaup = stack::stack_detail::push_upvalues(L, funcs); - if (refmeta && gcname == metafunctable[metafunctable.size()-2].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[metafunctable.size() - 2]; - luaL_Reg old = target; - target = { nullptr, nullptr }; - luaL_setfuncs(L, metafunctable.data(), metaup); - target = old; - } - else { - // Otherwise, just slap it in there. - luaL_setfuncs(L, metafunctable.data(), metaup); + switch (metastage) { + case stage::uniquemeta: { + if (gcname != metafunctable.back().name) { + metafunctable.push_back({ "__gc", nullptr }); + } + luaL_Reg& target = metafunctable.back(); + luaL_Reg old = target; + target.func = detail::unique_destruct; + metafunctable.push_back({nullptr, nullptr}); + luaL_setfuncs(L, metafunctable.data(), metaup); + metafunctable.pop_back(); + 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) { // 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 lua_createtable(L, 0, static_cast(functable.size())); int up = stack::stack_detail::push_upvalues(L, funcs); + functable.push_back({nullptr, nullptr}); luaL_setfuncs(L, functable.data(), up); + functable.pop_back(); lua_setfield(L, metatableindex, "__index"); 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 lua_setglobal(L, &usertype_traits::gc_table[0]); } -} +} // usertype_detail template class usertype { @@ -195,12 +221,12 @@ private: template std::unique_ptr make_function(const std::string&, overload_set func) { - return std::make_unique>(std::move(func)); + return std::make_unique>(std::move(func.set)); } template std::unique_ptr make_function(const std::string&, constructor_wrapper func) { - return std::make_unique>(std::move(func)); + return std::make_unique>(std::move(func.set)); } template @@ -234,7 +260,7 @@ private: template std::unique_ptr make_function(const std::string&, Fx&& func) { typedef meta::Unqualified Fxu; - typedef std::tuple_element_t<0, typename meta::function_traits::args_tuple_type> Arg0; + typedef meta::tuple_element_t<0, typename meta::function_traits::args_tuple_type> Arg0; typedef meta::Unqualified> Argu; static_assert(std::is_base_of::value, "Any non-member-function must have a first argument which is covariant with the desired usertype."); typedef std::decay_t function_type; @@ -397,8 +423,6 @@ private: metafunctiontable.reserve(sizeof...(args)+3); build_function_tables<0>(std::forward(args)...); - metafunctiontable.push_back({ nullptr, nullptr }); - functiontable.push_back({ nullptr, nullptr }); } template @@ -419,11 +443,13 @@ public: int push(lua_State* L) { // push pointer tables first, - usertype_detail::push_metatable(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast); + usertype_detail::push_metatable(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast); + lua_pop(L, 1); + usertype_detail::push_metatable, usertype_detail::stage::uniquemeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast); lua_pop(L, 1); // but leave the regular T table on last // so it can be linked to a type for usage with `.new(...)` or `:new(...)` - usertype_detail::push_metatable(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast); + usertype_detail::push_metatable(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast); // Make sure to drop a table in the global namespace to properly destroy the pushed functions // at some later point in life usertype_detail::set_global_deleter(L, functiongcfunc, functions); diff --git a/tests.cpp b/tests.cpp index 73a810ad..024b6ddf 100644 --- a/tests.cpp +++ b/tests.cpp @@ -1181,6 +1181,31 @@ TEST_CASE("usertype/nonmember-functions", "let users set non-member functions th REQUIRE((lua.get("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(unique_value); + auto sharedint = std::make_shared(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& uniqueintref = lua["uniqueint"]; + std::shared_ptr& 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 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") { sol::state lua; lua.new_usertype("vars", @@ -1349,7 +1374,6 @@ TEST_CASE("functions/destructor-tests", "Show that proper copies / destruction h } REQUIRE(created == 2); REQUIRE(destroyed == 2); - REQUIRE(created == destroyed); } // 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(destroyed == 4); - REQUIRE(created == destroyed); } TEST_CASE("functions/overloading", "Check if overloading works properly for regular set function syntax") {