Merge branch 'develop' into sol3

# Conflicts:
#	docs/source/tutorial/variables.rst
#	include/single/sol/sol.hpp
#	include/single/sol/sol_forward.hpp
#	include/sol/proxy.hpp
#	sol/usertype_metatable.hpp
This commit is contained in:
ThePhD 2018-11-10 07:12:13 -08:00
commit 162746dac9
No known key found for this signature in database
GPG Key ID: 1509DB1C0F702BFA
19 changed files with 1112 additions and 242 deletions

View File

@ -78,6 +78,17 @@ Gets the value associated with the keys and converts it to the type ``T``.
Gets the value associated with the keys and converts it to the type ``T``. If it is not of the proper type, it will return a ``sol::nullopt`` instead.
.. code-block:: c++
:caption: function: [overloaded] optionally get or create a value
:name: regular-get-or-create
template <typename T>
decltype(auto) get_or_create();
template <typename T, typename Otherwise>
decltype(auto) get_or_create( Otherwise&& other );
Gets the value associated with the keys if it exists. If it does not, it will set it with the value and return the result.
``operator[]`` proxy-only members
---------------------------------

8
docs/source/build.rst Normal file
View File

@ -0,0 +1,8 @@
Build
=====
sol2 comes with a CMake script in the top level. It is primarily made for building and running the examples and tests, but it includes exported and configured targets (``sol2``, ``sol2_single``) for your use.
sol2 also comes with a Meson Script. If things stop working, file a bug report.

View File

@ -1,61 +0,0 @@
CMake Script
============
sol2 comes with a CMake script in the top level. It is primarily made for building and running the examples and tests, but it includes exported and configured targets (``sol2``, ``sol2_single``) for your use. If you have any problems with it or its targets, please do file a report, or a pull request because CMake is not my forte.
.. warning::
The below is slightly outdated, but will probably still work for you!
Thanks to `Kevin Brightwell`_, you can drop this CMake Script into your CMake Projects to have Sol part of one of its builds:
.. code-block:: cmake
:caption: CMake Build Script
:name: cmake-build-script
# Needed for ExternalProject_Add()
include(ExternalProject)
# Needed for building single header for sol2
find_package(PythonInterp 3 REQUIRED)
# Configuration data for What sol2 version to use and where to put it
set(SOL2_TAG v2.5.6)
set(SOL2_HPP "${CMAKE_BINARY_DIR}/include/sol.hpp")
# Download and "install" sol2
ExternalProject_add(
sol2
PREFIX ${VENDOR_PATH} # Set this value yourself
GIT_REPOSITORY "https://github.com/ThePhD/sol2.git"
GIT_TAG ${SOL2_TAG}
# No CMake commands to run, so tell CMake not to configure
CONFIGURE_COMMAND ""
# Generate the single header and put it in ${SOL2_HPP}
BINARY_DIR ${VENDOR_PREFIX}/src/sol2
BUILD_COMMAND
${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/include
COMMAND
${PYTHON_EXECUTABLE} ./single.py -o "${SOL2_HPP}"
# No install or test command for the library
INSTALL_COMMAND ""
TEST_COMMAND "")
# Conditionally turn on SOL_CHECK_ARGUMENTS if using Debug mode
if (CMAKE_BUILD_TYPE MATCHES "[Dd]ebug")
if (VERBOSE)
message(STATUS "Turning on SOL_CHECK_ARGUMENTS in Debug mode.")
endif()
add_definitions(-DSOL_CHECK_ARGUMENTS)
endif()
# Make sure sol2 is found as a system directory
include_directories(SYSTEM ${CMAKE_BINARY_DIR}/include)
.. _Kevin Brightwell: https://github.com/ThePhD/sol2/issues/89

View File

@ -57,7 +57,7 @@ author = 'ThePhD'
# The short X.Y version.
version = '2.20'
# The full version, including alpha/beta/rc tags.
release = '2.20.2'
release = '2.20.5'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@ -40,7 +40,7 @@ get going:
exceptions
rtti
codecvt
cmake
build
licenses
origin

View File

@ -6,7 +6,7 @@ Sol can register all kinds of functions. Many are shown in the :doc:`quick 'n' d
Setting a new function
----------------------
Given a C++ function, you can drop it into Sol in several equivalent ways, working similar to how :ref:`setting variables<writing-main-cpp>` works:
Given a C++ function, you can drop it into Sol in several equivalent ways, working similar to how :ref:`setting variables<writing-variables-demo>` works:
.. code-block:: cpp
:linenos:

View File

@ -1,112 +1,47 @@
variables
=========
Working with variables is easy with Sol, and behaves pretty much like any associative array / map structure you've dealt with previously. Given this lua file that gets loaded into Sol:
Working with variables is easy with sol, and behaves pretty much like any associative array / map structure you might have dealt with previously.
reading
-------
.. code-block:: lua
:caption: variables.lua
Given this lua file that gets loaded into sol:
config = {
fullscreen = false,
resolution = { x = 1024, y = 768 }
}
.. literalinclude:: ../../../examples/tutorials/variables_demo.cpp
:linenos:
:lines: 15-18
.. code-block:: cpp
:caption: main.cpp
:name: variables-main-cpp
#define SOL_CHECK_ARGUMENTS 1
#include <sol/sol.hpp>
You can interact with the Lua Virtual Machine like so:
int main () {
sol::state lua;
lua.script_file( variables.lua );
return 0;
}
You can interact with the variables like this:
.. code-block:: cpp
:caption: main.cpp extended
:name: extended-variables-main-cpp
#define SOL_CHECK_ARGUMENTS 1
#include <sol/sol.hpp>
#include <tuple>
#include <utility> // for std::pair
int main () {
sol::state lua;
lua.script_file( variables.lua );
// the type "state" behaves exactly like a table!
bool isfullscreen = lua["config"]["fullscreen"]; // can get nested variables
sol::table config = lua["config"];
// can also get it using the "get" member function
// auto replaces the unqualified type name
auto resolution = config.get<sol::table>( "resolution" );
// table and state can have multiple things pulled out of it too
std::pair<int, int> xyresolution = resolution.get<int, int>( "x", "y" );
// As an example, you can also pull out a tuple as well
std::tuple<int, int> xyresolutiontuple = resolution.get<int, int>( "x", "y" );
return 0;
}
.. literalinclude:: ../../../examples/tutorials/variables_demo.cpp
:linenos:
:lines: 1-10, 12-12, 20-24, 70-
From this example, you can see that there's many ways to pull out the varaibles you want. For example, to determine if a nested variable exists or not, you can use ``auto`` to capture the value of a ``table[key]`` lookup, and then use the ``.valid()`` method:
.. code-block:: cpp
:caption: safe lookup
auto bark = lua["config"]["bark"];
if (bark.valid()) {
// branch not taken: config / bark is not a variable
}
else {
// Branch taken: config is a not a variable
}
.. literalinclude:: ../../../examples/tutorials/variables_demo.cpp
:linenos:
:lines: 1-10, 12-12, 34-43, 70-
This comes in handy when you want to check if a nested variable exists. You can also check if a toplevel variable is present or not by using ``sol::optional``, which also checks if A) the keys you're going into exist and B) the type you're trying to get is of a specific type:
.. code-block:: cpp
.. literalinclude:: ../../../examples/tutorials/variables_demo.cpp
:linenos:
:caption: optional lookup
:lines: 1-10, 12-12, 43-58, 70-
sol::optional<int> not_an_integer = lua["config"]["fullscreen"];
if (not_an_integer) {
// Branch not taken: value is not an integer
}
sol::optional<bool> is_a_boolean = lua["config"]["fullscreen"];
if (is_a_boolean) {
// Branch taken: the value is a boolean
}
sol::optional<double> does_not_exist = lua["not_a_variable"];
if (does_not_exist) {
// Branch not taken: that variable is not present
}
This can come in handy when, even in optimized or release modes, you still want the safety of checking. You can also use the `get_or` methods to, if a certain value may be present but you just want to default the value to something else:
.. code-block:: cpp
:caption: get_or lookup
.. literalinclude:: ../../../examples/tutorials/variables_demo.cpp
:linenos:
:caption: optional lookup
:lines: 1-10, 12-12, 60-
// this will result in a value of '24'
int is_defaulted = lua["config"]["fullscreen"].get_or( 24 );
// This will result in the value of the config, which is 'false'
bool is_not_defaulted = lua["config"]["fullscreen"];
That's all it takes to read variables!
@ -116,96 +51,20 @@ writing
Writing gets a lot simpler. Even without scripting a file or a string, you can read and write variables into lua as you please:
.. code-block:: cpp
:caption: main.cpp
:name: writing-main-cpp
#define SOL_CHECK_ARGUMENTS 1
#include <sol/sol.hpp>
#include <iostream>
int main () {
sol::state lua;
// open those basic lua libraries again, like print() etc.
lua.open_libraries( sol::lib::base );
// value in the global table
lua["bark"] = 50;
// a table being created in the global table
lua["some_table"] = lua.create_table_with(
"key0", 24,
"key1", 25,
lua["bark"], "the key is 50 and this string is its value!"
);
// Run a plain ol' string of lua code
// Note you can interact with things set through Sol in C++ with lua!
// Using a "Raw String Literal" to have multi-line goodness: http://en.cppreference.com/w/cpp/language/string_literal
lua.script(R"(
print(some_table[50])
print(some_table["key0"])
print(some_table["key1"])
-- a lua comment: access a global in a lua script with the _G table
print(_G["bark"])
)");
return 0;
}
.. literalinclude:: ../../../examples/tutorials/write_variables_demo.cpp
:linenos:
:name: writing-variables-demo
This example pretty much sums up what can be done. Note that the syntax ``lua["non_existing_key_1"] = 1`` will make that variable, but if you tunnel too deep without first creating a table, the Lua API will panic (e.g., ``lua["does_not_exist"]["b"] = 20`` will trigger a panic). You can also be lazy with reading / writing values:
.. code-block:: cpp
:caption: main.cpp
:name: lazy-main-cpp
.. literalinclude:: ../../../examples/tutorials/lazy_demo.cpp
:linenos:
#define SOL_CHECK_ARGUMENTS 1
#include <sol/sol.hpp>
#include <iostream>
int main () {
sol::state lua;
auto barkkey = lua["bark"];
if (barkkey.valid()) {
// Branch not taken: doesn't exist yet
std::cout << "How did you get in here, arf?!" << std::endl;
}
barkkey = 50;
if (barkkey.valid()) {
// Branch taken: value exists!
std::cout << "Bark Bjork Wan Wan Wan" << std::endl;
}
}
Finally, it's possible to erase a reference/variable by setting it to ``nil``, using the constant ``sol::nil`` in C++:
.. code-block:: cpp
:caption: main.cpp
:name: erase-main-cpp
.. literalinclude:: ../../../examples/tutorials/erase_demo.cpp
:linenos:
#define SOL_CHECK_ARGUMENTS 1
#include <sol/sol.hpp>
int main () {
sol::state lua;
lua["bark"] = 50;
sol::optional<int> x = lua["bark"];
// x will have a value
lua["bark"] = sol::nil;
sol::optional<int> y = lua["bark"];
// y will not have a value
}
It's easy to see that there's a lot of options to do what you want here. But, these are just traditional numbers and strings. What if we want more power, more capabilities than what these limited types can offer us? Let's throw some :doc:`functions in there<functions>` :doc:`C++ classes into the mix<cxx-in-lua>`!

View File

@ -0,0 +1,16 @@
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
int main() {
sol::state lua;
lua["bark"] = 50;
sol::optional<int> x = lua["bark"];
// x will have a value
lua["bark"] = sol::nil;
sol::optional<int> y = lua["bark"];
// y will not have a value
return 0;
}

View File

@ -0,0 +1,23 @@
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
#include <iostream>
int main() {
sol::state lua;
auto barkkey = lua["bark"];
if (barkkey.valid()) {
// Branch not taken: doesn't exist yet
std::cout << "How did you get in here, arf?!" << std::endl;
}
barkkey = 50;
if (barkkey.valid()) {
// Branch taken: value exists!
std::cout << "Bark Bjork Wan Wan Wan" << std::endl;
}
return 0;
}

View File

@ -24,7 +24,14 @@ int main() {
// "bark" namespacing in Lua
// namespacing is just putting things in a table
sol::table bark = lua.create_named_table("bark");
// forces creation if it does not exist
auto bark = lua["bark"].get_or_create<sol::table>();
// equivalent-ish:
//sol::table bark = lua["bark"].force(); // forces table creation
// equivalent, and more flexible:
//sol::table bark = lua["bark"].get_or_create<sol::table>(sol::new_table());
// equivalent, but less efficient/ugly:
//sol::table bark = lua["bark"] = lua.get_or("bark", lua.create_table());
bark.new_usertype<my_class>("my_class",
"f", &my_class::f,
"g", &my_class::g); // the usual

View File

@ -0,0 +1,71 @@
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
#include <tuple>
#include "../assert.hpp"
#include <utility> // for std::pair
int main() {
sol::state lua;
/*
lua.script_file("variables.lua");
*/
lua.script(R"(
config = {
fullscreen = false,
resolution = { x = 1024, y = 768 }
}
)");
// the type "sol::state" behaves
// exactly like a table!
bool isfullscreen = lua["config"]["fullscreen"]; // can get nested variables
sol::table config = lua["config"];
c_assert(isfullscreen);
// can also get it using the "get" member function
// auto replaces the unqualified type name
auto resolution = config.get<sol::table>("resolution");
// table and state can have multiple things pulled out of it too
std::tuple<int, int> xyresolutiontuple = resolution.get<int, int>("x", "y");
c_assert(std::get<0>(xyresolutiontuple) == 1024);
c_assert(std::get<1>(xyresolutiontuple) == 768);
// test variable
auto bark = lua["config"]["bark"];
if (bark.valid()) {
// branch not taken: config and/or bark are not variables
}
else {
// Branch taken: config and bark are existing variables
}
// can also use optional
sol::optional<int> not_an_integer = lua["config"]["fullscreen"];
if (not_an_integer) {
// Branch not taken: value is not an integer
}
sol::optional<bool> is_a_boolean = lua["config"]["fullscreen"];
if (is_a_boolean) {
// Branch taken: the value is a boolean
}
sol::optional<double> does_not_exist = lua["not_a_variable"];
if (does_not_exist) {
// Branch not taken: that variable is not present
}
// this will result in a value of '24'
// (it tries to get a number, and fullscreen is
// not a number
int is_defaulted = lua["config"]["fullscreen"].get_or(24);
c_assert(is_defaulted == 24);
// This will result in the value of the config, which is 'false'
bool is_not_defaulted = lua["config"]["fullscreen"];
c_assert(!is_not_defaulted);
return 0;
}

View File

@ -0,0 +1,39 @@
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
#include <iostream>
int main() {
sol::state lua;
// open those basic lua libraries
// again, for print() and other basic utilities
lua.open_libraries(sol::lib::base);
// value in the global table
lua["bark"] = 50;
// a table being created in the global table
lua["some_table"] = lua.create_table_with(
"key0", 24,
"key1", 25,
lua["bark"], "the key is 50 and this string is its value!");
// Run a plain ol' string of lua code
// Note you can interact with things set through Sol in C++ with lua!
// Using a "Raw String Literal" to have multi-line goodness:
// http://en.cppreference.com/w/cpp/language/string_literal
lua.script(R"(
print(some_table[50])
print(some_table["key0"])
print(some_table["key1"])
-- a lua comment: access a global in a lua script with the _G table
print(_G["bark"])
)");
return 0;
}

View File

@ -208,7 +208,7 @@ namespace sol {
}
template <typename T, bool checked, bool clean_stack, typename... TypeLists>
inline int construct(lua_State* L) {
inline int construct_trampolined(lua_State* L) {
static const auto& meta = usertype_traits<T>::metatable();
int argcount = lua_gettop(L);
call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, usertype_traits<T>::user_metatable(), 1) : call_syntax::dot;
@ -227,6 +227,11 @@ namespace sol {
return 1;
}
template <typename T, bool checked, bool clean_stack, typename... TypeLists>
inline int construct(lua_State* L) {
return detail::static_trampoline<&construct_trampolined<T, checked, clean_stack, TypeLists...>>(L);
}
template <typename F, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename = void>
struct agnostic_lua_call_wrapper {
typedef wrapper<meta::unqualified_t<F>> wrap;

View File

@ -220,7 +220,7 @@ namespace sol {
template <bool is_yielding, typename Fx, typename... Args>
static void set_fx(lua_State* L, Args&&... args) {
lua_CFunction freefunc = function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>;
lua_CFunction freefunc = detail::static_trampoline<function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>>;
int upvalues = 0;
upvalues += stack::push(L, nullptr);

View File

@ -1,4 +1,4 @@
// sol3
// sol2
// The MIT License (MIT)
@ -118,6 +118,20 @@ namespace sol {
return static_cast<T>(std::forward<D>(otherwise));
}
template <typename T>
decltype(auto) get_or_create() {
return get_or_create<T>(new_table());
}
template <typename T, typename Otherwise>
decltype(auto) get_or_create(Otherwise&& other) {
if (!this->valid()) {
this->set(std::forward<Otherwise>(other));
}
return get<T>();
}
template <typename K>
decltype(auto) operator[](K&& k) const {
auto keys = meta::tuplefy(key, std::forward<K>(k));
@ -168,6 +182,13 @@ namespace sol {
lua_State* lua_state() const {
return tbl.lua_state();
}
proxy& force() {
if (this->valid()) {
this->set(new_table());
}
return *this;
}
};
template <typename Table, typename Key, typename T>

View File

@ -140,12 +140,12 @@ namespace stack {
}
#endif // Do not allow strings to be numbers
int isnum = 0;
const lua_Number v = lua_tonumberx(L, index, &isnum);
const bool success = isnum != 0
#if (defined(SOL_SAFE_NUMERICS) && SOL_SAFE_NUMERICS) && !(defined(SOL_NO_CHECK_NUMBER_PRECISION) && SOL_NO_CHECK_NUMBER_PRECISION)
&& static_cast<lua_Number>(llround(v)) == v
const lua_Number v = lua_tonumberx(L, index, &isnum);
const bool success = isnum != 0 && static_cast<lua_Number>(llround(v)) == v;
#else
const bool success = isnum != 0;
#endif // Safe numerics and number precision checking
;
if (!success) {
// expected type, actual type
#if defined(SOL_STRINGS_ARE_NUMBERS) && SOL_STRINGS_ARE_NUMBERS

View File

@ -86,6 +86,7 @@ namespace stack {
struct qualified_getter<T, std::enable_if_t<
!std::is_reference<T>::value
&& is_container<meta::unqualified_t<T>>::value
&& std::is_default_constructible<meta::unqualified_t<T>>::value
&& !is_lua_primitive<T>::value
&& !is_transparent_argument<T>::value
>> {

842
sol/usertype_metatable.hpp Normal file
View File

@ -0,0 +1,842 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// 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
// the Software without restriction, including without limitation the rights to
// 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,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// 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
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef SOL_USERTYPE_METATABLE_HPP
#define SOL_USERTYPE_METATABLE_HPP
#include "wrapper.hpp"
#include "call.hpp"
#include "stack.hpp"
#include "types.hpp"
#include "stack_reference.hpp"
#include "usertype_traits.hpp"
#include "inheritance.hpp"
#include "raii.hpp"
#include "deprecate.hpp"
#include "object.hpp"
#include "container_usertype_metatable.hpp"
#include "usertype_core.hpp"
#include <unordered_map>
#include <cstdio>
#include <sstream>
#include <cassert>
#include <bitset>
namespace sol {
struct usertype_metatable_core;
namespace usertype_detail {
const int metatable_index = 2;
const int metatable_core_index = 3;
const int filler_index = 4;
const int magic_index = 5;
const int simple_metatable_index = 2;
const int index_function_index = 3;
const int newindex_function_index = 4;
typedef void (*base_walk)(lua_State*, bool&, int&, string_view&);
typedef int (*member_search)(lua_State*, void*, usertype_metatable_core&, int);
struct call_information {
member_search index;
member_search new_index;
int runtime_target;
call_information(member_search index, member_search newindex)
: call_information(index, newindex, -1) {
}
call_information(member_search index, member_search newindex, int runtimetarget)
: index(index), new_index(newindex), runtime_target(runtimetarget) {
}
};
typedef map_t<std::string, call_information> mapping_t;
struct variable_wrapper {
virtual int index(lua_State* L) = 0;
virtual int new_index(lua_State* L) = 0;
virtual ~variable_wrapper(){};
};
template <typename T, typename F>
struct callable_binding : variable_wrapper {
F fx;
template <typename Arg>
callable_binding(Arg&& arg)
: fx(std::forward<Arg>(arg)) {
}
virtual int index(lua_State* L) override {
return call_detail::call_wrapped<T, true, true>(L, fx);
}
virtual int new_index(lua_State* L) override {
return call_detail::call_wrapped<T, false, true>(L, fx);
}
};
typedef map_t<std::string, std::unique_ptr<variable_wrapper>> variable_map;
typedef map_t<std::string, object> function_map;
struct simple_map {
const char* metakey;
variable_map variables;
function_map functions;
object index;
object newindex;
base_walk indexbaseclasspropogation;
base_walk newindexbaseclasspropogation;
simple_map(const char* mkey, base_walk index, base_walk newindex, object i, object ni, variable_map&& vars, function_map&& funcs)
: metakey(mkey), variables(std::move(vars)), functions(std::move(funcs)), index(std::move(i)), newindex(std::move(ni)), indexbaseclasspropogation(index), newindexbaseclasspropogation(newindex) {
}
};
} // namespace usertype_detail
struct usertype_metatable_core {
usertype_detail::mapping_t mapping;
lua_CFunction indexfunc;
lua_CFunction newindexfunc;
std::vector<object> runtime;
bool mustindex;
usertype_metatable_core(lua_CFunction ifx, lua_CFunction nifx)
: mapping(), indexfunc(ifx), newindexfunc(nifx), runtime(), mustindex(false) {
}
usertype_metatable_core(const usertype_metatable_core&) = default;
usertype_metatable_core(usertype_metatable_core&&) = default;
usertype_metatable_core& operator=(const usertype_metatable_core&) = default;
usertype_metatable_core& operator=(usertype_metatable_core&&) = default;
};
namespace usertype_detail {
const lua_Integer toplevel_magic = static_cast<lua_Integer>(0xCCC2CCC1);
inline int is_indexer(string_view s) {
if (s == to_string(meta_function::index)) {
return 1;
}
else if (s == to_string(meta_function::new_index)) {
return 2;
}
return 0;
}
inline int is_indexer(meta_function mf) {
if (mf == meta_function::index) {
return 1;
}
else if (mf == meta_function::new_index) {
return 2;
}
return 0;
}
inline int is_indexer(call_construction) {
return 0;
}
inline int is_indexer(base_classes_tag) {
return 0;
}
inline auto make_string_view(string_view s) {
return s;
}
inline auto make_string_view(call_construction) {
return string_view(to_string(meta_function::call_function));
}
inline auto make_string_view(meta_function mf) {
return string_view(to_string(mf));
}
inline auto make_string_view(base_classes_tag) {
return string_view(detail::base_class_cast_key());
}
template <typename Arg>
inline std::string make_string(Arg&& arg) {
string_view s = make_string_view(arg);
return std::string(s.data(), s.size());
}
template <typename N>
inline luaL_Reg make_reg(N&& n, lua_CFunction f) {
luaL_Reg l{make_string_view(std::forward<N>(n)).data(), f};
return l;
}
struct registrar {
registrar() = default;
registrar(const registrar&) = default;
registrar(registrar&&) = default;
registrar& operator=(const registrar&) = default;
registrar& operator=(registrar&&) = default;
virtual int push_um(lua_State* L) = 0;
virtual ~registrar() {
}
};
inline bool is_toplevel(lua_State* L, int index = magic_index) {
int isnum = 0;
lua_Integer magic = lua_tointegerx(L, upvalue_index(index), &isnum);
return isnum != 0 && magic == toplevel_magic;
}
inline int runtime_object_call(lua_State* L, void*, usertype_metatable_core& umc, int runtimetarget) {
std::vector<object>& runtime = umc.runtime;
object& runtimeobj = runtime[runtimetarget];
return stack::push(L, runtimeobj);
}
template <typename T, bool is_index>
inline int indexing_fail(lua_State* L) {
if (is_index) {
#if 0 //defined(SOL_SAFE_USERTYPE) && SOL_SAFE_USERTYPE
auto maybeaccessor = stack::get<optional<string_view>>(L, is_index ? -1 : -2);
string_view 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.data());
#else
if (is_toplevel(L)) {
if (lua_getmetatable(L, 1) == 1) {
int metatarget = lua_gettop(L);
stack::get_field(L, stack_reference(L, raw_index(2)), metatarget);
return 1;
}
}
// 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_view>>(L, is_index ? -1 : -2);
string_view accessor = maybeaccessor.value_or(string_view("(unknown)"));
return luaL_error(L, "sol: attempt to index (set) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.data());
}
}
int runtime_new_index(lua_State* L, void*, usertype_metatable_core&, int runtimetarget);
template <typename T, bool is_simple>
inline int metatable_new_index(lua_State* L) {
if (is_toplevel(L)) {
auto non_indexable = [&L]() {
if (is_simple) {
simple_map& sm = stack::get<user<simple_map>>(L, upvalue_index(simple_metatable_index));
function_map& functions = sm.functions;
optional<string_view> maybeaccessor = stack::get<optional<string_view>>(L, 2);
if (!maybeaccessor) {
return;
}
string_view& accessor_view = maybeaccessor.value();
#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
auto preexistingit = functions.find(accessor_view, string_view_hash(), std::equal_to<string_view>());
#else
std::string accessor(accessor_view.data(), accessor_view.size());
auto preexistingit = functions.find(accessor);
#endif
if (preexistingit == functions.cend()) {
#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
std::string accessor(accessor_view.data(), accessor_view.size());
#endif
functions.emplace_hint(preexistingit, std::move(accessor), object(L, 3));
}
else {
preexistingit->second = object(L, 3);
}
return;
}
usertype_metatable_core& umc = stack::get<light<usertype_metatable_core>>(L, upvalue_index(metatable_core_index));
bool mustindex = umc.mustindex;
if (!mustindex)
return;
optional<string_view> maybeaccessor = stack::get<optional<string_view>>(L, 2);
if (!maybeaccessor) {
return;
}
string_view& accessor_view = maybeaccessor.value();
mapping_t& mapping = umc.mapping;
std::vector<object>& runtime = umc.runtime;
int target = static_cast<int>(runtime.size());
#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
auto preexistingit = mapping.find(accessor_view, string_view_hash(), std::equal_to<string_view>());
#else
std::string accessor(accessor_view.data(), accessor_view.size());
auto preexistingit = mapping.find(accessor);
#endif
if (preexistingit == mapping.cend()) {
#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
std::string accessor(accessor_view.data(), accessor_view.size());
#endif
runtime.emplace_back(L, 3);
mapping.emplace_hint(mapping.cend(), std::move(accessor), call_information(&runtime_object_call, &runtime_new_index, target));
}
else {
target = preexistingit->second.runtime_target;
runtime[target] = object(L, 3);
preexistingit->second = call_information(&runtime_object_call, &runtime_new_index, target);
}
};
non_indexable();
for (std::size_t i = 0; i < 4; lua_settop(L, 3), ++i) {
const char* metakey = nullptr;
switch (i) {
case 0:
metakey = &usertype_traits<T*>::metatable()[0];
luaL_getmetatable(L, metakey);
break;
case 1:
metakey = &usertype_traits<detail::unique_usertype<T>>::metatable()[0];
luaL_getmetatable(L, metakey);
break;
case 2:
metakey = &usertype_traits<T>::metatable()[0];
luaL_getmetatable(L, metakey);
break;
case 3:
default:
metakey = &usertype_traits<T>::user_metatable()[0];
{
luaL_getmetatable(L, metakey);
lua_getmetatable(L, -1);
}
break;
}
int tableindex = lua_gettop(L);
if (type_of(L, tableindex) == type::lua_nil) {
continue;
}
stack::set_field<false, true>(L, stack_reference(L, raw_index(2)), stack_reference(L, raw_index(3)), tableindex);
}
lua_settop(L, 0);
return 0;
}
return indexing_fail<T, false>(L);
}
inline int runtime_new_index(lua_State* L, void*, usertype_metatable_core& umc, int runtimetarget) {
std::vector<object>& runtime = umc.runtime;
object& runtimeobj = runtime[runtimetarget];
runtimeobj = object(L, 3);
return 0;
}
template <bool is_index, typename Base>
static void walk_single_base(lua_State* L, bool& found, int& ret, string_view&) {
if (found)
return;
const char* metakey = &usertype_traits<Base>::metatable()[0];
const char* gcmetakey = &usertype_traits<Base>::gc_table()[0];
const char* basewalkkey = is_index ? detail::base_class_index_propogation_key() : detail::base_class_new_index_propogation_key();
luaL_getmetatable(L, metakey);
if (type_of(L, -1) == type::lua_nil) {
lua_pop(L, 1);
return;
}
stack::get_field(L, basewalkkey);
if (type_of(L, -1) == type::lua_nil) {
lua_pop(L, 2);
return;
}
lua_CFunction basewalkfunc = stack::pop<lua_CFunction>(L);
lua_pop(L, 1);
stack::get_field<true>(L, gcmetakey);
int value = basewalkfunc(L);
if (value > -1) {
found = true;
ret = value;
}
}
template <bool is_index, typename... Bases>
static void walk_all_bases(lua_State* L, bool& found, int& ret, string_view& accessor) {
(void)L;
(void)found;
(void)ret;
(void)accessor;
(void)detail::swallow{0, (walk_single_base<is_index, Bases>(L, found, ret, accessor), 0)...};
}
} // namespace usertype_detail
template <typename T>
struct clean_type {
typedef std::conditional_t<std::is_array<meta::unqualified_t<T>>::value, T&, std::decay_t<T>> type;
};
template <typename T>
using clean_type_t = typename clean_type<T>::type;
template <typename T, typename IndexSequence, typename... Tn>
struct usertype_metatable : usertype_detail::registrar {};
template <typename T, std::size_t... I, typename... Tn>
struct usertype_metatable<T, std::index_sequence<I...>, Tn...> : usertype_metatable_core, usertype_detail::registrar {
typedef std::make_index_sequence<sizeof...(I) * 2> indices;
typedef std::index_sequence<I...> half_indices;
typedef std::array<luaL_Reg, sizeof...(Tn) / 2 + 1 + 31> regs_t;
typedef std::tuple<Tn...> RawTuple;
typedef std::tuple<clean_type_t<Tn>...> Tuple;
template <std::size_t Idx>
struct check_binding : is_variable_binding<meta::unqualified_tuple_element_t<Idx, Tuple>> {};
Tuple functions;
lua_CFunction destructfunc;
lua_CFunction callconstructfunc;
lua_CFunction indexbase;
lua_CFunction newindexbase;
usertype_detail::base_walk indexbaseclasspropogation;
usertype_detail::base_walk newindexbaseclasspropogation;
void* baseclasscheck;
void* baseclasscast;
bool secondarymeta;
std::bitset<32> properties;
template <std::size_t Idx, meta::enable<std::is_same<lua_CFunction, meta::unqualified_tuple_element<Idx + 1, RawTuple>>> = meta::enabler>
lua_CFunction make_func() const {
return std::get<Idx + 1>(functions);
}
template <std::size_t Idx, meta::disable<std::is_same<lua_CFunction, meta::unqualified_tuple_element<Idx + 1, RawTuple>>> = meta::enabler>
lua_CFunction make_func() const {
const auto& name = std::get<Idx>(functions);
return (usertype_detail::make_string_view(name) == "__newindex") ? &call<Idx + 1, false> : &call<Idx + 1, true>;
}
static bool contains_variable() {
typedef meta::any<check_binding<(I * 2 + 1)>...> has_variables;
return has_variables::value;
}
bool contains_index() const {
bool idx = false;
(void)detail::swallow{0, ((idx |= (usertype_detail::is_indexer(std::get<I * 2>(functions)) != 0)), 0)...};
return idx;
}
int finish_regs(regs_t& l, int& index) {
auto prop_fx = [&](meta_function mf) { return !properties[static_cast<int>(mf)]; };
usertype_detail::insert_default_registrations<T>(l, index, prop_fx);
if (destructfunc != nullptr) {
l[index] = luaL_Reg{to_string(meta_function::garbage_collect).c_str(), destructfunc};
++index;
}
return index;
}
template <std::size_t Idx, typename F>
void make_regs(regs_t&, int&, call_construction, F&&) {
callconstructfunc = call<Idx + 1>;
secondarymeta = true;
}
template <std::size_t, typename... Bases>
void make_regs(regs_t&, int&, base_classes_tag, bases<Bases...>) {
static_assert(!meta::any_same<T, Bases...>::value, "base classes cannot list the original class as part of the bases");
if (sizeof...(Bases) < 1) {
return;
}
mustindex = true;
(void)detail::swallow{0, ((detail::has_derived<Bases>::value = true), 0)...};
static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report.");
static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report.");
baseclasscheck = (void*)&detail::inheritance<T, Bases...>::type_check;
baseclasscast = (void*)&detail::inheritance<T, Bases...>::type_cast;
indexbaseclasspropogation = usertype_detail::walk_all_bases<true, Bases...>;
newindexbaseclasspropogation = usertype_detail::walk_all_bases<false, Bases...>;
}
template <std::size_t Idx, typename N, typename F, typename = std::enable_if_t<!meta::any_same<meta::unqualified_t<N>, base_classes_tag, call_construction>::value>>
void make_regs(regs_t& l, int& index, N&& n, F&&) {
if (is_variable_binding<meta::unqualified_t<F>>::value) {
return;
}
luaL_Reg reg = usertype_detail::make_reg(std::forward<N>(n), make_func<Idx>());
for (std::size_t i = 0; i < properties.size(); ++i) {
meta_function mf = static_cast<meta_function>(i);
const std::string& mfname = to_string(mf);
if (mfname == reg.name) {
switch (mf) {
case meta_function::construct:
if (properties[i]) {
#if !(defined(SOL_NO_EXCEPTIONS) && SOL_NO_EXCEPTIONS)
throw error("sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems");
#else
assert(false && "sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems");
#endif
}
break;
case meta_function::garbage_collect:
if (destructfunc != nullptr) {
#if !(defined(SOL_NO_EXCEPTIONS) && SOL_NO_EXCEPTIONS)
throw error("sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems");
#else
assert(false && "sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems");
#endif
}
destructfunc = reg.func;
return;
case meta_function::index:
indexfunc = reg.func;
mustindex = true;
properties.set(i);
return;
case meta_function::new_index:
newindexfunc = reg.func;
mustindex = true;
properties.set(i);
return;
default:
break;
}
properties.set(i);
break;
}
}
l[index] = reg;
++index;
}
template <typename... Args, typename = std::enable_if_t<sizeof...(Args) == sizeof...(Tn)>>
usertype_metatable(Args&&... args)
: usertype_metatable_core(&usertype_detail::indexing_fail<T, true>, &usertype_detail::metatable_new_index<T, false>), usertype_detail::registrar(), functions(std::forward<Args>(args)...), destructfunc(nullptr), callconstructfunc(nullptr), indexbase(&core_indexing_call<true>), newindexbase(&core_indexing_call<false>), indexbaseclasspropogation(usertype_detail::walk_all_bases<true>), newindexbaseclasspropogation(usertype_detail::walk_all_bases<false>), baseclasscheck(nullptr), baseclasscast(nullptr), secondarymeta(contains_variable()), properties() {
properties.reset();
std::initializer_list<typename usertype_detail::mapping_t::value_type> ilist{{std::pair<std::string, usertype_detail::call_information>(usertype_detail::make_string(std::get<I * 2>(functions)),
usertype_detail::call_information(&usertype_metatable::real_find_call<I * 2, I * 2 + 1, true>,
&usertype_metatable::real_find_call<I * 2, I * 2 + 1, false>))}...};
this->mapping.insert(ilist);
for (const auto& n : meta_function_names()) {
this->mapping.erase(n);
}
this->mustindex = contains_variable() || contains_index();
}
usertype_metatable(const usertype_metatable&) = default;
usertype_metatable(usertype_metatable&&) = default;
usertype_metatable& operator=(const usertype_metatable&) = default;
usertype_metatable& operator=(usertype_metatable&&) = default;
template <std::size_t I0, std::size_t I1, bool is_index>
static int real_find_call(lua_State* L, void* um, usertype_metatable_core&, int) {
auto& f = *static_cast<usertype_metatable*>(um);
if (is_variable_binding<decltype(std::get<I1>(f.functions))>::value) {
return real_call_with<I1, is_index, true>(L, f);
}
// set up upvalues
// for a chained call
int upvalues = 0;
upvalues += stack::push(L, nullptr);
upvalues += stack::push(L, light<usertype_metatable>(f));
auto cfunc = &call<I1, is_index>;
return stack::push(L, c_closure(cfunc, upvalues));
}
template <bool is_index>
static int real_meta_call(lua_State* L, void* um, int) {
auto& f = *static_cast<usertype_metatable*>(um);
return is_index ? f.indexfunc(L) : f.newindexfunc(L);
}
template <bool is_index, bool toplevel = false, bool is_meta_bound = false>
static int core_indexing_call(lua_State* L) {
usertype_metatable& f = toplevel
? static_cast<usertype_metatable&>(stack::get<light<usertype_metatable>>(L, upvalue_index(usertype_detail::metatable_index)))
: static_cast<usertype_metatable&>(stack::pop<user<usertype_metatable>>(L));
static const int keyidx = -2 + static_cast<int>(is_index);
if (toplevel && stack::get<type>(L, keyidx) != type::string) {
return is_index ? f.indexfunc(L) : f.newindexfunc(L);
}
int runtime_target = 0;
usertype_detail::member_search member = nullptr;
{
#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
string_view name = stack::get<string_view>(L, keyidx);
auto memberit = f.mapping.find(name, string_view_hash(), std::equal_to<string_view>());
#else
std::string name = stack::get<std::string>(L, keyidx);
auto memberit = f.mapping.find(name);
#endif
if (memberit != f.mapping.cend()) {
const usertype_detail::call_information& ci = memberit->second;
member = is_index ? ci.index : ci.new_index;
runtime_target = ci.runtime_target;
}
}
if (member != nullptr) {
return (member)(L, static_cast<void*>(&f), static_cast<usertype_metatable_core&>(f), runtime_target);
}
string_view accessor = stack::get<string_view>(L, keyidx);
int ret = 0;
bool found = false;
// Otherwise, we need to do propagating calls through the bases
if (is_index)
f.indexbaseclasspropogation(L, found, ret, accessor);
else
f.newindexbaseclasspropogation(L, found, ret, accessor);
if (found) {
return ret;
}
if (is_meta_bound) {
return is_index ? usertype_detail::indexing_fail<T, is_index>(L) : usertype_detail::metatable_new_index<T, false>(L);
}
return toplevel ? (is_index ? f.indexfunc(L) : f.newindexfunc(L)) : -1;
}
static int real_index_call(lua_State* L) {
return core_indexing_call<true, true>(L);
}
static int real_new_index_call(lua_State* L) {
return core_indexing_call<false, true>(L);
}
static int real_meta_index_call(lua_State* L) {
return core_indexing_call<true, true, true>(L);
}
static int real_meta_new_index_call(lua_State* L) {
return core_indexing_call<false, true, true>(L);
}
template <std::size_t Idx, bool is_index = true, bool is_variable = false>
static int real_call(lua_State* L) {
usertype_metatable& f = stack::get<light<usertype_metatable>>(L, upvalue_index(usertype_detail::metatable_index));
return real_call_with<Idx, is_index, is_variable>(L, f);
}
template <std::size_t Idx, bool is_index = true, bool is_variable = false>
static int real_call_with(lua_State* L, usertype_metatable& um) {
typedef meta::unqualified_tuple_element_t<Idx - 1, Tuple> K;
typedef meta::unqualified_tuple_element_t<Idx, Tuple> F;
static const int boost = !detail::is_non_factory_constructor<F>::value
&& std::is_same<K, call_construction>::value
? 1
: 0;
auto& f = std::get<Idx>(um.functions);
return call_detail::call_wrapped<T, is_index, is_variable, boost>(L, f);
}
template <std::size_t Idx, bool is_index = true, bool is_variable = false>
static int call(lua_State* L) {
return detail::typed_static_trampoline<decltype(&real_call<Idx, is_index, is_variable>), (&real_call<Idx, is_index, is_variable>)>(L);
}
template <std::size_t Idx, bool is_index = true, bool is_variable = false>
static int call_with(lua_State* L) {
return detail::typed_static_trampoline<decltype(&real_call_with<Idx, is_index, is_variable>), (&real_call_with<Idx, is_index, is_variable>)>(L);
}
static int index_call(lua_State* L) {
return detail::typed_static_trampoline<decltype(&real_index_call), (&real_index_call)>(L);
}
static int new_index_call(lua_State* L) {
return detail::typed_static_trampoline<decltype(&real_new_index_call), (&real_new_index_call)>(L);
}
static int meta_index_call(lua_State* L) {
return detail::typed_static_trampoline<decltype(&real_meta_index_call), (&real_meta_index_call)>(L);
}
static int meta_new_index_call(lua_State* L) {
return detail::typed_static_trampoline<decltype(&real_meta_new_index_call), (&real_meta_new_index_call)>(L);
}
virtual int push_um(lua_State* L) override {
return stack::push(L, std::move(*this));
}
~usertype_metatable() override {
}
};
namespace stack {
template <typename T, std::size_t... I, typename... Args>
struct pusher<usertype_metatable<T, std::index_sequence<I...>, Args...>> {
typedef usertype_metatable<T, std::index_sequence<I...>, Args...> umt_t;
typedef typename umt_t::regs_t regs_t;
static umt_t& make_cleanup(lua_State* L, umt_t&& umx) {
// ensure some sort of uniqueness
static int uniqueness = 0;
std::string uniquegcmetakey = usertype_traits<T>::user_gc_metatable();
// std::to_string doesn't exist in android still, with NDK, so this bullshit
// is necessary
// thanks, Android :v
int appended = snprintf(nullptr, 0, "%d", uniqueness);
std::size_t insertionpoint = uniquegcmetakey.length() - 1;
uniquegcmetakey.append(appended, '\0');
char* uniquetarget = &uniquegcmetakey[insertionpoint];
snprintf(uniquetarget, uniquegcmetakey.length(), "%d", uniqueness);
++uniqueness;
const char* gcmetakey = &usertype_traits<T>::gc_table()[0];
// Make sure userdata's memory is properly in lua first,
// otherwise all the light userdata we make later will become invalid
stack::push<user<umt_t>>(L, metatable_key, uniquegcmetakey, std::move(umx));
// Create the top level thing that will act as our deleter later on
stack_reference umt(L, -1);
stack::set_field<true>(L, gcmetakey, umt);
umt.pop();
stack::get_field<true>(L, gcmetakey);
umt_t& target_umt = stack::pop<user<umt_t>>(L);
return target_umt;
}
static int push(lua_State* L, umt_t&& umx) {
umt_t& um = make_cleanup(L, std::move(umx));
usertype_metatable_core& umc = um;
regs_t value_table{{}};
int lastreg = 0;
(void)detail::swallow{0, (um.template make_regs<(I * 2)>(value_table, lastreg, std::get<(I * 2)>(um.functions), std::get<(I * 2 + 1)>(um.functions)), 0)...};
um.finish_regs(value_table, lastreg);
value_table[lastreg] = {nullptr, nullptr};
regs_t ref_table = value_table;
regs_t unique_table = value_table;
bool hasdestructor = !value_table.empty() && to_string(meta_function::garbage_collect) == value_table[lastreg - 1].name;
if (hasdestructor) {
ref_table[lastreg - 1] = {nullptr, nullptr};
}
unique_table[lastreg - 1] = {value_table[lastreg - 1].name, detail::unique_destruct<T>};
lua_createtable(L, 0, 2);
stack_reference type_table(L, -1);
stack::set_field(L, "name", detail::demangle<T>(), type_table.stack_index());
stack::set_field(L, "is", &usertype_detail::is_check<T>, type_table.stack_index());
// Now use um
const bool& mustindex = umc.mustindex;
for (std::size_t i = 0; i < 3; ++i) {
// Pointer types, AKA "references" from C++
const char* metakey = nullptr;
luaL_Reg* metaregs = nullptr;
switch (i) {
case 0:
metakey = &usertype_traits<T*>::metatable()[0];
metaregs = ref_table.data();
break;
case 1:
metakey = &usertype_traits<detail::unique_usertype<T>>::metatable()[0];
metaregs = unique_table.data();
break;
case 2:
default:
metakey = &usertype_traits<T>::metatable()[0];
metaregs = value_table.data();
break;
}
luaL_newmetatable(L, metakey);
stack_reference t(L, -1);
stack::set_field(L, meta_function::type, type_table, t.stack_index());
int upvalues = 0;
upvalues += stack::push(L, nullptr);
upvalues += stack::push(L, make_light(um));
luaL_setfuncs(L, metaregs, upvalues);
if (um.baseclasscheck != nullptr) {
stack::set_field(L, detail::base_class_check_key(), um.baseclasscheck, t.stack_index());
}
if (um.baseclasscast != nullptr) {
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, nullptr, make_light(um), make_light(umc)), t.stack_index());
stack::set_field(L, detail::base_class_new_index_propogation_key(), make_closure(um.newindexbase, nullptr, make_light(um), make_light(umc)), t.stack_index());
if (mustindex) {
// Basic index pushing: specialize
// index and newindex to give variables and stuff
stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, nullptr, make_light(um), make_light(umc)), t.stack_index());
stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, nullptr, make_light(um), make_light(umc)), t.stack_index());
}
else {
// If there's only functions, we can use the fast index version
stack::set_field(L, meta_function::index, t, t.stack_index());
}
// metatable on the metatable
// for call constructor purposes and such
lua_createtable(L, 0, 3);
stack_reference metabehind(L, -1);
stack::set_field(L, meta_function::type, type_table, metabehind.stack_index());
if (um.callconstructfunc != nullptr) {
stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, nullptr, make_light(um), make_light(umc)), metabehind.stack_index());
}
if (um.secondarymeta) {
stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, nullptr, make_light(um), make_light(umc)), metabehind.stack_index());
stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, nullptr, make_light(um), make_light(umc)), metabehind.stack_index());
}
// type information needs to be present on the behind-tables too
stack::set_field(L, metatable_key, metabehind, t.stack_index());
metabehind.pop();
// We want to just leave the table
// in the registry only, otherwise we return it
t.pop();
}
// Now for the shim-table that actually gets assigned to the name
luaL_newmetatable(L, &usertype_traits<T>::user_metatable()[0]);
stack_reference t(L, -1);
stack::set_field(L, meta_function::type, type_table, t.stack_index());
int upvalues = 0;
upvalues += stack::push(L, nullptr);
upvalues += stack::push(L, make_light(um));
luaL_setfuncs(L, value_table.data(), upvalues);
{
lua_createtable(L, 0, 3);
stack_reference metabehind(L, -1);
// type information needs to be present on the behind-tables too
stack::set_field(L, meta_function::type, type_table, metabehind.stack_index());
if (um.callconstructfunc != nullptr) {
stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, nullptr, make_light(um), make_light(umc)), metabehind.stack_index());
}
stack::set_field(L, meta_function::index, make_closure(umt_t::meta_index_call, nullptr, make_light(um), make_light(umc), nullptr, usertype_detail::toplevel_magic), metabehind.stack_index());
stack::set_field(L, meta_function::new_index, make_closure(umt_t::meta_new_index_call, nullptr, make_light(um), make_light(umc), nullptr, usertype_detail::toplevel_magic), metabehind.stack_index());
stack::set_field(L, metatable_key, metabehind, t.stack_index());
metabehind.pop();
}
lua_remove(L, type_table.stack_index());
return 1;
}
};
} // namespace stack
} // namespace sol
#endif // SOL_USERTYPE_METATABLE_HPP

View File

@ -310,7 +310,7 @@ struct alignas(16) weird_aligned_wrapper {
}
void operator()(SelfType& self, sol::object param) const {
lambda(self, param.as<float>());
}
}
std::function<void(SelfType&, float)> lambda;
};
@ -1627,6 +1627,16 @@ TEST_CASE("usertype/runtime-extensibility", "Check if usertypes are runtime exte
};
int val = 0;
class base_a {
public:
int x;
};
class derived_b : public base_a {
};
SECTION("just functions") {
sol::state lua;
lua.open_libraries(sol::lib::base);
@ -1722,6 +1732,24 @@ end
val = lua["val"];
REQUIRE(val == 3);
}
SECTION("with bases") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<base_a>("A",
"x", &base_a::x //no crash without this
);
lua.new_usertype<derived_b>("B",
sol::base_classes, sol::bases<base_a>());
auto pfr0 = lua.safe_script("function A:c() print('A') return 1 end", sol::script_pass_on_error);
REQUIRE(pfr0.valid());
auto pfr1 = lua.safe_script("function B:c() print('B') return 2 end", sol::script_pass_on_error);
REQUIRE(pfr1.valid());
auto pfr2 = lua.safe_script("local obja = A.new() local objb = B.new() assert(obja:c() == 1) assert(objb:c() == 2)", sol::script_pass_on_error);
REQUIRE(pfr2.valid());
}
}
TEST_CASE("usertype/runtime-replacement", "ensure that functions can be properly replaced at runtime for non-indexed things") {