mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
Time to break everything.
Added the ability to extend all usertypes at runtime. The performance implications need to be examined closely. variadic_args documentation was updated with the desired example demonstrating proper usage usertype examples were updated demonstrating Lua runtime and C++ runtime updating of a usertype table SOL_SAFE_FUNCTIONS is now part of the definitions and defined (thanks @eliasdaler)
This commit is contained in:
parent
ab9126d892
commit
dce8053248
|
@ -4,9 +4,11 @@ structures and classes from C++ made available to Lua code (simpler)
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
This type is no different from :doc:`regular usertype<usertype>`, but allows much of its work to be done at runtime instead of compile-time. You can reduce compilation times from a plain `usertype` when you have an exceedingly bulky registration listing.
|
This type is no different from :doc:`regular usertype<usertype>`, but allows much of its work to be done at runtime instead of compile-time. You can reduce compilation times from a plain ``usertype`` when you have an exceedingly bulky registration listing.
|
||||||
|
|
||||||
You can set functions incrementally to reduce compile-time burden with ``simple_usertype`` as well, as shown in `this example`_. This means both adding incrementally during registration, and afterwards by adding items to the metatable at runtime.
|
You can set functions incrementally to reduce compile-time burden with ``simple_usertype`` as well, as shown in `this example`_. This means both adding incrementally during registration.
|
||||||
|
|
||||||
|
You can add functions to both regular and simple usertypes afterwards by adding items to the metatable directly at runtime (e.g., with :doc:`metatable_key<metatable_key>` or by accessing the named metatable yourself).
|
||||||
|
|
||||||
Some developers used ``simple_usertype`` in older versions to have variables automatically be functions. To achieve this behavior, wrap the desired variable into :doc:`sol::as_function<as_function>`.
|
Some developers used ``simple_usertype`` in older versions to have variables automatically be functions. To achieve this behavior, wrap the desired variable into :doc:`sol::as_function<as_function>`.
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ structures and classes from C++ made available to Lua code
|
||||||
|
|
||||||
*Note: ``T`` refers to the type being turned into a usertype.*
|
*Note: ``T`` refers to the type being turned into a usertype.*
|
||||||
|
|
||||||
While other frameworks extend lua's syntax or create Data Structure Languages (DSLs) to create classes in lua, :doc:`Sol<../index>` instead offers the ability to generate easy bindings. These use metatables and userdata in lua for their implementation. If you need a usertype that is also extensible at runtime and has less compiler crunch to it, try the :doc:`simple version of this after reading these docs<simple_usertype>` Given this C++ class:
|
While other frameworks extend lua's syntax or create Data Structure Languages (DSLs) to create classes in Lua, :doc:`Sol<../index>` instead offers the ability to generate easy bindings. These use metatables and userdata in Lua for their implementation. Usertypes are also `runtime extensible`_. If you need a usertype that has less compiler crunch-time to it, try the :doc:`simple version of this after reading these docs<simple_usertype>` Given this C++ class:
|
||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
:linenos:
|
:linenos:
|
||||||
|
@ -327,4 +327,5 @@ performance note
|
||||||
|
|
||||||
|
|
||||||
.. _destructible: http://en.cppreference.com/w/cpp/types/is_destructible
|
.. _destructible: http://en.cppreference.com/w/cpp/types/is_destructible
|
||||||
.. _default_constructible: http://en.cppreference.com/w/cpp/types/is_constructible
|
.. _default_constructible: http://en.cppreference.com/w/cpp/types/is_constructible
|
||||||
|
.. _runtime extensible: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_advanced.cpp#L81
|
|
@ -47,3 +47,41 @@ This class is meant to represent every single argument at its current index and
|
||||||
lua.script("print(x2)"); // 600
|
lua.script("print(x2)"); // 600
|
||||||
lua.script("print(x3)"); // 21
|
lua.script("print(x3)"); // 21
|
||||||
}
|
}
|
||||||
|
|
||||||
|
You can also "save" arguments and the like later, by stuffing them into a ``std::vector<sol::object>`` or something similar that pulls out all the arguments. Below is an example of saving all of the arguments provided by ``sol::variadic_args`` in a lambda capture variable called ``args``.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
:linenos:
|
||||||
|
|
||||||
|
#include "sol.hpp"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
std::function<void()> function_storage;
|
||||||
|
|
||||||
|
void store_routine(const sol::function& f, const sol::variadic_args& va) {
|
||||||
|
function_storage = [=, args = std::vector<sol::object>(va.begin(), va.end())]() {
|
||||||
|
f(sol::as_args(args));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
sol::state lua;
|
||||||
|
lua.open_libraries(sol::lib::base);
|
||||||
|
lua.set_function("store_routine", &store_routine);
|
||||||
|
lua.script(R"(
|
||||||
|
function a(name)
|
||||||
|
print(name)
|
||||||
|
end
|
||||||
|
store_routine(a, "some name")
|
||||||
|
)");
|
||||||
|
function_storage();
|
||||||
|
|
||||||
|
lua.script(R"(
|
||||||
|
function b(number, text)
|
||||||
|
print(number, "of", text)
|
||||||
|
end
|
||||||
|
store_routine(b, 20, "these apples")
|
||||||
|
)");
|
||||||
|
function_storage();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,14 +9,19 @@ config
|
||||||
Note that you can obtain safety with regards to functions you bind by using the :doc:`protect<api/protect>` wrapper around function/variable bindings you set into Lua. Additionally, you can have basic boolean checks when using the API by just converting to a :doc:`sol::optional\<T><api/optional>` when necessary for getting things out of Lua and for function arguments.
|
Note that you can obtain safety with regards to functions you bind by using the :doc:`protect<api/protect>` wrapper around function/variable bindings you set into Lua. Additionally, you can have basic boolean checks when using the API by just converting to a :doc:`sol::optional\<T><api/optional>` when necessary for getting things out of Lua and for function arguments.
|
||||||
|
|
||||||
``SOL_SAFE_USERTYPE`` triggers the following change:
|
``SOL_SAFE_USERTYPE`` triggers the following change:
|
||||||
* If the userdata to a usertype function is nil, will trigger an error instead of letting things go through and letting the system segfault.
|
* If the userdata to a usertype function is nil, will trigger an error instead of letting things go through and letting the system segfault/crash.
|
||||||
|
* Turned on by default with clang++, g++ and VC++ if a basic check for building in debug mode is detected
|
||||||
|
|
||||||
|
``SOL_SAFE_FUNCTION`` triggers the following change:
|
||||||
|
* All uses of ``sol::function`` and ``sol::stack_function`` will default to ``sol::protected_function`` and ``sol::stack_protected_function``, respectively.
|
||||||
|
* Not turned on by default under any detectible compiler settings
|
||||||
|
|
||||||
``SOL_CHECK_ARGUMENTS`` triggers the following changes:
|
``SOL_CHECK_ARGUMENTS`` triggers the following changes:
|
||||||
* ``sol::stack::get`` (used everywhere) defaults to using ``sol::stack::check_get`` and dereferencing the argument. It uses ``sol::type_panic`` as the handler if something goes wrong.
|
* ``sol::stack::get`` (used everywhere) defaults to using ``sol::stack::check_get`` and dereferencing the argument. It uses ``sol::type_panic`` as the handler if something goes wrong.
|
||||||
* ``sol::stack::call`` and its variants will, if no templated boolean is specified, check all of the arguments for a function call.
|
* ``sol::stack::call`` and its variants will, if no templated boolean is specified, check all of the arguments for a function call.
|
||||||
* If ``SOL_SAFE_USERTYPE`` is not defined, it gets defined to turn being on.
|
* If ``SOL_SAFE_USERTYPE`` is not defined, it gets defined to turn being on and the effects described above kick in
|
||||||
|
|
||||||
Tests are compiled with this on to ensure everything is going as expected. Remember that if you want these features, you must explicitly turn them on.
|
Tests are compiled with this on to ensure everything is going as expected. Remember that if you want these features, you must explicitly turn them on all of them to be sure you are getting them.
|
||||||
|
|
||||||
Finally, some warnings that may help with errors when working with Sol:
|
Finally, some warnings that may help with errors when working with Sol:
|
||||||
|
|
||||||
|
@ -26,7 +31,7 @@ functions
|
||||||
|
|
||||||
The *vast majority* of all users are going to want to work with :doc:`sol::safe_function/sol::protected_function<api/protected_function>`. This version allows for error checking, prunes results, and responds to the defines listed above by throwing errors if you try to use the result of a function without checking. :doc:`sol::function/sol::unsafe_function<api/function>` is unsafe. It assumes that its contents run correctly and throw no errors, which can result in crashes that are hard to debug while offering a very tiny performance boost for not checking error codes or catching exceptions.
|
The *vast majority* of all users are going to want to work with :doc:`sol::safe_function/sol::protected_function<api/protected_function>`. This version allows for error checking, prunes results, and responds to the defines listed above by throwing errors if you try to use the result of a function without checking. :doc:`sol::function/sol::unsafe_function<api/function>` is unsafe. It assumes that its contents run correctly and throw no errors, which can result in crashes that are hard to debug while offering a very tiny performance boost for not checking error codes or catching exceptions.
|
||||||
|
|
||||||
If you find yourself crashing inside of ``sol::function``, try changing it to a ``sol::protected_function`` and seeing if the error codes and such help you find out what's going on. You can read more about the API on :doc:`the page itself<api/protected_function>`.
|
If you find yourself crashing inside of ``sol::function``, try changing it to a ``sol::protected_function`` and seeing if the error codes and such help you find out what's going on. You can read more about the API on :doc:`the page itself<api/protected_function>`. You can also define ``SOL_SAFE_FUNCTION`` as described above, but be warned that the ``protected_function`` API is a superset of the regular default ``function`` API: trying to revert back after defining ``SOL_SAFE_FUNCTION`` may result in some compiler errors if you use things beyond the basic, shared interface of the two types.
|
||||||
|
|
||||||
As a side note, binding functions with default parameters does not magically bind multiple versions of the function to be called with the default parameters. You must instead use :doc:`sol::overload<api/overload>`.
|
As a side note, binding functions with default parameters does not magically bind multiple versions of the function to be called with the default parameters. You must instead use :doc:`sol::overload<api/overload>`.
|
||||||
|
|
||||||
|
|
|
@ -12,18 +12,24 @@ To learn more about usertypes, visit:
|
||||||
|
|
||||||
The examples folder also has a number of really great examples for you to see. There are also some notes about guarantees you can find about usertypes, and their associated userdata, below:
|
The examples folder also has a number of really great examples for you to see. There are also some notes about guarantees you can find about usertypes, and their associated userdata, below:
|
||||||
|
|
||||||
|
* All usertypes are runtime extensible in both `Lua`_ and `C++`_
|
||||||
* You can push types classified as userdata before you register a usertype.
|
* You can push types classified as userdata before you register a usertype.
|
||||||
- You can register a usertype with the Lua runtime at any time sol2
|
- You can register a usertype with the Lua runtime at any time sol2
|
||||||
- You can retrieve them from the Lua runtime as well through sol2
|
- You can retrieve them from the Lua runtime as well through sol2
|
||||||
- Methods and properties will be added to the type only after you register it in the Lua runtime
|
- Methods and properties will be added to the type only after you register the usertype with the Lua runtime
|
||||||
|
- All methods and properties will appear on all userdata, even if that object was pushed before the usertype (all userdata will be updated)
|
||||||
* Types either copy once or move once into the memory location, if it is a value type. If it is a pointer, we store only the reference.
|
* Types either copy once or move once into the memory location, if it is a value type. If it is a pointer, we store only the reference.
|
||||||
- This means take arguments of class types (not primitive types like strings or integers) by ``T&`` or ``T*`` to modify the data in Lua directly, or by plain ``T`` to get a copy
|
- This means retrieval of class types (not primitive types like strings or integers) by ``T&`` or ``T*`` allow you to modify the data in Lua directly.
|
||||||
- Return types and passing arguments to ``sol::function`` use perfect forwarding and reference semantics, which means no copies happen unless you specify a value explicitly. See :ref:`this note for details<function-argument-handling>`.
|
- Retrieve a plain ``T`` to get a copy
|
||||||
* The first ``sizeof( void* )`` bytes is always a pointer to the typed C++ memory. What comes after is based on what you've pushed into the system according to :doc:`the memory specification for usertypes<api/usertype_memory>`. This is compatible with a number of systems.
|
- Return types and passing arguments to ``sol::function``-types use perfect forwarding and reference semantics, which means no copies happen unless you specify a value explicitly. See :ref:`this note for details<function-argument-handling>`.
|
||||||
|
* The first ``sizeof( void* )`` bytes is always a pointer to the typed C++ memory. What comes after is based on what you've pushed into the system according to :doc:`the memory specification for usertypes<api/usertype_memory>`. This is compatible with a number of systems other than just sol2, making it easy to interop with select other Lua systems.
|
||||||
* Member methods, properties, variables and functions taking ``self&`` arguments modify data directly
|
* Member methods, properties, variables and functions taking ``self&`` arguments modify data directly
|
||||||
- Work on a copy by taking or returning a copy by value.
|
- Work on a copy by taking or returning a copy by value.
|
||||||
* The actual metatable associated with the usertype has a long name and is defined to be opaque by the Sol implementation.
|
* The actual metatable associated with the usertype has a long name and is defined to be opaque by the Sol implementation.
|
||||||
* Containers get pushed as special usertypes, but can be disabled if problems arising as detailed :doc:`here<api/containers>`.
|
* The actual metatable inner workings is opaque and defined by the Sol implementation, and there are no internal docs because optimizations on the operations are applied based on heuristics we discover from performance testing the system.
|
||||||
* You can use bitfields but it requires some finesse on your part. We have an example to help you get started `here that uses a few tricks`_.
|
* Containers get pushed as special usertypes, but can be disabled if problems arise as detailed :doc:`here<api/containers>`.
|
||||||
|
* You can use bitfields but it requires some finesse on your part. We have an example to help you get started `here, that uses a few tricks`_.
|
||||||
|
|
||||||
.. _here that uses a few tricks: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_bitfields.cpp
|
.. _here that uses a few tricks: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_bitfields.cpp
|
||||||
|
.. _Lua: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_advanced.cpp#L81
|
||||||
|
.. _C++: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_simple.cpp#L51
|
|
@ -78,6 +78,15 @@ int main() {
|
||||||
// can only read from, not write to
|
// can only read from, not write to
|
||||||
"bullets", sol::readonly(&player::bullets)
|
"bullets", sol::readonly(&player::bullets)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// You can also add members to the code, defined in Lua!
|
||||||
|
// This lets you have a high degree of flexibility in the code
|
||||||
|
std::string prelude_script = R"(
|
||||||
|
function player:brake ()
|
||||||
|
self.speed = 0
|
||||||
|
print("we hit the brakes!")
|
||||||
|
end
|
||||||
|
)";
|
||||||
|
|
||||||
std::string player_script = R"(
|
std::string player_script = R"(
|
||||||
-- call single argument integer constructor
|
-- call single argument integer constructor
|
||||||
|
@ -110,12 +119,16 @@ print(p1.bullets)
|
||||||
-- p1.bullets = 20
|
-- p1.bullets = 20
|
||||||
|
|
||||||
p1:boost()
|
p1:boost()
|
||||||
|
-- call the function we define at runtime from a Lua script
|
||||||
|
p1:brake()
|
||||||
)";
|
)";
|
||||||
|
|
||||||
// Uncomment and use the file to try that out, too!
|
// Uncomment and use the file to try that out, too!
|
||||||
// Make sure it's in the local directory of the executable after you build, or adjust the filename path
|
// Make sure it's in the local directory of the executable after you build, or adjust the filename path
|
||||||
// Or whatever else you like!
|
// Or whatever else you like!
|
||||||
|
//lua.script_file("prelude_script.lua");
|
||||||
//lua.script_file("player_script.lua");
|
//lua.script_file("player_script.lua");
|
||||||
|
lua.script(prelude_script);
|
||||||
lua.script(player_script);
|
lua.script(player_script);
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ int main() {
|
||||||
lua.set_usertype("generator", generator_registration);
|
lua.set_usertype("generator", generator_registration);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can update a simple_usertype at runtime, after registration
|
// Can update a usertype at runtime, after registration
|
||||||
lua["generator"]["generate_list"] = [](generator& self) { return self.generate_list(); };
|
lua["generator"]["generate_list"] = [](generator& self) { return self.generate_list(); };
|
||||||
// can set 'static methods' (no self) as well
|
// can set 'static methods' (no self) as well
|
||||||
lua["generator"]["get_num"] = []() { return 100; };
|
lua["generator"]["get_num"] = []() { return 100; };
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
namespace sol {
|
namespace sol {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
311
sol/object.hpp
311
sol/object.hpp
|
@ -1,154 +1,157 @@
|
||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
|
|
||||||
// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors
|
// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors
|
||||||
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
// this software and associated documentation files (the "Software"), to deal in
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
// the Software without restriction, including without limitation the rights to
|
// the Software without restriction, including without limitation the rights to
|
||||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
// subject to the following conditions:
|
// subject to the following conditions:
|
||||||
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
// copies or substantial portions of the Software.
|
// copies or substantial portions of the Software.
|
||||||
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
#ifndef SOL_OBJECT_HPP
|
#ifndef SOL_OBJECT_HPP
|
||||||
#define SOL_OBJECT_HPP
|
#define SOL_OBJECT_HPP
|
||||||
|
|
||||||
#include "reference.hpp"
|
#include "reference.hpp"
|
||||||
#include "stack.hpp"
|
#include "stack.hpp"
|
||||||
#include "userdata.hpp"
|
#include "userdata.hpp"
|
||||||
#include "as_args.hpp"
|
#include "as_args.hpp"
|
||||||
#include "variadic_args.hpp"
|
#include "variadic_args.hpp"
|
||||||
#include "optional.hpp"
|
#include "optional.hpp"
|
||||||
|
|
||||||
namespace sol {
|
namespace sol {
|
||||||
|
|
||||||
template <typename R = reference, bool should_pop = !std::is_base_of<stack_reference, R>::value, typename T>
|
template <typename R = reference, bool should_pop = !std::is_base_of<stack_reference, R>::value, typename T>
|
||||||
R make_reference(lua_State* L, T&& value) {
|
R make_reference(lua_State* L, T&& value) {
|
||||||
int backpedal = stack::push(L, std::forward<T>(value));
|
int backpedal = stack::push(L, std::forward<T>(value));
|
||||||
R r = stack::get<R>(L, -backpedal);
|
R r = stack::get<R>(L, -backpedal);
|
||||||
if (should_pop) {
|
if (should_pop) {
|
||||||
lua_pop(L, backpedal);
|
lua_pop(L, backpedal);
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename R = reference, bool should_pop = !std::is_base_of<stack_reference, R>::value, typename... Args>
|
template <typename T, typename R = reference, bool should_pop = !std::is_base_of<stack_reference, R>::value, typename... Args>
|
||||||
R make_reference(lua_State* L, Args&&... args) {
|
R make_reference(lua_State* L, Args&&... args) {
|
||||||
int backpedal = stack::push<T>(L, std::forward<Args>(args)...);
|
int backpedal = stack::push<T>(L, std::forward<Args>(args)...);
|
||||||
R r = stack::get<R>(L, -backpedal);
|
R r = stack::get<R>(L, -backpedal);
|
||||||
if (should_pop) {
|
if (should_pop) {
|
||||||
lua_pop(L, backpedal);
|
lua_pop(L, backpedal);
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename base_t>
|
template <typename base_t>
|
||||||
class basic_object : public base_t {
|
class basic_object : public base_t {
|
||||||
private:
|
private:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
decltype(auto) as_stack(std::true_type) const {
|
decltype(auto) as_stack(std::true_type) const {
|
||||||
return stack::get<T>(base_t::lua_state(), base_t::stack_index());
|
return stack::get<T>(base_t::lua_state(), base_t::stack_index());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
decltype(auto) as_stack(std::false_type) const {
|
decltype(auto) as_stack(std::false_type) const {
|
||||||
base_t::push();
|
base_t::push();
|
||||||
return stack::pop<T>(base_t::lua_state());
|
return stack::pop<T>(base_t::lua_state());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
bool is_stack(std::true_type) const {
|
bool is_stack(std::true_type) const {
|
||||||
return stack::check<T>(base_t::lua_state(), base_t::stack_index(), no_panic);
|
return stack::check<T>(base_t::lua_state(), base_t::stack_index(), no_panic);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
bool is_stack(std::false_type) const {
|
bool is_stack(std::false_type) const {
|
||||||
auto pp = stack::push_pop(*this);
|
auto pp = stack::push_pop(*this);
|
||||||
return stack::check<T>(base_t::lua_state(), -1, no_panic);
|
return stack::check<T>(base_t::lua_state(), -1, no_panic);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool invert_and_pop = false>
|
template <bool invert_and_pop = false>
|
||||||
basic_object(std::integral_constant<bool, invert_and_pop>, lua_State* L, int index = -1) noexcept : base_t(L, index) {
|
basic_object(std::integral_constant<bool, invert_and_pop>, lua_State* L, int index = -1) noexcept : base_t(L, index) {
|
||||||
if (invert_and_pop) {
|
if (invert_and_pop) {
|
||||||
lua_pop(L, -index);
|
lua_pop(L, -index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
basic_object() noexcept = default;
|
basic_object() noexcept = default;
|
||||||
template <typename T, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_object>>, meta::neg<std::is_same<base_t, stack_reference>>, std::is_base_of<base_t, meta::unqualified_t<T>>> = meta::enabler>
|
template <typename T, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_object>>, meta::neg<std::is_same<base_t, stack_reference>>, std::is_base_of<base_t, meta::unqualified_t<T>>> = meta::enabler>
|
||||||
basic_object(T&& r) : base_t(std::forward<T>(r)) {}
|
basic_object(T&& r) : base_t(std::forward<T>(r)) {}
|
||||||
basic_object(lua_nil_t r) : base_t(r) {}
|
basic_object(lua_nil_t r) : base_t(r) {}
|
||||||
basic_object(const basic_object&) = default;
|
basic_object(const basic_object&) = default;
|
||||||
basic_object(basic_object&&) = default;
|
basic_object(basic_object&&) = default;
|
||||||
basic_object(const stack_reference& r) noexcept : basic_object(r.lua_state(), r.stack_index()) {}
|
basic_object(const stack_reference& r) noexcept : basic_object(r.lua_state(), r.stack_index()) {}
|
||||||
basic_object(stack_reference&& r) noexcept : basic_object(r.lua_state(), r.stack_index()) {}
|
basic_object(stack_reference&& r) noexcept : basic_object(r.lua_state(), r.stack_index()) {}
|
||||||
template <typename Super>
|
template <typename Super>
|
||||||
basic_object(const proxy_base<Super>& r) noexcept : basic_object(r.operator basic_object()) {}
|
basic_object(const proxy_base<Super>& r) noexcept : basic_object(r.operator basic_object()) {}
|
||||||
template <typename Super>
|
template <typename Super>
|
||||||
basic_object(proxy_base<Super>&& r) noexcept : basic_object(r.operator basic_object()) {}
|
basic_object(proxy_base<Super>&& r) noexcept : basic_object(r.operator basic_object()) {}
|
||||||
basic_object(lua_State* L, int index = -1) noexcept : base_t(L, index) {}
|
basic_object(lua_State* L, int index = -1) noexcept : base_t(L, index) {}
|
||||||
basic_object(lua_State* L, ref_index index) noexcept : base_t(L, index) {}
|
basic_object(lua_State* L, ref_index index) noexcept : base_t(L, index) {}
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
basic_object(lua_State* L, in_place_type_t<T>, Args&&... args) noexcept : basic_object(std::integral_constant<bool, !std::is_base_of<stack_reference, base_t>::value>(), L, -stack::push<T>(L, std::forward<Args>(args)...)) {}
|
basic_object(lua_State* L, in_place_type_t<T>, Args&&... args) noexcept : basic_object(std::integral_constant<bool, !std::is_base_of<stack_reference, base_t>::value>(), L, -stack::push<T>(L, std::forward<Args>(args)...)) {}
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
basic_object(lua_State* L, in_place_t, T&& arg, Args&&... args) noexcept : basic_object(L, in_place<T>, std::forward<T>(arg), std::forward<Args>(args)...) {}
|
basic_object(lua_State* L, in_place_t, T&& arg, Args&&... args) noexcept : basic_object(L, in_place<T>, std::forward<T>(arg), std::forward<Args>(args)...) {}
|
||||||
basic_object& operator=(const basic_object&) = default;
|
basic_object& operator=(const basic_object&) = default;
|
||||||
basic_object& operator=(basic_object&&) = default;
|
basic_object& operator=(basic_object&&) = default;
|
||||||
basic_object& operator=(const base_t& b) { base_t::operator=(b); return *this; }
|
basic_object& operator=(const base_t& b) { base_t::operator=(b); return *this; }
|
||||||
basic_object& operator=(base_t&& b) { base_t::operator=(std::move(b)); return *this; }
|
basic_object& operator=(base_t&& b) { base_t::operator=(std::move(b)); return *this; }
|
||||||
template <typename Super>
|
template <typename Super>
|
||||||
basic_object& operator=(const proxy_base<Super>& r) { this->operator=(r.operator basic_object()); return *this; }
|
basic_object& operator=(const proxy_base<Super>& r) { this->operator=(r.operator basic_object()); return *this; }
|
||||||
template <typename Super>
|
template <typename Super>
|
||||||
basic_object& operator=(proxy_base<Super>&& r) { this->operator=(r.operator basic_object()); return *this; }
|
basic_object& operator=(proxy_base<Super>&& r) { this->operator=(r.operator basic_object()); return *this; }
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
decltype(auto) as() const {
|
decltype(auto) as() const {
|
||||||
return as_stack<T>(std::is_same<base_t, stack_reference>());
|
return as_stack<T>(std::is_same<base_t, stack_reference>());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
bool is() const {
|
bool is() const {
|
||||||
if (!base_t::valid())
|
int r = base_t::registry_index();
|
||||||
return false;
|
if (r == LUA_REFNIL)
|
||||||
return is_stack<T>(std::is_same<base_t, stack_reference>());
|
return meta::any_same<meta::unqualified_t<T>, lua_nil_t, nullopt_t, std::nullptr_t>::value ? true : false;
|
||||||
}
|
if (r == LUA_NOREF)
|
||||||
};
|
return false;
|
||||||
|
return is_stack<T>(std::is_same<base_t, stack_reference>());
|
||||||
template <typename T>
|
}
|
||||||
object make_object(lua_State* L, T&& value) {
|
};
|
||||||
return make_reference<object, true>(L, std::forward<T>(value));
|
|
||||||
}
|
template <typename T>
|
||||||
|
object make_object(lua_State* L, T&& value) {
|
||||||
template <typename T, typename... Args>
|
return make_reference<object, true>(L, std::forward<T>(value));
|
||||||
object make_object(lua_State* L, Args&&... args) {
|
}
|
||||||
return make_reference<T, object, true>(L, std::forward<Args>(args)...);
|
|
||||||
}
|
template <typename T, typename... Args>
|
||||||
|
object make_object(lua_State* L, Args&&... args) {
|
||||||
inline bool operator==(const object& lhs, const lua_nil_t&) {
|
return make_reference<T, object, true>(L, std::forward<Args>(args)...);
|
||||||
return !lhs.valid();
|
}
|
||||||
}
|
|
||||||
|
inline bool operator==(const object& lhs, const lua_nil_t&) {
|
||||||
inline bool operator==(const lua_nil_t&, const object& rhs) {
|
return !lhs.valid();
|
||||||
return !rhs.valid();
|
}
|
||||||
}
|
|
||||||
|
inline bool operator==(const lua_nil_t&, const object& rhs) {
|
||||||
inline bool operator!=(const object& lhs, const lua_nil_t&) {
|
return !rhs.valid();
|
||||||
return lhs.valid();
|
}
|
||||||
}
|
|
||||||
|
inline bool operator!=(const object& lhs, const lua_nil_t&) {
|
||||||
inline bool operator!=(const lua_nil_t&, const object& rhs) {
|
return lhs.valid();
|
||||||
return rhs.valid();
|
}
|
||||||
}
|
|
||||||
} // sol
|
inline bool operator!=(const lua_nil_t&, const object& rhs) {
|
||||||
|
return rhs.valid();
|
||||||
#endif // SOL_OBJECT_HPP
|
}
|
||||||
|
} // sol
|
||||||
|
|
||||||
|
#endif // SOL_OBJECT_HPP
|
||||||
|
|
|
@ -68,43 +68,6 @@ namespace sol {
|
||||||
simple_map(const char* mkey, base_walk index, base_walk newindex, variable_map&& vars, function_map&& funcs) : metakey(mkey), variables(std::move(vars)), functions(std::move(funcs)), indexbaseclasspropogation(index), newindexbaseclasspropogation(newindex) {}
|
simple_map(const char* mkey, base_walk index, base_walk newindex, variable_map&& vars, function_map&& funcs) : metakey(mkey), variables(std::move(vars)), functions(std::move(funcs)), indexbaseclasspropogation(index), newindexbaseclasspropogation(newindex) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline int simple_metatable_newindex(lua_State* L) {
|
|
||||||
int isnum = 0;
|
|
||||||
lua_Integer magic = lua_tointegerx(L, lua_upvalueindex(4), &isnum);
|
|
||||||
if (isnum != 0 && magic == toplevel_magic) {
|
|
||||||
for (std::size_t i = 0; i < 3; lua_pop(L, 1), ++i) {
|
|
||||||
// Pointer types, AKA "references" from C++
|
|
||||||
const char* metakey = nullptr;
|
|
||||||
switch (i) {
|
|
||||||
case 0:
|
|
||||||
metakey = &usertype_traits<T*>::metatable()[0];
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
metakey = &usertype_traits<detail::unique_usertype<T>>::metatable()[0];
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
default:
|
|
||||||
metakey = &usertype_traits<T>::metatable()[0];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
luaL_getmetatable(L, metakey);
|
|
||||||
int tableindex = lua_gettop(L);
|
|
||||||
if (type_of(L, tableindex) == type::lua_nil) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
stack::set_field<false, true>(L, stack_reference(L, 2), stack_reference(L, 3), tableindex);
|
|
||||||
}
|
|
||||||
lua_settop(L, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return indexing_fail<false>(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int simple_indexing_fail(lua_State* L) {
|
|
||||||
return stack::push(L, sol::lua_nil);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool is_index, bool toplevel = false>
|
template <bool is_index, bool toplevel = false>
|
||||||
inline int simple_core_indexing_call(lua_State* L) {
|
inline int simple_core_indexing_call(lua_State* L) {
|
||||||
simple_map& sm = toplevel ? stack::get<user<simple_map>>(L, upvalue_index(1)) : stack::pop<user<simple_map>>(L);
|
simple_map& sm = toplevel ? stack::get<user<simple_map>>(L, upvalue_index(1)) : stack::pop<user<simple_map>>(L);
|
||||||
|
@ -328,7 +291,7 @@ namespace sol {
|
||||||
template<std::size_t... I, typename Tuple>
|
template<std::size_t... I, typename Tuple>
|
||||||
simple_usertype_metatable(usertype_detail::verified_tag, std::index_sequence<I...>, lua_State* L, Tuple&& args)
|
simple_usertype_metatable(usertype_detail::verified_tag, std::index_sequence<I...>, lua_State* L, Tuple&& args)
|
||||||
: callconstructfunc(lua_nil),
|
: callconstructfunc(lua_nil),
|
||||||
indexfunc(&usertype_detail::simple_indexing_fail), newindexfunc(&usertype_detail::simple_metatable_newindex<T>),
|
indexfunc(&usertype_detail::indexing_fail<true>), newindexfunc(&usertype_detail::metatable_newindex<T, true>),
|
||||||
indexbase(&usertype_detail::simple_core_indexing_call<true>), newindexbase(&usertype_detail::simple_core_indexing_call<false>),
|
indexbase(&usertype_detail::simple_core_indexing_call<true>), newindexbase(&usertype_detail::simple_core_indexing_call<false>),
|
||||||
indexbaseclasspropogation(usertype_detail::walk_all_bases<true>), newindexbaseclasspropogation(&usertype_detail::walk_all_bases<false>),
|
indexbaseclasspropogation(usertype_detail::walk_all_bases<true>), newindexbaseclasspropogation(&usertype_detail::walk_all_bases<false>),
|
||||||
baseclasscheck(nullptr), baseclasscast(nullptr),
|
baseclasscheck(nullptr), baseclasscast(nullptr),
|
||||||
|
|
|
@ -510,14 +510,19 @@ namespace sol {
|
||||||
class basic_function;
|
class basic_function;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class basic_protected_function;
|
class basic_protected_function;
|
||||||
using function = basic_function<reference>;
|
|
||||||
using protected_function = basic_protected_function<reference>;
|
using protected_function = basic_protected_function<reference>;
|
||||||
using stack_function = basic_function<stack_reference>;
|
|
||||||
using stack_protected_function = basic_protected_function<stack_reference>;
|
using stack_protected_function = basic_protected_function<stack_reference>;
|
||||||
using unsafe_function = basic_function<reference>;
|
using unsafe_function = basic_function<reference>;
|
||||||
using safe_function = basic_protected_function<reference>;
|
using safe_function = basic_protected_function<reference>;
|
||||||
using stack_unsafe_function = basic_function<stack_reference>;
|
using stack_unsafe_function = basic_function<stack_reference>;
|
||||||
using stack_safe_function = basic_protected_function<stack_reference>;
|
using stack_safe_function = basic_protected_function<stack_reference>;
|
||||||
|
#ifdef SOL_SAFE_FUNCTIONS
|
||||||
|
using function = protected_function;
|
||||||
|
using stack_function = stack_protected_function;
|
||||||
|
#else
|
||||||
|
using function = unsafe_function;
|
||||||
|
using stack_function = stack_unsafe_function;
|
||||||
|
#endif
|
||||||
template <typename base_t>
|
template <typename base_t>
|
||||||
class basic_object;
|
class basic_object;
|
||||||
template <typename base_t>
|
template <typename base_t>
|
||||||
|
|
|
@ -36,6 +36,41 @@
|
||||||
|
|
||||||
namespace sol {
|
namespace sol {
|
||||||
namespace usertype_detail {
|
namespace usertype_detail {
|
||||||
|
|
||||||
|
struct add_destructor_tag {};
|
||||||
|
struct check_destructor_tag {};
|
||||||
|
struct verified_tag {} const verified{};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_non_factory_constructor : std::false_type {};
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
struct is_non_factory_constructor<constructors<Args...>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
struct is_non_factory_constructor<constructor_wrapper<Args...>> : std::true_type {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct is_non_factory_constructor<no_construction> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_constructor : is_non_factory_constructor<T> {};
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
struct is_constructor<factory_wrapper<Args...>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
using has_constructor = meta::any<is_constructor<meta::unqualified_t<Args>>...>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_destructor : std::false_type {};
|
||||||
|
|
||||||
|
template <typename Fx>
|
||||||
|
struct is_destructor<destructor_wrapper<Fx>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
using has_destructor = meta::any<is_destructor<meta::unqualified_t<Args>>...>;
|
||||||
|
|
||||||
struct no_comp {
|
struct no_comp {
|
||||||
template <typename A, typename B>
|
template <typename A, typename B>
|
||||||
bool operator()(A&&, B&&) const {
|
bool operator()(A&&, B&&) const {
|
||||||
|
@ -44,15 +79,19 @@ namespace sol {
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void(*base_walk)(lua_State*, bool&, int&, string_detail::string_shim&);
|
typedef void(*base_walk)(lua_State*, bool&, int&, string_detail::string_shim&);
|
||||||
typedef int(*member_search)(lua_State*, void*);
|
typedef int(*member_search)(lua_State*, void*, int);
|
||||||
|
|
||||||
struct find_call_pair {
|
struct call_information {
|
||||||
member_search first;
|
member_search first;
|
||||||
member_search second;
|
member_search second;
|
||||||
|
int runtime_target;
|
||||||
|
|
||||||
find_call_pair(member_search first, member_search second) : first(first), second(second) {}
|
call_information(member_search first, member_search second) : call_information(first, second, -1) {}
|
||||||
|
call_information(member_search first, member_search second, int runtimetarget) : first(first), second(second), runtime_target(runtimetarget) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::unordered_map<std::string, call_information> mapping_t;
|
||||||
|
|
||||||
inline bool is_indexer(string_detail::string_shim s) {
|
inline bool is_indexer(string_detail::string_shim s) {
|
||||||
return s == name_of(meta_function::index) || s == name_of(meta_function::new_index);
|
return s == name_of(meta_function::index) || s == name_of(meta_function::new_index);
|
||||||
}
|
}
|
||||||
|
@ -102,14 +141,71 @@ namespace sol {
|
||||||
virtual ~registrar() {}
|
virtual ~registrar() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline int runtime_object_call(lua_State* L, void*, int runtimetarget) {
|
||||||
|
std::vector<object>& runtime = stack::get<light<std::vector<object>>>(L, lua_upvalueindex(2));
|
||||||
|
return stack::push(L, runtime[runtimetarget]);
|
||||||
|
}
|
||||||
|
|
||||||
template <bool is_index>
|
template <bool is_index>
|
||||||
inline int indexing_fail(lua_State* L) {
|
inline int indexing_fail(lua_State* L) {
|
||||||
auto maybeaccessor = stack::get<optional<string_detail::string_shim>>(L, is_index ? -1 : -2);
|
if (is_index) {
|
||||||
string_detail::string_shim accessor = maybeaccessor.value_or(string_detail::string_shim("(unknown)"));
|
#if 0//def SOL_SAFE_USERTYPE
|
||||||
if (is_index)
|
auto maybeaccessor = stack::get<optional<string_detail::string_shim>>(L, is_index ? -1 : -2);
|
||||||
|
string_detail::string_shim accessor = maybeaccessor.value_or(string_detail::string_shim("(unknown)"));
|
||||||
return luaL_error(L, "sol: attempt to index (get) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.c_str());
|
return luaL_error(L, "sol: attempt to index (get) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.c_str());
|
||||||
else
|
#else
|
||||||
|
// With runtime extensibility, we can't hard-error things. They have to return nil, like regular table types, unfortunately...
|
||||||
|
return stack::push(L, lua_nil);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto maybeaccessor = stack::get<optional<string_detail::string_shim>>(L, is_index ? -1 : -2);
|
||||||
|
string_detail::string_shim accessor = maybeaccessor.value_or(string_detail::string_shim("(unknown)"));
|
||||||
return luaL_error(L, "sol: attempt to index (set) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.c_str());
|
return luaL_error(L, "sol: attempt to index (set) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, bool is_simple>
|
||||||
|
inline int metatable_newindex(lua_State* L) {
|
||||||
|
int isnum = 0;
|
||||||
|
lua_Integer magic = lua_tointegerx(L, lua_upvalueindex(4), &isnum);
|
||||||
|
if (isnum != 0 && magic == toplevel_magic) {
|
||||||
|
bool mustindex = lua_isboolean(L, lua_upvalueindex(5)) != 0 && (lua_toboolean(L, lua_upvalueindex(5)) != 0);
|
||||||
|
if (!is_simple && mustindex) {
|
||||||
|
mapping_t& mapping = stack::get<light<mapping_t>>(L, lua_upvalueindex(3));
|
||||||
|
std::vector<object>& runtime = stack::get<light<std::vector<object>>>(L, lua_upvalueindex(2));
|
||||||
|
int target = static_cast<int>(runtime.size());
|
||||||
|
runtime.emplace_back(L, 3);
|
||||||
|
mapping.emplace_hint(mapping.cend(), stack::get<std::string>(L, 2), call_information(&runtime_object_call, &runtime_object_call, target));
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < 4; lua_pop(L, 1), ++i) {
|
||||||
|
const char* metakey = nullptr;
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
metakey = &usertype_traits<T*>::metatable()[0];
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
metakey = &usertype_traits<detail::unique_usertype<T>>::metatable()[0];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
metakey = &usertype_traits<T>::user_metatable()[0];
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
default:
|
||||||
|
metakey = &usertype_traits<T>::metatable()[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
luaL_getmetatable(L, metakey);
|
||||||
|
int tableindex = lua_gettop(L);
|
||||||
|
if (type_of(L, tableindex) == type::lua_nil) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
stack::set_field<false, true>(L, stack_reference(L, 2), stack_reference(L, 3), tableindex);
|
||||||
|
}
|
||||||
|
lua_settop(L, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return indexing_fail<false>(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool is_index, typename Base>
|
template <bool is_index, typename Base>
|
||||||
|
@ -181,41 +277,6 @@ namespace sol {
|
||||||
inline void make_reg_op(Regs&, int&, const char*) {
|
inline void make_reg_op(Regs&, int&, const char*) {
|
||||||
// Do nothing if there's no support
|
// Do nothing if there's no support
|
||||||
}
|
}
|
||||||
|
|
||||||
struct add_destructor_tag {};
|
|
||||||
struct check_destructor_tag {};
|
|
||||||
struct verified_tag {} const verified{};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_non_factory_constructor : std::false_type {};
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
struct is_non_factory_constructor<constructors<Args...>> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
struct is_non_factory_constructor<constructor_wrapper<Args...>> : std::true_type {};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct is_non_factory_constructor<no_construction> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_constructor : is_non_factory_constructor<T> {};
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
struct is_constructor<factory_wrapper<Args...>> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
using has_constructor = meta::any<is_constructor<meta::unqualified_t<Args>>...>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_destructor : std::false_type {};
|
|
||||||
|
|
||||||
template <typename Fx>
|
|
||||||
struct is_destructor<destructor_wrapper<Fx>> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
using has_destructor = meta::any<is_destructor<meta::unqualified_t<Args>>...>;
|
|
||||||
|
|
||||||
} // usertype_detail
|
} // usertype_detail
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -238,9 +299,9 @@ namespace sol {
|
||||||
typedef std::tuple<clean_type_t<Tn> ...> Tuple;
|
typedef std::tuple<clean_type_t<Tn> ...> Tuple;
|
||||||
template <std::size_t Idx>
|
template <std::size_t Idx>
|
||||||
struct check_binding : is_variable_binding<meta::unqualified_tuple_element_t<Idx, Tuple>> {};
|
struct check_binding : is_variable_binding<meta::unqualified_tuple_element_t<Idx, Tuple>> {};
|
||||||
typedef std::unordered_map<std::string, usertype_detail::find_call_pair> mapping_t;
|
usertype_detail::mapping_t mapping;
|
||||||
|
std::vector<object> runtime;
|
||||||
Tuple functions;
|
Tuple functions;
|
||||||
mapping_t mapping;
|
|
||||||
lua_CFunction indexfunc;
|
lua_CFunction indexfunc;
|
||||||
lua_CFunction newindexfunc;
|
lua_CFunction newindexfunc;
|
||||||
lua_CFunction destructfunc;
|
lua_CFunction destructfunc;
|
||||||
|
@ -357,19 +418,20 @@ namespace sol {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args, typename = std::enable_if_t<sizeof...(Args) == sizeof...(Tn)>>
|
template <typename... Args, typename = std::enable_if_t<sizeof...(Args) == sizeof...(Tn)>>
|
||||||
usertype_metatable(Args&&... args) : functions(std::forward<Args>(args)...),
|
usertype_metatable(Args&&... args) :
|
||||||
mapping(),
|
mapping(),
|
||||||
indexfunc(usertype_detail::indexing_fail<true>), newindexfunc(usertype_detail::indexing_fail<false>),
|
functions(std::forward<Args>(args)...),
|
||||||
|
indexfunc(&usertype_detail::indexing_fail<true>), newindexfunc(&usertype_detail::metatable_newindex<T, false>),
|
||||||
destructfunc(nullptr), callconstructfunc(nullptr),
|
destructfunc(nullptr), callconstructfunc(nullptr),
|
||||||
indexbase(&core_indexing_call<true>), newindexbase(&core_indexing_call<false>),
|
indexbase(&core_indexing_call<true>), newindexbase(&core_indexing_call<false>),
|
||||||
indexbaseclasspropogation(usertype_detail::walk_all_bases<true>), newindexbaseclasspropogation(usertype_detail::walk_all_bases<false>),
|
indexbaseclasspropogation(usertype_detail::walk_all_bases<true>), newindexbaseclasspropogation(usertype_detail::walk_all_bases<false>),
|
||||||
baseclasscheck(nullptr), baseclasscast(nullptr),
|
baseclasscheck(nullptr), baseclasscast(nullptr),
|
||||||
mustindex(contains_variable() || contains_index()), secondarymeta(contains_variable()),
|
mustindex(contains_variable() || contains_index()), secondarymeta(contains_variable()),
|
||||||
hasequals(false), hasless(false), haslessequals(false) {
|
hasequals(false), hasless(false), haslessequals(false) {
|
||||||
std::initializer_list<typename mapping_t::value_type> ilist{ {
|
std::initializer_list<typename usertype_detail::mapping_t::value_type> ilist{ {
|
||||||
std::pair<std::string, usertype_detail::find_call_pair>(
|
std::pair<std::string, usertype_detail::call_information>(
|
||||||
usertype_detail::make_string(std::get<I * 2>(functions)),
|
usertype_detail::make_string(std::get<I * 2>(functions)),
|
||||||
usertype_detail::find_call_pair(&usertype_metatable::real_find_call<I * 2, I * 2 + 1, false>,
|
usertype_detail::call_information(&usertype_metatable::real_find_call<I * 2, I * 2 + 1, false>,
|
||||||
&usertype_metatable::real_find_call<I * 2, I * 2 + 1, true>)
|
&usertype_metatable::real_find_call<I * 2, I * 2 + 1, true>)
|
||||||
)
|
)
|
||||||
}... };
|
}... };
|
||||||
|
@ -377,7 +439,7 @@ namespace sol {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t I0, std::size_t I1, bool is_index>
|
template <std::size_t I0, std::size_t I1, bool is_index>
|
||||||
static int real_find_call(lua_State* L, void* um) {
|
static int real_find_call(lua_State* L, void* um, int) {
|
||||||
auto& f = *static_cast<usertype_metatable*>(um);
|
auto& f = *static_cast<usertype_metatable*>(um);
|
||||||
if (is_variable_binding<decltype(std::get<I1>(f.functions))>::value) {
|
if (is_variable_binding<decltype(std::get<I1>(f.functions))>::value) {
|
||||||
return real_call_with<I1, is_index, true>(L, f);
|
return real_call_with<I1, is_index, true>(L, f);
|
||||||
|
@ -395,8 +457,9 @@ namespace sol {
|
||||||
std::string name = stack::get<std::string>(L, keyidx);
|
std::string name = stack::get<std::string>(L, keyidx);
|
||||||
auto memberit = f.mapping.find(name);
|
auto memberit = f.mapping.find(name);
|
||||||
if (memberit != f.mapping.cend()) {
|
if (memberit != f.mapping.cend()) {
|
||||||
auto& member = is_index ? memberit->second.second : memberit->second.first;
|
const usertype_detail::call_information& ci = memberit->second;
|
||||||
return (member)(L, static_cast<void*>(&f));
|
const usertype_detail::member_search& member = is_index ? ci.second : ci.first;
|
||||||
|
return (member)(L, static_cast<void*>(&f), ci.runtime_target);
|
||||||
}
|
}
|
||||||
string_detail::string_shim accessor = name;
|
string_detail::string_shim accessor = name;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -548,14 +611,14 @@ namespace sol {
|
||||||
stack::set_field(L, detail::base_class_cast_key(), um.baseclasscast, t.stack_index());
|
stack::set_field(L, detail::base_class_cast_key(), um.baseclasscast, t.stack_index());
|
||||||
}
|
}
|
||||||
|
|
||||||
stack::set_field(L, detail::base_class_index_propogation_key(), make_closure(um.indexbase, make_light(um)), t.stack_index());
|
stack::set_field(L, detail::base_class_index_propogation_key(), make_closure(um.indexbase, make_light(um), make_light(um.runtime)), t.stack_index());
|
||||||
stack::set_field(L, detail::base_class_new_index_propogation_key(), make_closure(um.newindexbase, make_light(um)), t.stack_index());
|
stack::set_field(L, detail::base_class_new_index_propogation_key(), make_closure(um.newindexbase, make_light(um), make_light(um.runtime)), t.stack_index());
|
||||||
|
|
||||||
if (mustindex) {
|
if (mustindex) {
|
||||||
// Basic index pushing: specialize
|
// Basic index pushing: specialize
|
||||||
// index and newindex to give variables and stuff
|
// index and newindex to give variables and stuff
|
||||||
stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um)), t.stack_index());
|
stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um), make_light(um.runtime)), t.stack_index());
|
||||||
stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um)), t.stack_index());
|
stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um), make_light(um.runtime)), t.stack_index());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// If there's only functions, we can use the fast index version
|
// If there's only functions, we can use the fast index version
|
||||||
|
@ -566,11 +629,11 @@ namespace sol {
|
||||||
lua_createtable(L, 0, 3);
|
lua_createtable(L, 0, 3);
|
||||||
stack_reference metabehind(L, -1);
|
stack_reference metabehind(L, -1);
|
||||||
if (um.callconstructfunc != nullptr) {
|
if (um.callconstructfunc != nullptr) {
|
||||||
stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um)), metabehind.stack_index());
|
stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um), make_light(um.runtime)), metabehind.stack_index());
|
||||||
}
|
}
|
||||||
if (um.secondarymeta) {
|
if (um.secondarymeta) {
|
||||||
stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um)), metabehind.stack_index());
|
stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um), make_light(um.runtime)), metabehind.stack_index());
|
||||||
stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um)), metabehind.stack_index());
|
stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um), make_light(um.runtime)), metabehind.stack_index());
|
||||||
}
|
}
|
||||||
stack::set_field(L, metatable_key, metabehind, t.stack_index());
|
stack::set_field(L, metatable_key, metabehind, t.stack_index());
|
||||||
metabehind.pop();
|
metabehind.pop();
|
||||||
|
@ -588,12 +651,12 @@ namespace sol {
|
||||||
lua_createtable(L, 0, 3);
|
lua_createtable(L, 0, 3);
|
||||||
stack_reference metabehind(L, -1);
|
stack_reference metabehind(L, -1);
|
||||||
if (um.callconstructfunc != nullptr) {
|
if (um.callconstructfunc != nullptr) {
|
||||||
stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um)), metabehind.stack_index());
|
stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um), static_cast<void*>(&um.runtime)), metabehind.stack_index());
|
||||||
}
|
|
||||||
if (um.secondarymeta) {
|
|
||||||
stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um)), metabehind.stack_index());
|
|
||||||
stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um)), metabehind.stack_index());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um), static_cast<void*>(&um.runtime), static_cast<void*>(&um.mapping), usertype_detail::toplevel_magic, um.mustindex), metabehind.stack_index());
|
||||||
|
stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um), static_cast<void*>(&um.runtime), static_cast<void*>(&um.mapping), usertype_detail::toplevel_magic, um.mustindex), metabehind.stack_index());
|
||||||
|
|
||||||
stack::set_field(L, metatable_key, metabehind, t.stack_index());
|
stack::set_field(L, metatable_key, metabehind, t.stack_index());
|
||||||
metabehind.pop();
|
metabehind.pop();
|
||||||
}
|
}
|
||||||
|
|
|
@ -598,3 +598,101 @@ TEST_CASE("usertype/simple-missing-key", "make sure a missing key returns nil")
|
||||||
lua.new_simple_usertype<thing>("thing");
|
lua.new_simple_usertype<thing>("thing");
|
||||||
REQUIRE_NOTHROW(lua.script("print(thing.missingKey)"));
|
REQUIRE_NOTHROW(lua.script("print(thing.missingKey)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("usertype/simple-runtime-extensibility", "Check if usertypes are runtime extensible") {
|
||||||
|
struct thing {
|
||||||
|
int v = 20;
|
||||||
|
int func(int a) { return a; }
|
||||||
|
};
|
||||||
|
int val = 0;
|
||||||
|
|
||||||
|
SECTION("just functions") {
|
||||||
|
sol::state lua;
|
||||||
|
lua.open_libraries(sol::lib::base);
|
||||||
|
|
||||||
|
lua.new_simple_usertype<thing>("thing",
|
||||||
|
"func", &thing::func
|
||||||
|
);
|
||||||
|
|
||||||
|
lua.script(R"(
|
||||||
|
t = thing.new()
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE_THROWS([&lua]() {
|
||||||
|
lua.script(R"(
|
||||||
|
t.runtime_func = function (a)
|
||||||
|
return a + 50
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
}());
|
||||||
|
|
||||||
|
REQUIRE_THROWS([&lua]() {
|
||||||
|
lua.script(R"(
|
||||||
|
function t:runtime_func(a)
|
||||||
|
return a + 52
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
}());
|
||||||
|
|
||||||
|
lua.script("val = t:func(2)");
|
||||||
|
val = lua["val"];
|
||||||
|
REQUIRE(val == 2);
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW([&lua]() {
|
||||||
|
lua.script(R"(
|
||||||
|
function thing:runtime_func(a)
|
||||||
|
return a + 1
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
}());
|
||||||
|
|
||||||
|
lua.script("val = t:runtime_func(2)");
|
||||||
|
val = lua["val"];
|
||||||
|
REQUIRE(val == 3);
|
||||||
|
}
|
||||||
|
SECTION("with variable") {
|
||||||
|
sol::state lua;
|
||||||
|
lua.open_libraries(sol::lib::base);
|
||||||
|
|
||||||
|
lua.new_simple_usertype<thing>("thing",
|
||||||
|
"func", &thing::func,
|
||||||
|
"v", &thing::v
|
||||||
|
);
|
||||||
|
|
||||||
|
lua.script(R"(
|
||||||
|
t = thing.new()
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE_THROWS([&lua]() {
|
||||||
|
lua.script(R"(
|
||||||
|
t.runtime_func = function (a)
|
||||||
|
return a + 50
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
}());
|
||||||
|
|
||||||
|
REQUIRE_THROWS([&lua]() {
|
||||||
|
lua.script(R"(
|
||||||
|
function t:runtime_func(a)
|
||||||
|
return a + 52
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
}());
|
||||||
|
|
||||||
|
lua.script("val = t:func(2)");
|
||||||
|
val = lua["val"];
|
||||||
|
REQUIRE(val == 2);
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW([&lua]() {
|
||||||
|
lua.script(R"(
|
||||||
|
function thing:runtime_func(a)
|
||||||
|
return a + 1
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
}());
|
||||||
|
|
||||||
|
lua.script("val = t:runtime_func(2)");
|
||||||
|
val = lua["val"];
|
||||||
|
REQUIRE(val == 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1520,5 +1520,106 @@ TEST_CASE("usertype/missing-key", "make sure a missing key returns nil") {
|
||||||
lua.open_libraries(sol::lib::base);
|
lua.open_libraries(sol::lib::base);
|
||||||
|
|
||||||
lua.new_usertype<thing>("thing");
|
lua.new_usertype<thing>("thing");
|
||||||
REQUIRE_NOTHROW(lua.script("print(thing.missingKey)"));
|
REQUIRE_NOTHROW(lua.script("v = thing.missingKey\nprint(v)"));
|
||||||
|
sol::object o = lua["v"];
|
||||||
|
bool isnil = o.is<sol::lua_nil_t>();
|
||||||
|
REQUIRE(isnil);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("usertype/runtime-extensibility", "Check if usertypes are runtime extensible") {
|
||||||
|
struct thing {
|
||||||
|
int v = 20;
|
||||||
|
int func(int a) { return a; }
|
||||||
|
};
|
||||||
|
int val = 0;
|
||||||
|
|
||||||
|
SECTION("just functions") {
|
||||||
|
sol::state lua;
|
||||||
|
lua.open_libraries(sol::lib::base);
|
||||||
|
|
||||||
|
lua.new_usertype<thing>("thing",
|
||||||
|
"func", &thing::func
|
||||||
|
);
|
||||||
|
|
||||||
|
lua.script(R"(
|
||||||
|
t = thing.new()
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE_THROWS([&lua]() {
|
||||||
|
lua.script(R"(
|
||||||
|
t.runtime_func = function (a)
|
||||||
|
return a + 50
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
}());
|
||||||
|
|
||||||
|
REQUIRE_THROWS([&lua]() {
|
||||||
|
lua.script(R"(
|
||||||
|
function t:runtime_func(a)
|
||||||
|
return a + 52
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
}());
|
||||||
|
|
||||||
|
lua.script("val = t:func(2)");
|
||||||
|
val = lua["val"];
|
||||||
|
REQUIRE(val == 2);
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW([&lua]() {
|
||||||
|
lua.script(R"(
|
||||||
|
function thing:runtime_func(a)
|
||||||
|
return a + 1
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
}());
|
||||||
|
|
||||||
|
lua.script("val = t:runtime_func(2)");
|
||||||
|
val = lua["val"];
|
||||||
|
REQUIRE(val == 3);
|
||||||
|
}
|
||||||
|
SECTION("with variable") {
|
||||||
|
sol::state lua;
|
||||||
|
lua.open_libraries(sol::lib::base);
|
||||||
|
|
||||||
|
lua.new_usertype<thing>("thing",
|
||||||
|
"func", &thing::func,
|
||||||
|
"v", &thing::v
|
||||||
|
);
|
||||||
|
|
||||||
|
lua.script(R"(
|
||||||
|
t = thing.new()
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE_THROWS([&lua]() {
|
||||||
|
lua.script(R"(
|
||||||
|
t.runtime_func = function (a)
|
||||||
|
return a + 50
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
}());
|
||||||
|
|
||||||
|
REQUIRE_THROWS([&lua]() {
|
||||||
|
lua.script(R"(
|
||||||
|
function t:runtime_func(a)
|
||||||
|
return a + 52
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
}());
|
||||||
|
|
||||||
|
lua.script("val = t:func(2)");
|
||||||
|
val = lua["val"];
|
||||||
|
REQUIRE(val == 2);
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW([&lua]() {
|
||||||
|
lua.script(R"(
|
||||||
|
function thing:runtime_func(a)
|
||||||
|
return a + 1
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
}());
|
||||||
|
|
||||||
|
lua.script("val = t:runtime_func(2)");
|
||||||
|
val = lua["val"];
|
||||||
|
REQUIRE(val == 3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -326,8 +326,6 @@ TEST_CASE("object/conversions", "make sure all basic reference types can be made
|
||||||
TEST_CASE("state/require_file", "opening files as 'requires'") {
|
TEST_CASE("state/require_file", "opening files as 'requires'") {
|
||||||
static const char FILE_NAME[] = "./tmp_thingy.lua";
|
static const char FILE_NAME[] = "./tmp_thingy.lua";
|
||||||
|
|
||||||
std::fstream file(FILE_NAME, std::ios::out);
|
|
||||||
|
|
||||||
sol::state lua;
|
sol::state lua;
|
||||||
lua.open_libraries(sol::lib::base);
|
lua.open_libraries(sol::lib::base);
|
||||||
|
|
||||||
|
@ -344,6 +342,7 @@ TEST_CASE("state/require_file", "opening files as 'requires'") {
|
||||||
"bar", &foo::bar
|
"bar", &foo::bar
|
||||||
);
|
);
|
||||||
|
|
||||||
|
std::fstream file(FILE_NAME, std::ios::out);
|
||||||
file << "return { modfunc = function () return foo.new(221) end }" << std::endl;
|
file << "return { modfunc = function () return foo.new(221) end }" << std::endl;
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
@ -360,6 +359,7 @@ TEST_CASE("state/require_file", "opening files as 'requires'") {
|
||||||
|
|
||||||
SECTION("simple")
|
SECTION("simple")
|
||||||
{
|
{
|
||||||
|
std::fstream file(FILE_NAME, std::ios::out);
|
||||||
file << "return { modfunc = function () return 221 end }" << std::endl;
|
file << "return { modfunc = function () return 221 end }" << std::endl;
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user