Many of the tests are still busted, but we're getting closer.

Closes #96
Closes #79
Fixes #105 - but we still need to double-check and add a test to prove it
This commit is contained in:
ThePhD 2016-06-06 15:46:53 -04:00
parent e35fe6be85
commit c458849d29
28 changed files with 2648 additions and 3331 deletions

View File

@ -44,6 +44,35 @@ namespace call_detail {
return f.write;
}
template <typename T, typename List>
struct void_call;
template <typename T, typename... Args>
struct void_call<T, types<Args...>> {
static void call(Args...) {}
};
template <typename T, int additional_pop = 0>
struct constructor_match {
T* obj;
constructor_match(T* obj) : obj(obj) {}
template <typename Fx, std::size_t I, typename... R, typename... Args>
int operator()(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) const {
detail::default_construct func{};
return stack::call_into_lua<additional_pop, false>(r, a, L, start, func, obj);
}
};
template <typename T>
inline int destruct(lua_State* L) {
T* obj = stack::get<non_null<T*>>(L, 1);
std::allocator<T> alloc{};
alloc.destroy(obj);
return 0;
}
namespace overload_detail {
template <std::size_t... M, typename Match, typename... Args>
inline int overload_match_arity(sol::types<>, std::index_sequence<>, std::index_sequence<M...>, Match&&, lua_State* L, int, int, Args&&...) {
@ -82,6 +111,39 @@ namespace call_detail {
return overload_match_arity<Functions...>(std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
}
template <typename T, typename... TypeLists, typename Match, typename... Args>
inline int construct(Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) {
// use same overload resolution matching as all other parts of the framework
return overload_match_arity<decltype(void_call<T, TypeLists>::call)...>(std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
}
template <typename T, typename... TypeLists>
inline int construct(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, meta, 1) : call_syntax::dot;
argcount -= static_cast<int>(syntax);
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
T*& referencepointer = *pointerpointer;
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
referencepointer = obj;
reference userdataref(L, -1);
userdataref.pop();
construct<T, TypeLists...>(constructor_match<T>(obj), L, argcount, 1 + static_cast<int>(syntax));
userdataref.push();
luaL_getmetatable(L, &meta[0]);
if (stack::get<type>(L) == type::nil) {
lua_pop(L, 1);
return luaL_error(L, "sol: unable to get usertype metatable");
}
lua_setmetatable(L, -2);
return 1;
}
template <typename F, bool is_index, bool is_variable, typename = void>
struct agnostic_lua_call_wrapper {
static int var_call(std::true_type, lua_State* L, F f) {
@ -142,9 +204,9 @@ namespace call_detail {
object_type* o = stack::get<object_type*>(L, 1);
if (o == nullptr) {
if (is_variable) {
return luaL_error(L, "sol: received null for 'self' argument (bad '.' access?)");
return luaL_error(L, "sol: received nil for 'self' argument (bad '.' access?)");
}
return luaL_error(L, "sol: received null for 'self' argument (pass 'self' as first argument)");
return luaL_error(L, "sol: received nil for 'self' argument (pass 'self' as first argument)");
}
return stack::call_into_lua<is_variable ? 2 : 1>(types<void>(), args_list(), L, is_variable ? 3 : 2, wrap::caller(), f, *o);
#else
@ -183,9 +245,9 @@ namespace call_detail {
object_type* o = stack::get<object_type*>(L, 1);
if (o == nullptr) {
if (is_variable) {
return luaL_error(L, "sol: 'self' argument is nullptr (bad '.' access?)");
return luaL_error(L, "sol: 'self' argument is nil (bad '.' access?)");
}
return luaL_error(L, "sol: 'self' argument is nullptr (pass 'self' as first argument)");
return luaL_error(L, "sol: 'self' argument is nil (pass 'self' as first argument)");
}
return stack::call_into_lua<is_variable ? 2 : 1>(returns_list(), types<>(), L, is_variable ? 3 : 2, wrap::caller(), f, *o);
#else
@ -212,9 +274,17 @@ namespace call_detail {
};
template <bool is_index, bool is_variable, typename C>
struct agnostic_lua_call_wrapper<sol::no_construction, is_index, is_variable, C> {
static int call(lua_State* L, sol::no_construction&) {
return luaL_error(L, "cannot call something tagged with 'no construction'");
struct agnostic_lua_call_wrapper<no_construction, is_index, is_variable, C> {
static int call(lua_State* L, no_construction&) {
return luaL_error(L, "sol: cannot call this constructor (tagged as non-constructible)");
}
};
template <typename... Args, bool is_index, bool is_variable, typename C>
struct agnostic_lua_call_wrapper<bases<Args...>, is_index, is_variable, C> {
static int call(lua_State* L, bases<Args...>&) {
// Uh. How did you even call this, lul
return 0;
}
};
@ -226,22 +296,21 @@ namespace call_detail {
typedef sol::constructor_list<Args...> F;
static int call(lua_State* L, F&) {
static const auto& meta = usertype_traits<T>::metatable;
static const auto& metakey = usertype_traits<T>::metatable;
int argcount = lua_gettop(L);
call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, meta, 1) : call_syntax::dot;
call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, metakey, 1) : call_syntax::dot;
argcount -= static_cast<int>(syntax);
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
reference userdataref(L, -1);
T*& referencepointer = *pointerpointer;
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
referencepointer = obj;
reference userdataref(L, -1);
userdataref.pop();
function_detail::construct<T, Args...>(detail::constructor_match<T>(obj), L, argcount, 1 + static_cast<int>(syntax));
construct<T, Args...>(constructor_match<T, 1>(obj), L, argcount, 1 + static_cast<int>(syntax));
userdataref.push();
luaL_getmetatable(L, &meta[0]);
luaL_getmetatable(L, &metakey[0]);
if (stack::get<type>(L) == type::nil) {
lua_pop(L, 1);
return luaL_error(L, "sol: unable to get usertype metatable");
@ -252,6 +321,65 @@ namespace call_detail {
}
};
template <typename T, typename... Cxs, bool is_index, bool is_variable, typename C>
struct lua_call_wrapper<T, sol::constructor_wrapper<Cxs...>, is_index, is_variable, C> {
typedef sol::constructor_wrapper<Cxs...> F;
struct matchfx {
template <typename Fx, std::size_t I, typename... R, typename... Args>
int operator()(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start, F& f) {
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
reference userdataref(L, -1);
T*& referencepointer = *pointerpointer;
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
referencepointer = obj;
auto& func = std::get<I>(f.set);
stack::call_into_lua<1, false>(r, a, L, start, func, detail::implicit_wrapper<T>(obj));
userdataref.push();
luaL_getmetatable(L, &usertype_traits<T>::metatable[0]);
if (stack::get<type>(L) == type::nil) {
lua_pop(L, 1);
std::string err = "sol: unable to get usertype metatable for ";
err += usertype_traits<T>::name;
return luaL_error(L, err.c_str());
}
lua_setmetatable(L, -2);
return 1;
}
};
static int call(lua_State* L, F& f) {
call_syntax syntax = stack::get_call_syntax(L, usertype_traits<T>::metatable);
int syntaxval = static_cast<int>(syntax);
int argcount = lua_gettop(L) - syntaxval;
return construct<T, meta::pop_front_type_t<meta::function_args_t<Cxs>>...>(matchfx{}, L, argcount, 1 + syntaxval, f);
}
};
template <typename T, typename Fx, bool is_index, bool is_variable>
struct lua_call_wrapper<T, sol::destructor_wrapper<Fx>, is_index, is_variable, std::enable_if_t<std::is_void<Fx>::value>> {
typedef sol::destructor_wrapper<Fx> F;
static int call(lua_State* L, F&) {
return destruct<T>(L);
}
};
template <typename T, typename Fx, bool is_index, bool is_variable>
struct lua_call_wrapper<T, sol::destructor_wrapper<Fx>, is_index, is_variable, std::enable_if_t<!std::is_void<Fx>::value>> {
typedef sol::destructor_wrapper<Fx> F;
static int call(lua_State* L, F& f) {
T* obj = stack::get<non_null<T*>>(L);
f.fx(detail::implicit_wrapper<T>(obj));
return 0;
}
};
template <typename T, bool is_index, bool is_variable, typename Fx>
int call_wrapped(lua_State* L, Fx&& fx) {
return lua_call_wrapper<T, meta::unqualified_t<Fx>, is_index, is_variable>{}.call(L, std::forward<Fx>(fx));

View File

@ -25,11 +25,8 @@
#include "function_types_core.hpp"
#include "function_types_templated.hpp"
#include "function_types_basic.hpp"
#include "function_types_allocator.hpp"
#include "function_types_member.hpp"
#include "function_types_usertype.hpp"
#include "function_types_overload.hpp"
#include "function_types_allocator.hpp"
#include "function_types_overloaded.hpp"
#include "resolve.hpp"
#include "call.hpp"

View File

@ -1,145 +0,0 @@
// The MIT License (MIT)
// Copyright (c) 2013-2016 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_FUNCTION_TYPES_ALLOCATOR_HPP
#define SOL_FUNCTION_TYPES_ALLOCATOR_HPP
#include "raii.hpp"
#include "stack.hpp"
#include "function_types_overload.hpp"
namespace sol {
namespace detail {
template <typename T, typename List>
struct void_call;
template <typename T, typename... Args>
struct void_call<T, types<Args...>> {
static void call(Args...) {}
};
template <typename T>
struct constructor_match {
T* obj;
constructor_match(T* obj) : obj(obj) {}
template <typename Fx, std::size_t I, typename... R, typename... Args>
int operator()(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) const {
default_construct func{};
return stack::call_into_lua<0, false>(r, a, L, start, func, obj);
}
};
} // detail
namespace function_detail {
template <typename T, typename... TypeLists, typename Match>
inline int construct(Match&& matchfx, lua_State* L, int fxarity, int start) {
// use same overload resolution matching as all other parts of the framework
return overload_match_arity<decltype(detail::void_call<T, TypeLists>::call)...>(std::forward<Match>(matchfx), L, fxarity, start);
}
template <typename T, typename... TypeLists>
inline int construct(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, meta, 1) : call_syntax::dot;
argcount -= static_cast<int>(syntax);
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
T*& referencepointer = *pointerpointer;
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
referencepointer = obj;
reference userdataref(L, -1);
userdataref.pop();
construct<T, TypeLists...>(detail::constructor_match<T>(obj), L, argcount, 1 + static_cast<int>(syntax));
userdataref.push();
luaL_getmetatable(L, &meta[0]);
if (stack::get<type>(L) == type::nil) {
lua_pop(L, 1);
return luaL_error(L, "sol: unable to get usertype metatable");
}
lua_setmetatable(L, -2);
return 1;
}
template <typename T>
inline int destruct(lua_State* L) {
T* obj = stack::get<non_null<T*>>(L, 1);
std::allocator<T> alloc{};
alloc.destroy(obj);
return 0;
}
template <typename T, typename... Functions>
struct usertype_constructor_function : base_function {
typedef std::tuple<Functions...> overload_list;
typedef std::make_index_sequence<sizeof...(Functions)> indices;
overload_list overloads;
usertype_constructor_function(overload_list set) : overloads(std::move(set)) {}
template <std::size_t... I>
usertype_constructor_function(std::index_sequence<I...>, constructor_wrapper<Functions...> set) : usertype_constructor_function(detail::forward_get<I>(set.set)...) {}
usertype_constructor_function(Functions... fxs) : overloads(fxs...) {}
template <typename Fx, std::size_t I, typename... R, typename... Args>
int call(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) {
static const auto& meta = usertype_traits<T>::metatable;
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
T*& referencepointer = *pointerpointer;
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
referencepointer = obj;
reference userdataref(L, -1);
userdataref.pop();
auto& func = std::get<I>(overloads);
stack::call_into_lua<false>(r, a, L, start, func, function_detail::implicit_wrapper<T>(obj));
userdataref.push();
luaL_getmetatable(L, &meta[0]);
if (stack::get<type>(L) == type::nil) {
lua_pop(L, 1);
std::string err = "sol: unable to get usertype metatable for ";
err += usertype_traits<T>::name;
return luaL_error(L, err.c_str());
}
lua_setmetatable(L, -2);
return 1;
}
virtual int operator()(lua_State* L) override {
static const auto& meta = usertype_traits<T>::metatable;
call_syntax syntax = stack::get_call_syntax(L, meta);
int argcount = lua_gettop(L) - static_cast<int>(syntax);
auto mfx = [&](auto&&... args) { return this->call(std::forward<decltype(args)>(args)...); };
return construct<T, meta::pop_front_type_t<meta::function_args_t<Functions>>...>(mfx, L, argcount, 1 + static_cast<int>(syntax));
}
};
} // function_detail
} // sol
#endif // SOL_FUNCTION_TYPES_ALLOCATOR_HPP

View File

@ -27,289 +27,12 @@
#include <memory>
namespace sol {
namespace detail {
struct empty {};
}
template <typename RSig = void, typename WSig = void>
struct member_property {
typedef std::conditional_t<std::is_void<RSig>::value, detail::empty, RSig> R;
typedef std::conditional_t<std::is_void<WSig>::value, detail::empty, WSig> W;
R read;
W write;
member_property(R read, W write) : read(std::move(read)), write(std::move(write)) {}
template <typename T, typename Arg>
void write_if(std::true_type, T& mem, Arg&& arg) {
write(mem, arg);
}
template <typename T, typename Arg>
void write_if(std::false_type, T&, Arg&&) {
// This is a fatal error if we get here...
// Should never happen but...
// Crash horrifically, for safety?
std::abort();
}
template <typename T, typename Arg>
void operator()(T& mem, Arg&& arg) {
write_if(meta::neg<std::is_void<WSig>>(), mem, arg);
}
template <typename T>
decltype(auto) read_if(std::true_type, T& mem) {
return read(mem);
}
template <typename T>
decltype(auto) read_if(std::false_type, T&) {
typedef typename meta::bind_traits<WSig>::template arg_at<1> Arg;
typedef std::add_pointer_t<std::remove_reference_t<Arg>> pret;
// This is a fatal error if we get here...
// Should never happen but...
// Crash horrifically, for safety?
std::abort();
return *pret();
}
template <typename T>
decltype(auto) operator()(T& mem) {
return read_if(meta::neg<std::is_void<RSig>>(), mem);
}
};
template <typename R, typename T>
inline decltype(auto) property( R(T::* readfunc )() const) {
auto rf = [readfunc](T& mem) -> R {return (mem.*readfunc)();};
return member_property<decltype(rf)>(std::move(rf), detail::empty());
}
template <typename R, typename T>
inline decltype(auto) property( R(T::* readfunc )()) {
auto rf = [readfunc](T& mem) -> R {return (mem.*readfunc)();};
return member_property<decltype(rf)>(std::move(rf), detail::empty());
}
template <typename R, typename T, typename Arg>
inline decltype(auto) property(R(T::* writefunc)(Arg)) {
auto wf = [writefunc](T& mem, Arg arg) {(mem.*writefunc)(std::forward<Arg>(arg));};
return member_property<void, decltype(wf)>(detail::empty(), std::move(wf));
}
template <typename R, typename T, typename Arg>
inline decltype(auto) property(R(T::* writefunc)(Arg) const) {
auto wf = [writefunc](T& mem, Arg arg) {(mem.*writefunc)(std::forward<Arg>(arg));};
return member_property<void, decltype(wf)>(detail::empty(), std::move(wf));
}
template <typename RR, typename RT, typename WR, typename WT, typename Arg>
inline decltype(auto) property(RR(RT::* readfunc)(), WR(WT::* writefunc)(Arg)) {
auto rf = [readfunc](RT& mem) -> RR {return (mem.*readfunc)();};
auto wf = [writefunc](WT& mem, Arg arg) {(mem.*writefunc)(std::forward<Arg>(arg));};
return member_property<decltype(rf), decltype(wf)>(std::move(rf), std::move(wf));
}
template <typename RR, typename RT, typename WR, typename WT, typename Arg>
inline decltype(auto) property(RR(RT::* readfunc)() const, WR(WT::* writefunc)(Arg)) {
auto rf = [readfunc](RT& mem) -> RR {return (mem.*readfunc)();};
auto wf = [writefunc](WT& mem, Arg arg) {(mem.*writefunc)(std::forward<Arg>(arg));};
return member_property<decltype(rf), decltype(wf)>(std::move(rf), std::move(wf));
}
template <typename RR, typename RT, typename WR, typename WT, typename Arg>
inline decltype(auto) property(RR(RT::* readfunc)(), WR(WT::* writefunc)(Arg) const) {
auto rf = [readfunc](RT& mem) -> RR {return (mem.*readfunc)();};
auto wf = [writefunc](WT& mem, Arg arg) {(mem.*writefunc)(std::forward<Arg>(arg));};
return member_property<decltype(rf), decltype(wf)>(std::move(rf), std::move(wf));
}
template <typename RR, typename RT, typename WR, typename WT, typename Arg>
inline decltype(auto) property(RR(RT::* readfunc)() const, WR(WT::* writefunc)(Arg) const) {
auto rf = [readfunc](RT& mem) -> RR {return (mem.*readfunc)();};
auto wf = [writefunc](WT& mem, Arg arg) {(mem.*writefunc)(std::forward<Arg>(arg));};
return member_property<decltype(rf), decltype(wf)>(std::move(rf), std::move(wf));
}
namespace function_detail {
template <typename T>
struct implicit_wrapper {
T& item;
implicit_wrapper(T* item) : item(*item) {}
implicit_wrapper(T& item) : item(item) {}
operator T& () {
return item;
}
operator T* () {
return std::addressof(item);
}
};
inline decltype(auto) cleanup_key() {
const auto& name = u8"sol.ƒ.♲.🗑.(/¯◡ ‿ ◡)/¯ ~ ┻━┻ (ノ◕ヮ◕)ノ*:・゚✧";
return name;
}
template<typename T, typename Func, typename = void>
struct functor {
typedef meta::bind_traits<Func> traits_type;
typedef typename traits_type::args_list args_list;
typedef typename traits_type::return_type return_type;
typedef std::conditional_t<std::is_pointer<Func>::value || std::is_class<Func>::value, Func, std::add_pointer_t<Func>> function_type;
static const std::size_t arity = traits_type::arity;
static const bool is_free = true;
function_type invocation;
template<typename... Args>
functor(Args&&... args): invocation(std::forward<Args>(args)...) {}
template<typename... Args>
void call(types<void>, Args&&... args) {
invocation(std::forward<Args>(args)...);
}
template<typename Ret, typename... Args>
Ret call(types<Ret>, Args&&... args) {
return invocation(std::forward<Args>(args)...);
}
template<typename... Args>
auto operator()(Args&&... args) -> decltype(std::declval<functor>().call(types<return_type>{}, std::forward<Args>(args)...)) {
return this->call(types<return_type>(), std::forward<Args>(args)...);
}
};
template<typename T, typename Func>
struct functor<T, Func, std::enable_if_t<!std::is_member_pointer<Func>::value && std::is_base_of<T, meta::unqualified_t<typename meta::bind_traits<Func>::template arg_at<0>>>::value>> {
typedef meta::bind_traits<Func> traits_type;
typedef meta::pop_front_type_t<typename traits_type::args_list> args_list;
typedef typename traits_type::return_type return_type;
typedef std::conditional_t<std::is_pointer<Func>::value || std::is_class<Func>::value, Func, std::add_pointer_t<Func>> function_type;
static const std::size_t arity = traits_type::arity - 1;
static const bool is_free = false;
T* item;
function_type invocation;
template<typename... Args>
functor(Args&&... args): item(nullptr), invocation(std::forward<Args>(args)...) {}
template<typename... Args>
void call(types<void>, Args&&... args) {
T& member = *item;
invocation(implicit_wrapper<T>(member), std::forward<Args>(args)...);
}
template<typename Ret, typename... Args>
Ret call(types<Ret>, Args&&... args) {
T& member = *item;
return invocation(implicit_wrapper<T>(member), std::forward<Args>(args)...);
}
template<typename... Args>
auto operator()(Args&&... args) -> decltype(std::declval<functor>().call(types<return_type>{}, std::forward<Args>(args)...)) {
return this->call(types<return_type>(), std::forward<Args>(args)...);
}
};
template<typename T, typename RSig, typename WSig, typename C>
struct functor<T, member_property<RSig, WSig>, C> {
typedef meta::bind_traits<std::conditional_t<std::is_void<WSig>::value, RSig, WSig>> traits_type;
typedef meta::pop_front_type_t<typename traits_type::args_list> args_list;
typedef std::conditional_t<std::is_void<typename traits_type::return_type>::value, typename traits_type::template arg_at<0>, typename traits_type::return_type> return_type;
typedef member_property<RSig, WSig> function_type;
typedef meta::neg<std::is_void<RSig>> can_read;
typedef meta::neg<std::is_void<WSig>> can_write;
static const bool is_free = false;
T* item;
function_type invocation;
template<typename... Args>
functor(Args&&... args): item(nullptr), invocation(std::forward<Args>(args)...) {}
template<typename Arg>
void call(Arg&& arg) {
T& member = *item;
invocation(member, std::forward<Arg>(arg));
}
decltype(auto) call() {
T& member = *item;
return invocation(member);
}
template<typename... Args>
decltype(auto) operator()(Args&&... args) {
return this->call(std::forward<Args>(args)...);
}
};
template<typename T, typename Func>
struct functor<T, Func, std::enable_if_t<std::is_member_object_pointer<Func>::value>> {
typedef meta::bind_traits<Func> traits_type;
typedef typename traits_type::args_list args_list;
typedef typename traits_type::return_type return_type;
static const std::size_t arity = traits_type::arity;
typedef std::true_type can_read;
typedef std::true_type can_write;
static const bool is_free = false;
T* item;
Func invocation;
template<typename... Args>
functor(Args&&... args): item(nullptr), invocation(std::forward<Args>(args)...) {}
template<typename Arg>
void call(Arg&& arg) {
T& member = *item;
(member.*invocation) = std::forward<Arg>(arg);
}
return_type& call() {
T& member = *item;
return (member.*invocation);
}
template<typename... Args>
decltype(auto) operator()(Args&&... args) {
return this->call(std::forward<Args>(args)...);
}
};
template<typename T, typename Func>
struct functor<T, Func, std::enable_if_t<std::is_member_function_pointer<Func>::value>> {
typedef meta::bind_traits<Func> traits_type;
typedef typename traits_type::args_list args_list;
typedef typename traits_type::return_type return_type;
static const std::size_t arity = traits_type::arity;
static const bool is_free = false;
T* item;
Func invocation;
template<typename... Args>
functor(Args&&... args): item(nullptr), invocation(std::forward<Args>(args)...) {}
template<typename... Args>
void call(types<void>, Args&&... args) {
T& member = *item;
(member.*invocation)(std::forward<Args>(args)...);
}
template<typename Ret, typename... Args>
Ret call(types<Ret>, Args&&... args) {
T& member = *item;
return (member.*invocation)(std::forward<Args>(args)...);
}
template<typename... Args>
decltype(auto) operator()(Args&&... args) {
return this->call(types<return_type>{}, std::forward<Args>(args)...);
}
};
struct base_function {
virtual int operator()(lua_State* L) {
return luaL_error(L, "sol: failure to call specialized wrapped C++ function from Lua");

View File

@ -1,142 +0,0 @@
// The MIT License (MIT)
// Copyright (c) 2013-2016 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_FUNCTION_TYPES_OVERLOAD_HPP
#define SOL_FUNCTION_TYPES_OVERLOAD_HPP
#include "overload.hpp"
#include "function_types_core.hpp"
#include "function_types_usertype.hpp"
namespace sol {
namespace function_detail {
namespace internals {
template <typename T>
struct overload_traits : lua_bind_traits<T> {
static const std::size_t boost = 0;
};
template <typename T, typename Func, typename X>
struct overload_traits<functor<T, Func, X>> {
typedef typename functor<T, Func, X>::args_list args_list;
typedef typename functor<T, Func, X>::return_type return_type;
static const std::size_t arity = functor<T, Func, X>::arity;
static const std::size_t boost = static_cast<std::size_t>(functor<T, Func, X>::is_free);
};
template <std::size_t... M, typename Match, typename... Args>
inline int overload_match_arity(types<>, std::index_sequence<>, std::index_sequence<M...>, Match&&, lua_State* L, int, int, Args&&...) {
return luaL_error(L, "sol: no matching function call takes this number of arguments and the specified types");
}
template <typename Fx, typename... Fxs, std::size_t I, std::size_t... In, std::size_t... M, typename Match, typename... Args>
inline int overload_match_arity(types<Fx, Fxs...>, std::index_sequence<I, In...>, std::index_sequence<M...>, Match&& matchfx, lua_State* L, int nfxarity, int start, Args&&... args) {
typedef overload_traits<meta::unqualified_t<Fx>> traits;
typedef meta::tuple_types<typename traits::return_type> return_types;
typedef typename traits::args_list args_list;
typedef typename args_list::indices args_indices;
int fxarity = traits::boost + nfxarity;
// compile-time eliminate any functions that we know ahead of time are of improper arity
if (meta::find_in_pack_v<index_value<traits::arity>, index_value<M>...>::value) {
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), std::forward<Match>(matchfx), L, nfxarity, start, std::forward<Args>(args)...);
}
if (traits::arity != fxarity) {
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<traits::arity, M...>(), std::forward<Match>(matchfx), L, nfxarity, start, std::forward<Args>(args)...);
}
if (!stack::stack_detail::check_types<true>().check(args_list(), args_indices(), L, start - traits::boost, no_panic)) {
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), std::forward<Match>(matchfx), L, nfxarity, start, std::forward<Args>(args)...);
}
return matchfx(types<Fx>(), index_value<I>(), return_types(), args_list(), L, fxarity, start, std::forward<Args>(args)...);
}
} // internals
template <typename... Functions, typename Match, typename... Args>
inline int overload_match_arity(Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) {
return internals::overload_match_arity(types<Functions...>(), std::make_index_sequence<sizeof...(Functions)>(), std::index_sequence<>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
}
template <typename... Functions, typename Match, typename... Args>
inline int overload_match(Match&& matchfx, lua_State* L, int start, Args&&... args) {
int fxarity = lua_gettop(L) - (start - 1);
return overload_match_arity<Functions...>(std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
}
template <typename... Functions>
struct overloaded_function : base_function {
typedef std::tuple<Functions...> overload_list;
typedef std::make_index_sequence<sizeof...(Functions)> indices;
overload_list overloads;
overloaded_function(overload_list set)
: overloads(std::move(set)) {}
overloaded_function(Functions... fxs)
: overloads(fxs...) {
}
template <typename Fx, std::size_t I, typename... R, typename... Args>
int call(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) {
auto& func = std::get<I>(overloads);
return stack::call_into_lua<0, false>(r, a, L, start, func);
}
virtual int operator()(lua_State* L) override {
auto mfx = [&](auto&&... args){ return this->call(std::forward<decltype(args)>(args)...); };
return overload_match<Functions...>(mfx, L, 1);
}
};
template <typename T, typename... Functions>
struct usertype_overloaded_function : base_function {
typedef std::tuple<functor<T, std::remove_pointer_t<std::decay_t<Functions>>>...> overload_list;
typedef std::make_index_sequence<sizeof...(Functions)> indices;
overload_list overloads;
usertype_overloaded_function(std::tuple<Functions...> set) : overloads(std::move(set)) {}
template <typename Fx, std::size_t I, typename... R, typename... Args, meta::disable<meta::boolean<Fx::is_free>> = meta::enabler>
int call(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) {
auto& func = std::get<I>(overloads);
func.item = stack::get<meta::unwrapped_t<T>*>(L, 1);
#ifdef SOL_SAFE_USERTYPE
if (func.item == nullptr) {
return luaL_error(L, "sol: received null for 'self' argument (use ':' for accessing member functions)");
}
#endif // Safety
return stack::call_into_lua<0, false>(r, a, L, start, func);
}
template <typename Fx, std::size_t I, typename... R, typename... Args, meta::enable<meta::boolean<Fx::is_free>> = meta::enabler>
int call(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) {
auto& func = std::get<I>(overloads);
return stack::call_into_lua<0, false>(r, a, L, start - 1, func);
}
virtual int operator()(lua_State* L) override {
auto mfx = [&](auto&&... args){ return this->call(std::forward<decltype(args)>(args)...); };
return overload_match<functor<T, std::remove_pointer_t<std::decay_t<Functions>>>...>(mfx, L, 2);
}
};
} // function_detail
} // sol
#endif // SOL_FUNCTION_TYPES_OVERLOAD_HPP

View File

@ -0,0 +1,59 @@
// The MIT License (MIT)
// Copyright (c) 2013-2016 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_FUNCTION_TYPES_OVERLOAD_HPP
#define SOL_FUNCTION_TYPES_OVERLOAD_HPP
#include "overload.hpp"
#include "call.hpp"
#include "function_types_core.hpp"
namespace sol {
namespace function_detail {
template <typename... Functions>
struct overloaded_function : base_function {
typedef std::tuple<Functions...> overload_list;
typedef std::make_index_sequence<sizeof...(Functions)> indices;
overload_list overloads;
overloaded_function(overload_list set)
: overloads(std::move(set)) {}
overloaded_function(Functions... fxs)
: overloads(fxs...) {
}
template <typename Fx, std::size_t I, typename... R, typename... Args>
int call(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) {
auto& func = std::get<I>(overloads);
return stack::call_into_lua<0, false>(r, a, L, start, func);
}
virtual int operator()(lua_State* L) override {
auto mfx = [&](auto&&... args){ return this->call(std::forward<decltype(args)>(args)...); };
return call_detail::overload_match<Functions...>(mfx, L, 1);
}
};
} // function_detail
} // sol
#endif // SOL_FUNCTION_TYPES_OVERLOAD_HPP

View File

@ -1,201 +0,0 @@
// The MIT License (MIT)
// Copyright (c) 2013-2016 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_FUNCTION_TYPES_USERTYPE_HPP
#define SOL_FUNCTION_TYPES_USERTYPE_HPP
#include "overload.hpp"
#include "function_types_core.hpp"
#include <map>
namespace sol {
namespace function_detail {
template<typename Tp, typename Function>
struct usertype_function_core : public base_function {
typedef std::remove_pointer_t<Tp> T;
typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
typedef functor<T, function_type> fx_t;
typedef typename fx_t::traits_type traits_type;
typedef typename fx_t::args_list args_list;
typedef typename fx_t::return_type return_type;
fx_t fx;
template<typename... Args>
usertype_function_core(Args&&... args): fx(std::forward<Args>(args)...) {}
template<typename... Ret, typename... Args, std::size_t Start>
int operator()(types<Ret...> tr, types<Args...> ta, index_value<Start>, lua_State* L) {
return stack::call_into_lua<1>(tr, ta, L, static_cast<int>(Start), fx);
}
};
template<typename Tp, typename Function>
struct usertype_function : public usertype_function_core<Tp, Function> {
typedef usertype_function_core<Tp, Function> base_t;
typedef std::remove_pointer_t<Tp> T;
typedef typename base_t::traits_type traits_type;
typedef typename base_t::args_list args_list;
typedef typename base_t::return_type return_type;
template<typename... Args>
usertype_function(Args&&... args): base_t(std::forward<Args>(args)...) {}
int prelude(lua_State* L) {
this->fx.item = stack::get<meta::unwrapped_t<T>*>(L, 1);
#ifdef SOL_SAFE_USERTYPE
if (this->fx.item == nullptr) {
return luaL_error(L, "sol: received null for 'self' argument (use ':' for accessing member functions)");
}
#endif // Safety
return static_cast<base_t&>(*this)(meta::tuple_types<return_type>(), args_list(), index_value<2>(), L);
}
virtual int operator()(lua_State* L) override {
return prelude(L);
}
};
template<typename Tp, typename Function>
struct usertype_variable_function : public usertype_function_core<Tp, Function> {
typedef usertype_function_core<Tp, Function> base_t;
typedef std::remove_pointer_t<Tp> T;
typedef typename base_t::fx_t fx_t;
typedef typename base_t::traits_type traits_type;
typedef typename base_t::args_list args_list;
typedef typename base_t::return_type return_type;
typedef typename fx_t::can_read can_read;
typedef typename fx_t::can_write can_write;
template<typename... Args>
usertype_variable_function(Args&&... args): base_t(std::forward<Args>(args)...) {}
int set_assignable(std::false_type, lua_State* L) {
lua_pop(L, 3);
return luaL_error(L, "sol: cannot write to this type: copy assignment/constructor not available");
}
int set_assignable(std::true_type, lua_State* L) {
return set_writable(can_write(), L);
}
int set_writable(std::false_type, lua_State* L) {
lua_pop(L, 3);
return luaL_error(L, "sol: cannot write to readonly variable");
}
int set_writable(std::true_type, lua_State* L) {
return static_cast<base_t&>(*this)(meta::tuple_types<void>(), args_list(), index_value<3>(), L);
}
int set_variable(std::false_type, lua_State* L) {
lua_pop(L, 3);
return luaL_error(L, "sol: cannot write to a const variable");
}
int set_variable(std::true_type, lua_State* L) {
return set_assignable(std::is_assignable<std::add_lvalue_reference_t<return_type>, return_type>(), L);
}
int get_variable(std::false_type, lua_State* L) {
lua_pop(L, 2);
return luaL_error(L, "sol: cannot read from a readonly property");
}
int get_variable(std::true_type, lua_State* L) {
return static_cast<base_t&>(*this)(meta::tuple_types<return_type>(), types<>(), index_value<2>(), L);
}
int prelude(lua_State* L) {
int argcount = lua_gettop(L);
this->fx.item = stack::get<T*>(L, 1);
#ifdef SOL_SAFE_USERTYPE
if (this->fx.item == nullptr) {
return luaL_error(L, "sol: received null for 'self' argument (use ':' for accessing member functions)");
}
#endif // Safety
switch(argcount) {
case 2:
return get_variable(can_read(), L);
case 3:
return set_variable(meta::neg<std::is_const<return_type>>(), L);
default:
return luaL_error(L, "sol: cannot get/set userdata member variable with inappropriate number of arguments");
}
}
virtual int operator()(lua_State* L) override {
return prelude(L);
}
};
struct usertype_indexing_function : base_function {
std::string name;
base_function* original;
std::map<std::string, std::pair<bool, base_function*>> functions;
template<typename... Args>
usertype_indexing_function(std::string name, base_function* original, Args&&... args): name(std::move(name)), original(original), functions(std::forward<Args>(args)...) {}
int prelude(lua_State* L) {
const char* accessor = stack::get<const char*>(L, 1 - lua_gettop(L));
auto functionpair = functions.find(accessor);
if (functionpair != functions.end()) {
std::pair<bool, base_function*>& target = functionpair->second;
if (target.first) {
stack::push<light_userdata_value>(L, target.second);
stack::push(L, c_closure(usertype_call<0>, 1));
return 1;
}
return (*target.second)(L);
}
base_function& core = *original;
return core(L);
}
virtual int operator()(lua_State* L) override {
return prelude(L);
}
};
struct fail_on_error : base_function {
int prelude(lua_State* L) {
const char* accessor = stack::get<const char*>(L, 1 - lua_gettop(L));
return luaL_error(L, "sol: attempt to index nil value \"%s\" on userdata (bad (mispelled?) key name or does not exist)", accessor);
}
virtual int operator()(lua_State* L) override {
return prelude(L);
}
~fail_on_error() {
}
};
inline fail_on_error& failure_on_error() {
static fail_on_error f{};
return f;
}
} // function_detail
} // sol
#endif // SOL_FUNCTION_TYPES_USERTYPE_HPP

View File

@ -167,7 +167,7 @@ inline int call_lua(lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) {
typedef lua_bind_traits<meta::unqualified_t<Fx>> traits_type;
typedef typename traits_type::args_list args_list;
typedef typename traits_type::returns_list returns_list;
return call_into_lua(returns_list(), args_list(), start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...)
return call_into_lua(returns_list(), args_list(), start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
}
inline call_syntax get_call_syntax(lua_State* L, const std::string& key, int index = -2) {

View File

@ -97,8 +97,8 @@ struct checker {
}
};
template <type expected, typename C>
struct checker<type, expected, C> {
template <typename C>
struct checker<type, type::none, C> {
template <typename Handler>
static bool check (lua_State*, int, Handler&&) {
return true;

View File

@ -65,7 +65,7 @@ struct check_getter<T, std::enable_if_t<std::is_integral<T>::value && lua_type_o
};
template <typename T>
struct check_getter<T, std::enable_if_t<std::is_enum<T>::value>> {
struct check_getter<T, std::enable_if_t<std::is_enum<T>::value && !meta::any_same<T, meta_function, type>::value>> {
template <typename Handler>
static optional<T> get(lua_State* L, int index, Handler&& handler) {
int isnum = 0;

View File

@ -55,11 +55,11 @@ inline int unique_destruct(lua_State* L) {
} // detail
namespace stack {
template<typename T, bool global = false, typename = void>
template<typename T, bool global = false, bool raw = false, typename = void>
struct field_getter;
template <typename T, bool global = false, typename = void>
template <typename T, bool global = false, bool raw = false, typename = void>
struct probe_field_getter;
template<typename T, bool global = false, typename = void>
template<typename T, bool global = false, bool raw = false, typename = void>
struct field_setter;
template<typename T, typename = void>
struct getter;
@ -208,6 +208,7 @@ inline int alloc_destroy(lua_State* L) {
T* data = static_cast<T*>(rawdata);
std::allocator<T> alloc;
alloc.destroy(data);
return 0;
}
} // stack_detail
@ -222,34 +223,64 @@ inline decltype(auto) pop(lua_State* L) {
return popper<meta::unqualified_t<T>>{}.pop(L);
}
template <bool global = false, typename Key>
template <bool global = false, bool raw = true, typename Key>
void get_field(lua_State* L, Key&& key) {
field_getter<meta::unqualified_t<Key>, global>{}.get(L, std::forward<Key>(key));
field_getter<meta::unqualified_t<Key>, global, raw>{}.get(L, std::forward<Key>(key));
}
template <bool global = false, typename Key>
template <bool global = false, bool raw = true, typename Key>
void get_field(lua_State* L, Key&& key, int tableindex) {
field_getter<meta::unqualified_t<Key>, global>{}.get(L, std::forward<Key>(key), tableindex);
field_getter<meta::unqualified_t<Key>, global, raw>{}.get(L, std::forward<Key>(key), tableindex);
}
template <bool global = false, typename Key>
void raw_get_field(lua_State* L, Key&& key) {
get_field<global, true>(L, std::forward<Key>(key));
}
template <bool global = false, typename Key>
void raw_get_field(lua_State* L, Key&& key, int tableindex) {
get_field<global, true>(L, std::forward<Key>(key), tableindex);
}
template <bool global = false, bool raw = false, typename Key>
probe probe_get_field(lua_State* L, Key&& key) {
return probe_field_getter<meta::unqualified_t<Key>, global>{}.get(L, std::forward<Key>(key));
return probe_field_getter<meta::unqualified_t<Key>, global, raw>{}.get(L, std::forward<Key>(key));
}
template <bool global = false, bool raw = false, typename Key>
probe probe_get_field(lua_State* L, Key&& key, int tableindex) {
return probe_field_getter<meta::unqualified_t<Key>, global, raw>{}.get(L, std::forward<Key>(key), tableindex);
}
template <bool global = false, typename Key>
probe probe_get_field(lua_State* L, Key&& key, int tableindex) {
return probe_field_getter<meta::unqualified_t<Key>, global>{}.get(L, std::forward<Key>(key), tableindex);
probe probe_raw_get_field(lua_State* L, Key&& key) {
return probe_get_field<global, true>(L, std::forward<Key>(key));
}
template <bool global = false, typename Key, typename Value>
template <bool global = false, typename Key>
probe probe_raw_get_field(lua_State* L, Key&& key, int tableindex) {
return probe_get_field<global, true>(L, std::forward<Key>(key), tableindex);
}
template <bool global = false, bool raw = false, typename Key, typename Value>
void set_field(lua_State* L, Key&& key, Value&& value) {
field_setter<meta::unqualified_t<Key>, global>{}.set(L, std::forward<Key>(key), std::forward<Value>(value));
field_setter<meta::unqualified_t<Key>, global, raw>{}.set(L, std::forward<Key>(key), std::forward<Value>(value));
}
template <bool global = false, bool raw = false, typename Key, typename Value>
void set_field(lua_State* L, Key&& key, Value&& value, int tableindex) {
field_setter<meta::unqualified_t<Key>, global, raw>{}.set(L, std::forward<Key>(key), std::forward<Value>(value), tableindex);
}
template <bool global = false, typename Key, typename Value>
void set_field(lua_State* L, Key&& key, Value&& value, int tableindex) {
field_setter<meta::unqualified_t<Key>, global>{}.set(L, std::forward<Key>(key), std::forward<Value>(value), tableindex);
void raw_set_field(lua_State* L, Key&& key, Value&& value) {
set_field<global, true>(L, std::forward<Key>(key), std::forward<Value>(value));
}
template <bool global = false, typename Key, typename Value>
void raw_set_field(lua_State* L, Key&& key, Value&& value, int tableindex) {
set_field<global, true>(L, std::forward<Key>(key), std::forward<Value>(value), tableindex);
}
} // stack
} // sol

View File

@ -29,7 +29,7 @@
namespace sol {
namespace stack {
template <typename T, bool, typename>
template <typename T, bool, bool, typename>
struct field_getter {
template <typename Key>
void get(lua_State* L, Key&& key, int tableindex = -2) {
@ -38,59 +38,16 @@ struct field_getter {
}
};
template <bool b, typename C>
struct field_getter<metatable_key_t, b, C> {
template <bool b, bool raw, typename C>
struct field_getter<metatable_key_t, b, raw, C> {
void get(lua_State* L, metatable_key_t, int tableindex = -1) {
if (lua_getmetatable(L, tableindex) == 0)
push(L, nil);
}
};
template <typename... Args, bool b, typename C>
struct field_getter<std::tuple<Args...>, b, C> {
template <std::size_t... I, typename Keys>
void apply(std::index_sequence<0, I...>, lua_State* L, Keys&& keys, int tableindex) {
get_field<b>(L, detail::forward_get<0>(keys), tableindex);
void(detail::swallow{ (get_field<false>(L, detail::forward_get<I>(keys)), 0)... });
reference saved(L, -1);
lua_pop(L, static_cast<int>(sizeof...(I)));
saved.push();
}
template <typename Keys>
void get(lua_State* L, Keys&& keys) {
apply(std::make_index_sequence<sizeof...(Args)>(), L, std::forward<Keys>(keys), lua_absindex(L, -1));
}
template <typename Keys>
void get(lua_State* L, Keys&& keys, int tableindex) {
apply(std::make_index_sequence<sizeof...(Args)>(), L, std::forward<Keys>(keys), tableindex);
}
};
template <typename A, typename B, bool b, typename C>
struct field_getter<std::pair<A, B>, b, C> {
template <typename Keys>
void get(lua_State* L, Keys&& keys, int tableindex) {
get_field<b>(L, detail::forward_get<0>(keys), tableindex);
get_field<false>(L, detail::forward_get<1>(keys));
reference saved(L, -1);
lua_pop(L, static_cast<int>(2));
saved.push();
}
template <typename Keys>
void get(lua_State* L, Keys&& keys) {
get_field<b>(L, detail::forward_get<0>(keys));
get_field<false>(L, detail::forward_get<1>(keys));
reference saved(L, -1);
lua_pop(L, static_cast<int>(2));
saved.push();
}
};
template <typename T>
struct field_getter<T, true, std::enable_if_t<meta::is_c_str<T>::value>> {
template <typename T, bool raw>
struct field_getter<T, true, raw, std::enable_if_t<meta::is_c_str<T>::value>> {
template <typename Key>
void get(lua_State* L, Key&& key, int = -1) {
lua_getglobal(L, &key[0]);
@ -98,7 +55,7 @@ struct field_getter<T, true, std::enable_if_t<meta::is_c_str<T>::value>> {
};
template <typename T>
struct field_getter<T, false, std::enable_if_t<meta::is_c_str<T>::value>> {
struct field_getter<T, false, false, std::enable_if_t<meta::is_c_str<T>::value>> {
template <typename Key>
void get(lua_State* L, Key&& key, int tableindex = -1) {
lua_getfield(L, tableindex, &key[0]);
@ -107,15 +64,76 @@ struct field_getter<T, false, std::enable_if_t<meta::is_c_str<T>::value>> {
#if SOL_LUA_VERSION >= 503
template <typename T>
struct field_getter<T, false, std::enable_if_t<std::is_integral<T>::value>> {
template <typename Key>
void get(lua_State* L, Key&& key, int tableindex = -1) {
lua_geti(L, tableindex, static_cast<lua_Integer>(key));
}
struct field_getter<T, false, false, std::enable_if_t<std::is_integral<T>::value>> {
template <typename Key>
void get(lua_State* L, Key&& key, int tableindex = -1) {
lua_geti(L, tableindex, static_cast<lua_Integer>(key));
}
};
#endif // Lua 5.3.x
template <typename T, bool, typename>
#if SOL_LUA_VERSION >= 502
template <typename C>
struct field_getter<void*, false, true, C> {
void get(lua_State* L, void* key, int tableindex = -1) {
lua_rawgetp(L, tableindex, key);
}
};
#endif // Lua 5.3.x
template <typename T>
struct field_getter<T, false, true, std::enable_if_t<std::is_integral<T>::value>> {
template <typename Key>
void get(lua_State* L, Key&& key, int tableindex = -1) {
lua_rawgeti(L, tableindex, static_cast<lua_Integer>(key));
}
};
template <typename... Args, bool b, bool raw, typename C>
struct field_getter<std::tuple<Args...>, b, raw, C> {
template <std::size_t... I, typename Keys>
void apply(std::index_sequence<0, I...>, lua_State* L, Keys&& keys, int tableindex) {
get_field<b, raw>(L, detail::forward_get<0>(keys), tableindex);
void(detail::swallow{ (get_field<false, raw>(L, detail::forward_get<I>(keys)), 0)... });
reference saved(L, -1);
lua_pop(L, static_cast<int>(sizeof...(I)));
saved.push();
}
template <typename Keys>
void get(lua_State* L, Keys&& keys) {
apply(std::make_index_sequence<sizeof...(Args)>(), L, std::forward<Keys>(keys), lua_absindex(L, -1));
}
template <typename Keys>
void get(lua_State* L, Keys&& keys, int tableindex) {
apply(std::make_index_sequence<sizeof...(Args)>(), L, std::forward<Keys>(keys), tableindex);
}
};
template <typename A, typename B, bool b, bool raw, typename C>
struct field_getter<std::pair<A, B>, b, raw, C> {
template <typename Keys>
void get(lua_State* L, Keys&& keys, int tableindex) {
get_field<b, raw>(L, detail::forward_get<0>(keys), tableindex);
get_field<false, raw>(L, detail::forward_get<1>(keys));
reference saved(L, -1);
lua_pop(L, static_cast<int>(2));
saved.push();
}
template <typename Keys>
void get(lua_State* L, Keys&& keys) {
get_field<b, raw>(L, detail::forward_get<0>(keys));
get_field<false, raw>(L, detail::forward_get<1>(keys));
reference saved(L, -1);
lua_pop(L, static_cast<int>(2));
saved.push();
}
};
template <typename T, bool, bool, typename>
struct field_setter {
template <typename Key, typename Value>
void set(lua_State* L, Key&& key, Value&& value, int tableindex = -3) {
@ -125,8 +143,18 @@ struct field_setter {
}
};
template <bool b, typename C>
struct field_setter<metatable_key_t, b, C> {
template <typename T, bool b, typename C>
struct field_setter<T, b, true, C> {
template <typename Key, typename Value>
void set(lua_State* L, Key&& key, Value&& value, int tableindex = -3) {
push(L, std::forward<Key>(key));
push(L, std::forward<Value>(value));
lua_rawset(L, tableindex);
}
};
template <bool b, bool raw, typename C>
struct field_setter<metatable_key_t, b, raw, C> {
template <typename Value>
void set(lua_State* L, metatable_key_t, Value&& value, int tableindex = -2) {
push(L, std::forward<Value>(value));
@ -134,8 +162,8 @@ struct field_setter<metatable_key_t, b, C> {
}
};
template <typename T>
struct field_setter<T, true, std::enable_if_t<meta::is_c_str<T>::value>> {
template <typename T, bool raw>
struct field_setter<T, true, raw, std::enable_if_t<meta::is_c_str<T>::value>> {
template <typename Key, typename Value>
void set(lua_State* L, Key&& key, Value&& value, int = -2) {
push(L, std::forward<Value>(value));
@ -144,7 +172,7 @@ struct field_setter<T, true, std::enable_if_t<meta::is_c_str<T>::value>> {
};
template <typename T>
struct field_setter<T, false, std::enable_if_t<meta::is_c_str<T>::value>> {
struct field_setter<T, false, false, std::enable_if_t<meta::is_c_str<T>::value>> {
template <typename Key, typename Value>
void set(lua_State* L, Key&& key, Value&& value, int tableindex = -2) {
push(L, std::forward<Value>(value));
@ -154,22 +182,42 @@ struct field_setter<T, false, std::enable_if_t<meta::is_c_str<T>::value>> {
#if SOL_LUA_VERSION >= 503
template <typename T>
struct field_setter<T, false, std::enable_if_t<std::is_integral<T>::value>> {
template <typename Key, typename Value>
void set(lua_State* L, Key&& key, Value&& value, int tableindex = -2) {
push(L, std::forward<Value>(value));
lua_seti(L, tableindex, static_cast<lua_Integer>(key));
}
struct field_setter<T, false, false, std::enable_if_t<std::is_integral<T>::value>> {
template <typename Key, typename Value>
void set(lua_State* L, Key&& key, Value&& value, int tableindex = -2) {
push(L, std::forward<Value>(value));
lua_seti(L, tableindex, static_cast<lua_Integer>(key));
}
};
#endif // Lua 5.3.x
template <typename... Args, bool b, typename C>
struct field_setter<std::tuple<Args...>, b, C> {
template <typename T>
struct field_setter<T, false, true, std::enable_if_t<std::is_integral<T>::value>> {
template <typename Key, typename Value>
void set(lua_State* L, Key&& key, Value&& value, int tableindex = -2) {
push(L, std::forward<Value>(value));
lua_rawseti(L, tableindex, static_cast<lua_Integer>(key));
}
};
#if SOL_LUA_VERSION >= 502
template <typename C>
struct field_setter<void*, false, true, C> {
template <typename Key, typename Value>
void set(lua_State* L, void* key, Value&& value, int tableindex = -2) {
push(L, std::forward<Value>(value));
lua_rawsetp(L, tableindex, key);
}
};
#endif // Lua 5.2.x
template <typename... Args, bool b, bool raw, typename C>
struct field_setter<std::tuple<Args...>, b, raw, C> {
template <bool g, std::size_t I, typename Key, typename Value>
void apply(std::index_sequence<I>, lua_State* L, Key&& keys, Value&& value, int tableindex) {
I < 1 ?
set_field<g>(L, detail::forward_get<I>(keys), std::forward<Value>(value), tableindex) :
set_field<g>(L, detail::forward_get<I>(keys), std::forward<Value>(value));
set_field<g, raw>(L, detail::forward_get<I>(keys), std::forward<Value>(value), tableindex) :
set_field<g, raw>(L, detail::forward_get<I>(keys), std::forward<Value>(value));
}
template <bool g, std::size_t I0, std::size_t I1, std::size_t... I, typename Keys, typename Value>
@ -184,12 +232,12 @@ struct field_setter<std::tuple<Args...>, b, C> {
}
};
template <typename A, typename B, bool b, typename C>
struct field_setter<std::pair<A, B>, b, C> {
template <typename A, typename B, bool b, bool raw, typename C>
struct field_setter<std::pair<A, B>, b, raw, C> {
template <typename Keys, typename Value>
void set(lua_State* L, Keys&& keys, Value&& value, int tableindex = -1) {
get_field<b>(L, detail::forward_get<0>(keys), tableindex);
set_field<false>(L, detail::forward_get<1>(keys), std::forward<Value>(value));
get_field<b, raw>(L, detail::forward_get<0>(keys), tableindex);
set_field<false, raw>(L, detail::forward_get<1>(keys), std::forward<Value>(value));
}
};
} // stack

View File

@ -28,45 +28,45 @@
namespace sol {
namespace stack {
template <typename T, bool b, typename>
template <typename T, bool b, bool raw, typename>
struct probe_field_getter {
template <typename Key>
probe get(lua_State* L, Key&& key, int tableindex = -2) {
if (!b && !maybe_indexable(L, tableindex)) {
return probe(false, 0);
}
get_field<b>(L, std::forward<Key>(key), tableindex);
get_field<b, raw>(L, std::forward<Key>(key), tableindex);
return probe(!check<nil_t>(L), 1);
}
};
template <typename A, typename B, bool b, typename C>
struct probe_field_getter<std::pair<A, B>, b, C> {
template <typename A, typename B, bool b, bool raw, typename C>
struct probe_field_getter<std::pair<A, B>, b, raw, C> {
template <typename Keys>
probe get(lua_State* L, Keys&& keys, int tableindex = -2) {
if (!b && !maybe_indexable(L, tableindex)) {
return probe(false, 0);
}
get_field<b>(L, std::get<0>(keys), tableindex);
get_field<b, raw>(L, std::get<0>(keys), tableindex);
if (!maybe_indexable(L)) {
return probe(false, 1);
}
get_field<false>(L, std::get<1>(keys), tableindex);
get_field<false, raw>(L, std::get<1>(keys), tableindex);
return probe(!check<nil_t>(L), 2);
}
};
template <typename... Args, bool b, typename C>
struct probe_field_getter<std::tuple<Args...>, b, C> {
template <typename... Args, bool b, bool raw, typename C>
struct probe_field_getter<std::tuple<Args...>, b, raw, C> {
template <std::size_t I, typename Keys>
probe apply(std::index_sequence<I>, int sofar, lua_State* L, Keys&& keys, int tableindex) {
get_field<I < 1 && b>(L, std::get<I>(keys), tableindex);
get_field<I < 1 && b, raw>(L, std::get<I>(keys), tableindex);
return probe(!check<nil_t>(L), sofar);
}
template <std::size_t I, std::size_t I1, std::size_t... In, typename Keys>
probe apply(std::index_sequence<I, I1, In...>, int sofar, lua_State* L, Keys&& keys, int tableindex) {
get_field<I < 1 && b>(L, std::get<I>(keys), tableindex);
get_field<I < 1 && b, raw>(L, std::get<I>(keys), tableindex);
if (!maybe_indexable(L)) {
return probe(false, sofar);
}

View File

@ -287,9 +287,11 @@ struct pusher<user<T>> {
alloc.construct(static_cast<T*>(rawdata), std::forward<Args>(args)...);
lua_CFunction cdel = stack_detail::alloc_destroy<T>;
// Make sure we have a plain GC set for this data
lua_createtable(L, 0, 1);
lua_pushlightuserdata(L, rawdata);
lua_pushcclosure(L, cdel, 1);
lua_setfield(L, -2, "__gc");
lua_setmetatable(L, -2);
return 1;
}
};

View File

@ -120,7 +120,7 @@ public:
template<typename... Args>
void open_libraries(Args&&... args) {
static_assert(meta::are_same<lib, Args...>::value, "all types must be libraries");
static_assert(meta::all_same<lib, Args...>::value, "all types must be libraries");
if(sizeof...(args) == 0) {
luaL_openlibs(L);
return;

View File

@ -9,7 +9,7 @@ namespace sol {
std::size_t s;
const char* p;
string_shim(std::string& r) : string_shim(r.data(), r.size()) {}
string_shim(const std::string& r) : string_shim(r.data(), r.size()) {}
string_shim(const char* p) : string_shim(p, std::char_traits<char>::length(p)) {}
string_shim(const char* p, std::size_t s) : s(s), p(p) {}
@ -24,6 +24,10 @@ namespace sol {
return 0;
}
const char* c_str() const {
return p;
}
const char* data() const {
return p;
}

View File

@ -80,10 +80,16 @@ template<typename... T, template<typename...> class Templ>
struct is_specialization_of<Templ, Templ<T...>> : std::true_type { };
template<class T, class...>
struct are_same : std::true_type { };
struct all_same : std::true_type { };
template<class T, class U, class... Args>
struct are_same<T, U, Args...> : std::integral_constant <bool, std::is_same<T, U>::value && are_same<T, Args...>::value> { };
struct all_same<T, U, Args...> : std::integral_constant <bool, std::is_same<T, U>::value && all_same<T, Args...>::value> { };
template<class T, class...>
struct any_same : std::false_type { };
template<class T, class U, class... Args>
struct any_same<T, U, Args...> : std::integral_constant <bool, std::is_same<T, U>::value || any_same<T, Args...>::value> { };
template<typename T>
using invoke_t = typename T::type;

View File

@ -84,16 +84,32 @@ inline int c_trampoline(lua_State* L, lua_CFunction f) {
return trampoline(L, f);
}
#endif // Exceptions vs. No Exceptions
template <typename T>
struct unique_usertype {};
template <typename T>
struct implicit_wrapper {
T& item;
implicit_wrapper(T* item) : item(*item) {}
implicit_wrapper(T& item) : item(item) {}
operator T& () {
return item;
}
operator T* () {
return std::addressof(item);
}
};
} // detail
struct nil_t {};
const nil_t nil {};
struct metatable_key_t {};
const metatable_key_t metatable_key = {};
inline bool operator==(nil_t, nil_t) { return true; }
inline bool operator!=(nil_t, nil_t) { return false; }
struct metatable_key_t {};
const metatable_key_t metatable_key = {};
typedef std::remove_pointer_t<lua_CFunction> lua_r_CFunction;
template <typename T>
@ -195,8 +211,8 @@ struct user {
U value;
user(U x) : value(std::forward<U>(x)) {}
operator U* () const { return std::addressof(value); }
operator U& () const { return value; }
operator U* () { return std::addressof(value); }
operator U& () { return value; }
};
template <typename T>
@ -571,7 +587,10 @@ template <typename T>
struct lua_type_of<T, std::enable_if_t<std::is_enum<T>::value>> : std::integral_constant<type, type::number> {};
template <>
struct lua_type_of<sol::meta_function> : std::integral_constant<type, type::string> {};
struct lua_type_of<meta_function> : std::integral_constant<type, type::string> {};
template <>
struct lua_type_of<type> : std::integral_constant<type, type::none> {};
template <>
struct lua_type_of<this_state> : std::integral_constant<type, type::none> {};

View File

@ -22,18 +22,9 @@
#ifndef SOL_USERTYPE_HPP
#define SOL_USERTYPE_HPP
#include "state.hpp"
#include "function_types.hpp"
#include "usertype_traits.hpp"
#include "inheritance.hpp"
#include "raii.hpp"
#include "deprecate.hpp"
#include "usertype_metatable.hpp"
#include "stack.hpp"
#include <vector>
#include <array>
#include <algorithm>
#include <map>
#include <memory>
namespace sol {
namespace usertype_detail {
@ -64,459 +55,36 @@ struct is_destructor<destructor_wrapper<Fx>> : std::true_type {};
template <typename... Args>
using has_destructor = meta::any<is_destructor<meta::unqualified_t<Args>>...>;
enum class stage {
normalmeta,
refmeta,
uniquemeta,
};
template<bool releasemem = false, typename TCont>
inline int push_upvalues(lua_State* L, TCont&& cont) {
int n = 0;
for(auto& c : cont) {
if(releasemem) {
stack::push<light_userdata_value>(L, c.release());
}
else {
stack::push<light_userdata_value>(L, c.get());
}
++n;
}
return n;
}
template<typename T, stage metastage>
inline void push_metatable(lua_State* L, bool needsindexfunction, std::vector<std::unique_ptr<function_detail::base_function>>& funcs, std::vector<luaL_Reg>& functable, std::vector<luaL_Reg>& metafunctable, void* baseclasscheck, void* baseclasscast) {
static const auto& gcname = meta_function_names[static_cast<int>(meta_function::garbage_collect)];
luaL_newmetatable(L, &usertype_traits<T>::metatable[0]);
int metatableindex = lua_gettop(L);
if (baseclasscheck != nullptr) {
stack::push(L, light_userdata_value(baseclasscheck));
lua_setfield(L, metatableindex, &detail::base_class_check_key()[0]);
}
if (baseclasscast != nullptr) {
stack::push(L, light_userdata_value(baseclasscast));
lua_setfield(L, metatableindex, &detail::base_class_cast_key()[0]);
}
if (funcs.size() < 1 && metafunctable.size() < 2) {
return;
}
// Functions should be placed on the metatable so that they can be called "statically" if the user wants
int up = push_upvalues(L, funcs);
functable.push_back({ nullptr, nullptr });
luaL_setfuncs(L, functable.data(), up);
functable.pop_back();
// Metamethods directly on the metatable itself
int metaup = push_upvalues(L, funcs);
switch (metastage) {
case stage::uniquemeta: {
if (gcname != metafunctable.back().name) {
metafunctable.push_back({ "__gc", nullptr });
}
luaL_Reg& target = metafunctable.back();
luaL_Reg old = target;
target.func = detail::unique_destruct<T>;
metafunctable.push_back({nullptr, nullptr});
luaL_setfuncs(L, metafunctable.data(), metaup);
metafunctable.pop_back();
target = old;
break; }
case stage::refmeta:
if (gcname == metafunctable.back().name) {
// We can just "clip" out the __gc function,
// which we always put as the last entry in the meta function table.
luaL_Reg& target = metafunctable.back();
luaL_Reg old = target;
target = { nullptr, nullptr };
luaL_setfuncs(L, metafunctable.data(), metaup);
target = old;
}
break;
case stage::normalmeta:
default:
metafunctable.push_back({nullptr, nullptr});
luaL_setfuncs(L, metafunctable.data(), metaup);
metafunctable.pop_back();
break;
}
if (needsindexfunction) {
// We don't need to do anything more
// since we've already bound the __index field using
// setfuncs above...
return;
}
// Otherwise, we use quick, fast table indexing for methods
// gives us performance boost in calling them
lua_createtable(L, 0, static_cast<int>(functable.size()));
up = push_upvalues(L, funcs);
functable.push_back({nullptr, nullptr});
luaL_setfuncs(L, functable.data(), up);
functable.pop_back();
lua_setfield(L, metatableindex, "__index");
return;
}
template <typename T, typename Functions>
inline void set_global_deleter(lua_State* L, lua_CFunction cleanup, Functions&& functions) {
// Automatic deleter table -- stays alive until lua VM dies
// even if the user calls collectgarbage(), weirdly enough
lua_createtable(L, 0, 0); // global table that sits at toplevel
lua_createtable(L, 0, 1); // metatable for the global table
int up = push_upvalues<true>(L, functions);
stack::set_field(L, "__gc", c_closure(cleanup, up));
lua_setmetatable(L, -2);
// gctable name by default has ♻ part of it
lua_setglobal(L, &usertype_traits<T>::gc_table[0]);
}
} // usertype_detail
template<typename T>
class usertype {
private:
typedef std::map<std::string, std::pair<bool, function_detail::base_function*>> function_map_t;
std::vector<std::string> functionnames;
std::vector<std::unique_ptr<function_detail::base_function>> functions;
std::vector<luaL_Reg> functiontable;
std::vector<luaL_Reg> metafunctiontable;
function_detail::base_function* indexfunc;
function_detail::base_function* newindexfunc;
function_map_t indexwrapper, newindexwrapper;
const char* constructfuncname;
lua_CFunction constructfunc;
const char* destructfuncname;
lua_CFunction destructfunc;
lua_CFunction functiongcfunc;
bool needsindexfunction;
void* baseclasscheck;
void* baseclasscast;
std::unique_ptr<usertype_detail::registrar> metatableregister;
template<typename... Functions>
std::unique_ptr<function_detail::base_function> make_function(const std::string&, overload_set<Functions...> func) {
return std::make_unique<function_detail::usertype_overloaded_function<T, Functions...>>(std::move(func.set));
}
template<typename... Args>
usertype(usertype_detail::verified_tag, Args&&... args) : metatableregister( std::make_unique<usertype_metatable<T, std::tuple<std::decay_t<Args>...>>>(std::make_tuple(std::forward<Args>(args)...)) ) {}
template<typename... Functions>
std::unique_ptr<function_detail::base_function> make_function(const std::string&, constructor_wrapper<Functions...> func) {
return std::make_unique<function_detail::usertype_constructor_function<T, Functions...>>(std::move(func.set));
}
template<typename... Args>
usertype(usertype_detail::add_destructor_tag, Args&&... args) : usertype(usertype_detail::verified, "__gc", default_destructor, std::forward<Args>(args)...) {}
template<typename RSig, typename WSig>
std::unique_ptr<function_detail::base_function> make_function(const std::string&, member_property<RSig, WSig> func) {
return std::make_unique<function_detail::usertype_variable_function<T, member_property<RSig, WSig>>>(std::move(func));
}
template<typename Fx>
std::unique_ptr<function_detail::base_function> make_free_function(std::true_type, const std::string&, Fx&& func) {
typedef std::decay_t<meta::unqualified_t<Fx>> function_type;
return std::make_unique<function_detail::usertype_function<T, function_type>>(func);
}
template<typename Fx>
std::unique_ptr<function_detail::base_function> make_free_function(std::false_type, const std::string&, Fx&& func) {
typedef std::decay_t<meta::unqualified_t<Fx>> function_type;
return std::make_unique<function_detail::free_function<function_type>>(func);
}
template<typename... Args, typename Ret>
std::unique_ptr<function_detail::base_function> make_function(const std::string& name, Ret(*func)(Args...)) {
typedef lua_bind_traits<Ret(Args...)> btraits;
typedef typename btraits::template arg_at<0> Argu;
typedef meta::unqualified_t<std::remove_pointer_t<Argu>> Arg0;
return make_free_function(std::is_base_of<Arg0, T>(), name, func);
}
template<typename Base, typename Ret>
std::unique_ptr<function_detail::base_function> make_variable_function(std::true_type, const std::string&, Ret Base::* func) {
static_assert(std::is_base_of<Base, T>::value, "Any registered member variable must be part of the class");
typedef std::decay_t<decltype(func)> function_type;
return std::make_unique<function_detail::usertype_variable_function<T, function_type>>(func);
}
template<typename Base, typename Ret>
std::unique_ptr<function_detail::base_function> make_variable_function(std::false_type, const std::string&, Ret Base::* func) {
static_assert(std::is_base_of<Base, T>::value, "Any registered member function must be part of the class");
typedef std::decay_t<decltype(func)> function_type;
return std::make_unique<function_detail::usertype_function<T, function_type>>(func);
}
template<typename Base, typename Ret>
std::unique_ptr<function_detail::base_function> make_function(const std::string& name, Ret Base::* func) {
typedef std::decay_t<decltype(func)> function_type;
return make_variable_function(std::is_member_object_pointer<function_type>(), name, func);
}
template<typename Fx>
std::unique_ptr<function_detail::base_function> make_functor_function(std::true_type, const std::string&, Fx&& func) {
typedef std::decay_t<meta::unqualified_t<Fx>> function_type;
return std::make_unique<function_detail::usertype_function<T, function_type>>(func);
}
template<typename Fx>
std::unique_ptr<function_detail::base_function> make_functor_function(std::false_type, const std::string&, Fx&& func) {
typedef std::decay_t<meta::unqualified_t<Fx>> function_type;
return std::make_unique<function_detail::functor_function<function_type>>(func);
}
template<typename Fx>
std::unique_ptr<function_detail::base_function> make_function(const std::string& name, Fx&& func) {
typedef meta::unqualified_t<Fx> Fxu;
typedef lua_bind_traits<Fxu> btraits;
typedef typename btraits::template arg_at<0> Argu;
typedef meta::unqualified_t<std::remove_pointer_t<Argu>> Arg0;
return make_functor_function(std::is_base_of<Arg0, T>(), name, std::forward<Fx>(func));
}
template<std::size_t N>
void build_function(std::string, no_construction) {}
template<std::size_t N, typename... Args>
void build_function(std::string funcname, constructors<Args...>) {
functionnames.push_back(std::move(funcname));
std::string& name = functionnames.back();
// Insert bubble to keep with compile-time argument count (simpler and cheaper to do)
functions.push_back(nullptr);
constructfuncname = name.c_str();
constructfunc = function_detail::construct<T, Args...>;
metafunctiontable.push_back({ name.c_str(), constructfunc });
}
template<std::size_t N, typename... Args>
void build_function(std::string funcname, lua_CFunction direct) {
functionnames.push_back(std::move(funcname));
std::string& name = functionnames.back();
// Insert bubble to keep with compile-time argument count (simpler and cheaper to do)
functions.push_back(nullptr);
auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), name);
if (metamethodfind != meta_function_names.end()) {
metafunctiontable.push_back({ name.c_str(), function_detail::usertype_call<N> });
meta_function metafunction = static_cast<meta_function>(metamethodfind - meta_function_names.begin());
switch (metafunction) {
case meta_function::garbage_collect:
destructfuncname = name.c_str();
destructfunc = function_detail::usertype_call<N>;
return;
case meta_function::index:
indexfunc = functions.back().get();
needsindexfunction = true;
break;
case meta_function::new_index:
newindexfunc = functions.back().get();
break;
case meta_function::construct:
constructfuncname = name.c_str();
constructfunc = function_detail::usertype_call<N>;
break;
default:
break;
}
return;
}
functiontable.push_back({ name.c_str(), direct });
}
template<std::size_t N>
void build_function(std::string funcname, destructor_wrapper<void>) {
auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), funcname);
if (metamethodfind == meta_function_names.end())
throw error("cannot set destructor to anything but the metamethod \"__gc\"");
meta_function metafunction = static_cast<meta_function>(metamethodfind - meta_function_names.begin());
if (metafunction != meta_function::garbage_collect)
throw error("cannot set destructor to anything but the metamethod \"__gc\"");
functionnames.push_back(std::move(funcname));
std::string& name = functionnames.back();
destructfunc = function_detail::destruct<T>;
destructfuncname = name.c_str();
// Insert bubble to stay with the compile-time count
functions.push_back(nullptr);
}
template<std::size_t N, typename Fx>
void build_function(std::string funcname, destructor_wrapper<Fx> dx) {
auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), funcname);
if (metamethodfind == meta_function_names.end())
throw error("cannot set destructor to anything but the metamethod \"__gc\"");
meta_function metafunction = static_cast<meta_function>(metamethodfind - meta_function_names.begin());
if (metafunction != meta_function::garbage_collect)
throw error("cannot set destructor to anything but the metamethod \"__gc\"");
functionnames.push_back(std::move(funcname));
std::string& name = functionnames.back();
auto baseptr = make_function(name, std::move(dx.fx));
functions.emplace_back(std::move(baseptr));
destructfunc = function_detail::usertype_call<N>;
destructfuncname = name.c_str();
}
template<std::size_t N, typename Fx>
void build_function(std::string funcname, Fx&& func) {
typedef meta::any<std::is_member_object_pointer<meta::unqualified_t<Fx>>, meta::is_specialization_of<member_property, meta::unqualified_t<Fx>>> is_variable;
functionnames.push_back(std::move(funcname));
std::string& name = functionnames.back();
auto baseptr = make_function(name, std::forward<Fx>(func));
functions.emplace_back(std::move(baseptr));
auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), name);
if (metamethodfind != meta_function_names.end()) {
metafunctiontable.push_back({ name.c_str(), function_detail::usertype_call<N> });
meta_function metafunction = static_cast<meta_function>(metamethodfind - meta_function_names.begin());
switch (metafunction) {
case meta_function::garbage_collect:
destructfuncname = name.c_str();
destructfunc = function_detail::usertype_call<N>;
return;
case meta_function::index:
indexfunc = functions.back().get();
needsindexfunction = true;
break;
case meta_function::new_index:
newindexfunc = functions.back().get();
break;
case meta_function::construct:
constructfuncname = name.c_str();
constructfunc = function_detail::usertype_call<N>;
break;
default:
break;
}
return;
}
if (is_variable::value) {
needsindexfunction = true;
indexwrapper.insert({ name, { false, functions.back().get() } });
newindexwrapper.insert({ name, { false, functions.back().get() } });
return;
}
indexwrapper.insert({ name, { true, functions.back().get() } });
functiontable.push_back({ name.c_str(), function_detail::usertype_call<N> });
}
template<std::size_t N, typename Fx, typename... Args>
void build_function_tables(std::string funcname, Fx&& func, Args&&... args) {
build_function<N>(std::move(funcname), std::forward<Fx>(func));
build_function_tables<N + 1>(std::forward<Args>(args)...);
}
template<std::size_t N, typename Fx, typename... Args>
void build_function_tables(meta_function metafunc, Fx&& func, Args&&... args) {
std::size_t idx = static_cast<std::size_t>(metafunc);
const std::string& funcname = meta_function_names[idx];
build_function_tables<N>(funcname, std::forward<Fx>(func), std::forward<Args>(args)...);
}
template<std::size_t N, typename Fx, typename... Args>
void build_function_tables(call_construction, Fx&& func, Args&&... args) {
build_function_tables<N>("__call", std::forward<Fx>(func), std::forward<Args>(args)...);
}
template<std::size_t N, typename... Bases, typename... Args>
void build_function_tables(base_classes_tag, bases<Bases...>, Args&&... args) {
build_function_tables<N>(std::forward<Args>(args)...);
if (sizeof...(Bases) < 1)
return;
#ifndef SOL_NO_EXCEPTIONS
static_assert(sizeof(void*) <= sizeof(detail::throw_cast), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report.");
baseclasscast = (void*)&detail::throw_as<T>;
#elif !defined(SOL_NO_RTTI)
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;
#else
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;
#endif // No Runtime Type Information vs. Throw-Style Inheritance
}
template<std::size_t N>
void build_function_tables() {
int variableend = 0;
if (!indexwrapper.empty()) {
if (indexfunc == nullptr) {
indexfunc = &function_detail::failure_on_error();
}
functions.push_back(std::make_unique<function_detail::usertype_indexing_function>("__index", indexfunc, std::move(indexwrapper)));
metafunctiontable.push_back({ "__index", function_detail::usertype_call<N> });
++variableend;
}
if (!newindexwrapper.empty()) {
if (newindexfunc == nullptr) {
newindexfunc = &function_detail::failure_on_error();
}
functions.push_back(std::make_unique<function_detail::usertype_indexing_function>("__newindex", newindexfunc, std::move(newindexwrapper)));
metafunctiontable.push_back({ "__newindex", indexwrapper.empty() ? function_detail::usertype_call<N> : function_detail::usertype_call<N + 1> });
++variableend;
}
if (destructfunc != nullptr) {
metafunctiontable.push_back({ destructfuncname, destructfunc });
}
switch (variableend) {
case 2:
functiongcfunc = function_detail::usertype_gc<N + 2>;
break;
case 1:
functiongcfunc = function_detail::usertype_gc<N + 1>;
break;
case 0:
functiongcfunc = function_detail::usertype_gc<N + 0>;
break;
}
}
template<typename... Args>
usertype(usertype_detail::verified_tag, Args&&... args) : indexfunc(nullptr), newindexfunc(nullptr), constructfuncname(""), constructfunc(nullptr),
destructfuncname(""), destructfunc(nullptr), functiongcfunc(nullptr), needsindexfunction(false), baseclasscheck(nullptr), baseclasscast(nullptr) {
static_assert((sizeof...(Args) % 2) == 0, "Incorrect argument count to usertype creation: not in pairs. Might be missing name, function/property/variable, comma");
functionnames.reserve(sizeof...(args)+3);
functiontable.reserve(sizeof...(args)+3);
metafunctiontable.reserve(sizeof...(args)+3);
build_function_tables<0>(std::forward<Args>(args)...);
}
template<typename... Args>
usertype(usertype_detail::add_destructor_tag, Args&&... args) : usertype(usertype_detail::verified, "__gc", default_destructor, std::forward<Args>(args)...) {}
template<typename... Args>
usertype(usertype_detail::check_destructor_tag, Args&&... args) : usertype(meta::condition<meta::all<std::is_destructible<T>, meta::neg<usertype_detail::has_destructor<Args...>>>, usertype_detail::add_destructor_tag, usertype_detail::verified_tag>(), std::forward<Args>(args)...) {}
template<typename... Args>
usertype(usertype_detail::check_destructor_tag, Args&&... args) : usertype(meta::condition<meta::all<std::is_destructible<T>, meta::neg<usertype_detail::has_destructor<Args...>>>, usertype_detail::add_destructor_tag, usertype_detail::verified_tag>(), std::forward<Args>(args)...) {}
public:
template<typename... Args>
usertype(Args&&... args) : usertype(meta::condition<meta::all<std::is_default_constructible<T>, meta::neg<usertype_detail::has_constructor<Args...>>>, decltype(default_constructor), usertype_detail::check_destructor_tag>(), std::forward<Args>(args)...) {}
template<typename... Args>
usertype(Args&&... args) : usertype(meta::condition<meta::all<std::is_default_constructible<T>, meta::neg<usertype_detail::has_constructor<Args...>>>, decltype(default_constructor), usertype_detail::check_destructor_tag>(), std::forward<Args>(args)...) {}
template<typename... Args, typename... CArgs>
usertype(constructors<CArgs...> constructorlist, Args&&... args) : usertype(usertype_detail::verified, "new", constructorlist, "__gc", default_destructor, std::forward<Args>(args)...) {
template<typename... Args, typename... CArgs>
usertype(constructors<CArgs...> constructorlist, Args&&... args) : usertype(usertype_detail::check_destructor_tag(), "new", constructorlist, std::forward<Args>(args)...) {}
}
template<typename... Args, typename... Fxs>
usertype(constructor_wrapper<Fxs...> constructorlist, Args&&... args) : usertype(usertype_detail::check_destructor_tag(), "new", constructorlist, std::forward<Args>(args)...) {}
int push(lua_State* L) {
// push pointer tables first,
usertype_detail::push_metatable<T*, usertype_detail::stage::refmeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast);
lua_pop(L, 1);
usertype_detail::push_metatable<detail::unique_usertype<T>, usertype_detail::stage::uniquemeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast);
lua_pop(L, 1);
// but leave the regular T table on last
// so it can be linked to a key for usage with `.new(...)` or `:new(...)`
usertype_detail::push_metatable<T, usertype_detail::stage::normalmeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast);
// be sure to link the construction function to allow for people to do the whole lua_bind thing
if (constructfunc != nullptr && constructfuncname != nullptr && std::find(meta_function_names.cbegin(), meta_function_names.cend(), constructfuncname) != meta_function_names.cend()) {
lua_createtable(L, 0, 0);
lua_pushcclosure(L, constructfunc, 0);
lua_setfield(L, -2, constructfuncname);
lua_setmetatable(L, -2);
}
// Make sure to drop a table in the global namespace to properly destroy the pushed functions
// at some later point in life
usertype_detail::set_global_deleter<T>(L, functiongcfunc, functions);
return 1;
}
int push(lua_State* L) {
return metatableregister->push_um(L);
}
};
namespace stack {

View File

@ -26,27 +26,85 @@
#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"
namespace sol {
namespace usertype_detail {
inline bool is_index(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);
}
inline bool is_index(meta_function mf) {
inline bool is_indexer(meta_function mf) {
return mf == meta_function::index || mf == meta_function::new_index;
}
inline bool is_indexer(call_construction) {
return false;
}
inline bool is_indexer(base_classes_tag) {
return false;
}
inline auto make_shim(string_detail::string_shim s) {
return s;
}
inline auto make_shim(call_construction) {
return string_detail::string_shim(name_of(meta_function::call_function));
}
inline auto make_shim(meta_function mf) {
return string_detail::string_shim(name_of(mf));
}
inline auto make_shim(base_classes_tag) {
return string_detail::string_shim(detail::base_class_cast_key());
}
template <typename N, typename F>
inline luaL_Reg make_reg(N&& n, F&& f) {
luaL_Reg l{ make_shim(std::forward<N>(n)).c_str(), std::forward<F>(f) };
return l;
}
struct registrar {
virtual int push_um(lua_State* L) = 0;
virtual ~registrar() {}
};
template <bool is_index>
inline int indexing_fail(lua_State* L) {
string_detail::string_shim accessor = stack::get<string_detail::string_shim>(L, -1);
if (is_index)
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
return luaL_error(L, "sol: attempt to index (set) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.data());
}
}
template <typename T, typename Tuple>
struct usertype_metatable {
struct usertype_metatable : usertype_detail::registrar {
typedef std::make_index_sequence<std::tuple_size<Tuple>::value> indices;
typedef std::make_index_sequence<std::tuple_size<Tuple>::value / 2> half_indices;
typedef luaL_Reg regs_t[std::tuple_size<Tuple>::value / 2 + 1];
template <std::size_t I>
struct check_binding : is_variable_binding<std::tuple_element_t<I, Tuple>> {};
struct check_binding : is_variable_binding<meta::unqualified_t<std::tuple_element_t<I, Tuple>>> {};
Tuple functions;
bool must_index;
lua_CFunction indexfunc;
lua_CFunction newindexfunc;
lua_CFunction destructfunc;
lua_CFunction callconstructfunc;
void* baseclasscheck;
void* baseclasscast;
bool mustindex;
bool secondarymeta;
template <std::size_t... I>
static bool contains_variable(std::index_sequence<I...>) {
@ -57,20 +115,87 @@ namespace sol {
template <std::size_t... I>
bool contains_index(std::index_sequence<I...>) const {
bool idx = false;
detail::swallow{ 0, ((idx &= usertype_detail::is_index(std::get<I * 2>(functions))), 0) ... };
detail::swallow{ 0, ((idx &= usertype_detail::is_indexer(std::get<I * 2>(functions))), 0) ... };
return idx;
}
usertype_metatable(Tuple t) : functions(std::move(t)), must_index(contains_variable(half_indices()) || contains_index(half_indices())) {}
template <std::size_t I = 0>
int make_regs(regs_t& l, int index ) {
if (destructfunc != nullptr) {
l[index] = { name_of(meta_function::garbage_collect).c_str(), destructfunc };
++index;
}
return index;
}
template <std::size_t I = 0, typename F, typename... Args>
int make_regs(regs_t& l, int index, sol::call_construction&, F&, Args&&... args) {
callconstructfunc = call<I + 1>;
secondarymeta = true;
int endindex = make_regs<I + 2>(l, index + 1, std::forward<Args>(args)...);
return endindex;
}
template <std::size_t I = 0, typename... Bases, typename... Args>
int make_regs(regs_t& l, int index, base_classes_tag&, bases<Bases...>&, Args&&... args) {
int endindex = make_regs<I + 2>(l, index + 1, std::forward<Args>(args)...);
if (sizeof...(Bases) < 1)
return endindex;
#ifndef SOL_NO_EXCEPTIONS
static_assert(sizeof(void*) <= sizeof(detail::throw_cast), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report.");
baseclasscast = (void*)&detail::throw_as<T>;
#elif !defined(SOL_NO_RTTI)
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, Args...>::type_check;
baseclasscast = (void*)&detail::inheritance<T, Args...>::type_cast;
#else
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, Args...>::type_check;
baseclasscast = (void*)&detail::inheritance<T, Args...>::type_cast;
#endif // No Runtime Type Information vs. Throw-Style Inheritance
}
template <std::size_t I = 0, typename N, typename F, typename... Args>
int make_regs(regs_t& l, int index, N&& n, F&&, Args&&... args) {
string_detail::string_shim shimname = usertype_detail::make_shim(n);
// Returnable scope
// That would be a neat keyword for C++
// returnable { ... };
[&]() {
if (shimname == name_of(meta_function::garbage_collect)) {
destructfunc = call<I + 1>;
return;
}
else if (shimname == name_of(meta_function::index)) {
indexfunc = call<I + 1>;
mustindex = true;
return;
}
l[index] = usertype_detail::make_reg(std::forward<N>(n), call<I + 1>);
++index;
}();
return make_regs<I + 2>(l, index, std::forward<Args>(args)...);
}
usertype_metatable(Tuple t) : functions(std::move(t)),
indexfunc(usertype_detail::indexing_fail<true>), newindexfunc(usertype_detail::indexing_fail<false>),
destructfunc(nullptr), callconstructfunc(nullptr), baseclasscheck(nullptr), baseclasscast(nullptr),
mustindex(contains_variable(half_indices()) || contains_index(half_indices())), secondarymeta(false) {
}
template <bool is_index>
int find_call(std::integral_constant<bool, is_index>, std::index_sequence<>, lua_State* L, const sol::string_detail::string_shim& accessor) {
return luaL_error(L, "sol: attempt to index nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.data());
if (is_index)
return indexfunc(L);
else
return newindexfunc(L);
}
template <bool is_index, std::size_t I0, std::size_t I1, std::size_t... In>
int find_call(std::integral_constant<bool, is_index> idx, std::index_sequence<I0, I1, In...>, lua_State* L, const sol::string_detail::string_shim& accessor) {
string_detail::string_shim name(std::get<I0>(functions));
string_detail::string_shim name = usertype_detail::make_shim(std::get<I0>(functions));
if (accessor == name) {
if (is_variable_binding<decltype(std::get<I1>(functions))>::value) {
return call_with<I1, is_index, true>(L, *this);
@ -105,12 +230,14 @@ namespace sol {
}
static int gc_call(lua_State* L) {
stack_object o(L, 1);
type x = o.get_type();
usertype_metatable& f = stack::get<light<usertype_metatable>>(L, up_value_index(1));
f.~usertype_metatable();
return 0;
}
virtual int push_um(lua_State* L) override {
return stack::push(L, std::move(*this));
}
};
namespace stack {
@ -118,21 +245,20 @@ namespace sol {
template <typename T, typename Tuple>
struct pusher<usertype_metatable<T, Tuple>> {
typedef usertype_metatable<T, Tuple> umt_t;
static void cleanup(lua_State* L, int metaidx, usertype_metatable<T, Tuple>&& um) {
const char* metakey = &usertype_traits<T>::gc_table[0];
lua_createtable(L, 1, 0);
stack_table t(L, -1);
t[meta_function::garbage_collect] = umt_t::gc_call;
stack::set_field(L, metakey, t, metaidx);
}
typedef typename umt_t::regs_t regs_t;
template <std::size_t... I>
static int push(std::index_sequence<I...>, lua_State* L, usertype_metatable<T, Tuple>&& um) {
// Basic index pushing: specialize
// index and newindex to give variables and stuff
// make sure to return final T metatable table
const bool mustindex = um.must_index;
static int push(std::index_sequence<I...>, lua_State* L, usertype_metatable<T, Tuple>&& umx) {
// Make sure userdata's memory is properly in lua first,
// otherwise all the light userdata we make later will become invalid
stack::push(L, make_user(std::move(umx)));
usertype_metatable<T, Tuple>& um = stack::get<light<usertype_metatable<T, Tuple>>>(L, -1);
reference umt(L, -1);
umt.pop();
// Now use um
const bool& mustindex = um.mustindex;
stack_reference t;
for (std::size_t i = 0; i < 3; ++i) {
// Pointer types, AKA "references" from C++
const char* metakey = nullptr;
@ -149,34 +275,72 @@ namespace sol {
break;
}
luaL_newmetatable(L, metakey);
stack_table t(L, -1);
t = stack_reference(L, -1);
stack::push(L, make_light(um));
luaL_Reg l[] = {
{ &std::get<I * 2>(um.functions)[0], umt_t::call<(I * 2) + 1> }...,
{ nullptr, nullptr }
};
regs_t l{};
int lastreg = um.make_regs(l, 0, std::get<I>(um.functions)... );
bool hasdestructor = lastreg > 0 && name_of(meta_function::garbage_collect) == l[lastreg - 1].name;
if (i < 2 && hasdestructor) {
l[lastreg - 1] = { nullptr, nullptr };
}
l[lastreg] = { nullptr, nullptr };
luaL_setfuncs(L, l, 1);
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());
}
if (mustindex) {
t[meta_function::index] = make_closure(umt_t::index_call, make_light(um));
t[meta_function::new_index] = make_closure(umt_t::new_index_call, make_light(um));
// 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, make_light(um)), t.stack_index());
stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um)), t.stack_index());
}
else {
t[meta_function::index] = t;
// 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, 1);
stack_reference metabehind(L, -1);
if (um.callconstructfunc != nullptr) {
stack::set_field(L, sol::meta_function::call_function, um.callconstructfunc, metabehind.stack_index());
}
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
if (i < 2) {
// We want to just leave the table
// in the registry only, otherwise we return it
t.pop();
}
else {
// Add cleanup to metatable
// Essentially, when the metatable dies,
// this too will call the class and kill itself
const char* metakey = &usertype_traits<T>::gc_table[0];
lua_createtable(L, 1, 0);
stack_reference cleanup(L, -1);
stack::set_field(L, meta_function::garbage_collect, make_closure(umt_t::gc_call, umt), cleanup.stack_index());
stack::set_field(L, metatable_key, cleanup, cleanup.stack_index());
// Needs to be raw since we
// override the metatable's metatable on 't'
// otherwise, it will trigger the __index metamethod
// we just set
stack::raw_set_field(L, metakey, t, t.stack_index());
cleanup.pop();
}
}
int metaidx = lua_gettop(L);
cleanup(L, metaidx, std::move(um));
return 1;
}
static int push(lua_State* L, usertype_metatable<T, Tuple>&& um) {
return push(std::make_index_sequence<std::tuple_size<Tuple>::value / 2>(), L, std::move(um));
typedef typename umt_t::indices indices;
return push(indices(), L, std::move(um));
}
};

View File

@ -4,7 +4,7 @@
#include <sol.hpp>
TEST_CASE("threading/coroutines", "ensure calling a coroutine works") {
const auto& script = R"(counter = 20
const auto& script = R"(counter = 20
function loop()
while counter ~= 30
@ -16,24 +16,24 @@ function loop()
end
)";
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::coroutine);
lua.script(script);
sol::coroutine cr = lua["loop"];
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::coroutine);
lua.script(script);
sol::coroutine cr = lua["loop"];
int counter;
for (counter = 20; counter < 31 && cr; ++counter) {
int value = cr();
if (counter != value) {
throw std::logic_error("fuck");
}
}
counter -= 1;
REQUIRE(counter == 30);
int counter;
for (counter = 20; counter < 31 && cr; ++counter) {
int value = cr();
if (counter != value) {
throw std::logic_error("fuck");
}
}
counter -= 1;
REQUIRE(counter == 30);
}
TEST_CASE("threading/new-thread-coroutines", "ensure calling a coroutine works when the work is put on a different thread") {
const auto& script = R"(counter = 20
const auto& script = R"(counter = 20
function loop()
while counter ~= 30
@ -45,20 +45,20 @@ function loop()
end
)";
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::coroutine);
lua.script(script);
sol::thread runner = sol::thread::create(lua.lua_state());
sol::state_view runnerstate = runner.state();
sol::coroutine cr = runnerstate["loop"];
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::coroutine);
lua.script(script);
sol::thread runner = sol::thread::create(lua.lua_state());
sol::state_view runnerstate = runner.state();
sol::coroutine cr = runnerstate["loop"];
int counter;
for (counter = 20; counter < 31 && cr; ++counter) {
int value = cr();
if (counter != value) {
throw std::logic_error("fuck");
}
}
counter -= 1;
REQUIRE(counter == 30);
int counter;
for (counter = 20; counter < 31 && cr; ++counter) {
int value = cr();
if (counter != value) {
throw std::logic_error("fuck");
}
}
counter -= 1;
REQUIRE(counter == 30);
}

File diff suppressed because it is too large Load Diff

View File

@ -5,26 +5,26 @@
TEST_CASE("issues/stack-overflow", "make sure various operations repeated don't trigger stack overflow") {
sol::state lua;
lua.script("t = {};t[0]=20");
lua.script("lua_function=function(i)return i;end");
sol::state lua;
lua.script("t = {};t[0]=20");
lua.script("lua_function=function(i)return i;end");
sol::function f = lua["lua_function"];
std::string teststring = "testtext";
REQUIRE_NOTHROW(
for (int i = 0; i < 1000000; ++i) {
std::string result = f(teststring);
if (result != teststring) throw std::logic_error("RIP");
}
);
sol::table t = lua["t"];
int expected = 20;
REQUIRE_NOTHROW(
for (int i = 0; i < 1000000; ++i) {
int result = t[0];
t.size();
if (result != expected)
throw std::logic_error("RIP");
}
);
sol::function f = lua["lua_function"];
std::string teststring = "testtext";
REQUIRE_NOTHROW(
for (int i = 0; i < 1000000; ++i) {
std::string result = f(teststring);
if (result != teststring) throw std::logic_error("RIP");
}
);
sol::table t = lua["t"];
int expected = 20;
REQUIRE_NOTHROW(
for (int i = 0; i < 1000000; ++i) {
int result = t[0];
t.size();
if (result != expected)
throw std::logic_error("RIP");
}
);
}

View File

@ -1,11 +1,11 @@
#pragma once
struct test_stack_guard {
lua_State* L;
int& begintop;
int& endtop;
test_stack_guard(lua_State* L, int& begintop, int& endtop) : L(L), begintop(begintop), endtop(endtop) {
begintop = lua_gettop(L);
}
~test_stack_guard() { endtop = lua_gettop(L); }
lua_State* L;
int& begintop;
int& endtop;
test_stack_guard(lua_State* L, int& begintop, int& endtop) : L(L), begintop(begintop), endtop(endtop) {
begintop = lua_gettop(L);
}
~test_stack_guard() { endtop = lua_gettop(L); }
};

View File

@ -5,68 +5,68 @@
// There isn't a single library roundtripping with codecvt works on. We'll do the nitty-gritty of it later...
TEST_CASE("stack/strings", "test that strings can be roundtripped") {
sol::state lua;
sol::state lua;
static const char utf8str[] = "\xF0\x9F\x8D\x8C\x20\xE6\x99\xA5\x20\x46\x6F\x6F\x20\xC2\xA9\x20\x62\x61\x72\x20\xF0\x9D\x8C\x86\x20\x62\x61\x7A\x20\xE2\x98\x83\x20\x71\x75\x78";
static const char16_t utf16str[] = { 0xD83C, 0xDF4C, 0x20, 0x6665, 0x20, 0x46, 0x6F, 0x6F, 0x20, 0xA9, 0x20, 0x62, 0x61, 0x72, 0x20, 0xD834, 0xDF06, 0x20, 0x62, 0x61, 0x7A, 0x20, 0x2603, 0x20, 0x71, 0x75, 0x78, 0x00 };
static const char32_t utf32str[] = { 0x1F34C, 0x0020, 0x6665, 0x0020, 0x0046, 0x006F, 0x006F, 0x0020, 0x00A9, 0x0020, 0x0062, 0x0061, 0x0072, 0x0020, 0x1D306, 0x0020, 0x0062, 0x0061, 0x007A, 0x0020, 0x2603, 0x0020, 0x0071, 0x0075, 0x0078, 0x00 };
static const wchar_t widestr[] = L"Fuck these shitty compilers";
static const char32_t utf32str2[] = U"🕴";
static const char utf8str[] = "\xF0\x9F\x8D\x8C\x20\xE6\x99\xA5\x20\x46\x6F\x6F\x20\xC2\xA9\x20\x62\x61\x72\x20\xF0\x9D\x8C\x86\x20\x62\x61\x7A\x20\xE2\x98\x83\x20\x71\x75\x78";
static const char16_t utf16str[] = { 0xD83C, 0xDF4C, 0x20, 0x6665, 0x20, 0x46, 0x6F, 0x6F, 0x20, 0xA9, 0x20, 0x62, 0x61, 0x72, 0x20, 0xD834, 0xDF06, 0x20, 0x62, 0x61, 0x7A, 0x20, 0x2603, 0x20, 0x71, 0x75, 0x78, 0x00 };
static const char32_t utf32str[] = { 0x1F34C, 0x0020, 0x6665, 0x0020, 0x0046, 0x006F, 0x006F, 0x0020, 0x00A9, 0x0020, 0x0062, 0x0061, 0x0072, 0x0020, 0x1D306, 0x0020, 0x0062, 0x0061, 0x007A, 0x0020, 0x2603, 0x0020, 0x0071, 0x0075, 0x0078, 0x00 };
static const wchar_t widestr[] = L"Fuck these shitty compilers";
static const char32_t utf32str2[] = U"🕴";
#if 0
lua["utf8"] = utf8str;
lua["utf16"] = utf16str;
lua["utf32"] = utf32str;
lua["wide"] = widestr;
lua["utf8"] = utf8str;
lua["utf16"] = utf16str;
lua["utf32"] = utf32str;
lua["wide"] = widestr;
std::string utf8_to_utf8 = lua["utf8"];
std::string utf16_to_utf8 = lua["utf16"];
std::string utf32_to_utf8 = lua["utf32"];
std::string wide_to_utf8 = lua["wide"];
std::string utf8_to_utf8 = lua["utf8"];
std::string utf16_to_utf8 = lua["utf16"];
std::string utf32_to_utf8 = lua["utf32"];
std::string wide_to_utf8 = lua["wide"];
std::wstring utf8_to_wide = lua["utf8"];
std::wstring utf16_to_wide = lua["utf16"];
std::wstring utf32_to_wide = lua["utf32"];
std::wstring wide_to_wide = lua["wide"];
std::wstring utf8_to_wide = lua["utf8"];
std::wstring utf16_to_wide = lua["utf16"];
std::wstring utf32_to_wide = lua["utf32"];
std::wstring wide_to_wide = lua["wide"];
std::u16string utf8_to_utf16 = lua["utf8"];
std::u16string utf16_to_utf16 = lua["utf16"];
std::u16string utf32_to_utf16 = lua["utf32"];
std::u16string wide_to_utf16 = lua["wide"];
std::u16string utf8_to_utf16 = lua["utf8"];
std::u16string utf16_to_utf16 = lua["utf16"];
std::u16string utf32_to_utf16 = lua["utf32"];
std::u16string wide_to_utf16 = lua["wide"];
std::u32string utf8_to_utf32 = lua["utf8"];
std::u32string utf16_to_utf32 = lua["utf16"];
std::u32string utf32_to_utf32 = lua["utf32"];
std::u32string wide_to_utf32 = lua["wide"];
std::u32string utf8_to_utf32 = lua["utf8"];
std::u32string utf16_to_utf32 = lua["utf16"];
std::u32string utf32_to_utf32 = lua["utf32"];
std::u32string wide_to_utf32 = lua["wide"];
REQUIRE(utf8_to_utf8 == utf8str);
REQUIRE(utf16_to_utf8 == utf8str);
REQUIRE(utf32_to_utf8 == utf8str);
REQUIRE(wide_to_utf8 == utf8str);
REQUIRE(utf8_to_utf8 == utf8str);
REQUIRE(utf16_to_utf8 == utf8str);
REQUIRE(utf32_to_utf8 == utf8str);
REQUIRE(wide_to_utf8 == utf8str);
REQUIRE(utf8_to_utf16 == utf16str);
REQUIRE(utf16_to_utf16 == utf16str);
REQUIRE(utf32_to_utf16 == utf16str);
REQUIRE(wide_to_utf16 == utf16str);
REQUIRE(utf8_to_utf16 == utf16str);
REQUIRE(utf16_to_utf16 == utf16str);
REQUIRE(utf32_to_utf16 == utf16str);
REQUIRE(wide_to_utf16 == utf16str);
REQUIRE(utf8_to_utf32 == utf32str);
REQUIRE(utf16_to_utf32 == utf32str);
REQUIRE(utf32_to_utf32 == utf32str);
REQUIRE(wide_to_utf32 == utf32str);
REQUIRE(utf8_to_utf32 == utf32str);
REQUIRE(utf16_to_utf32 == utf32str);
REQUIRE(utf32_to_utf32 == utf32str);
REQUIRE(wide_to_utf32 == utf32str);
REQUIRE(utf8_to_wide == widestr);
REQUIRE(utf16_to_wide == widestr);
REQUIRE(utf32_to_wide == widestr);
REQUIRE(wide_to_wide == widestr);
REQUIRE(utf8_to_wide == widestr);
REQUIRE(utf16_to_wide == widestr);
REQUIRE(utf32_to_wide == widestr);
REQUIRE(wide_to_wide == widestr);
char32_t utf8_to_char32 = lua["utf8"];
char32_t utf16_to_char32 = lua["utf16"];
char32_t utf32_to_char32 = lua["utf32"];
char32_t wide_to_char32 = lua["wide"];
char32_t utf8_to_char32 = lua["utf8"];
char32_t utf16_to_char32 = lua["utf16"];
char32_t utf32_to_char32 = lua["utf32"];
char32_t wide_to_char32 = lua["wide"];
REQUIRE(utf8_to_char32 == utf32str[0]);
REQUIRE(utf16_to_char32 == utf32str[0]);
REQUIRE(utf32_to_char32 == utf32str[0]);
REQUIRE(wide_to_char32 == utf32str[0]);
REQUIRE(utf8_to_char32 == utf32str[0]);
REQUIRE(utf16_to_char32 == utf32str[0]);
REQUIRE(utf32_to_char32 == utf32str[0]);
REQUIRE(wide_to_char32 == utf32str[0]);
#endif // Shit C++
}

View File

@ -3,455 +3,506 @@
#include <catch.hpp>
#include <sol.hpp>
#include <iostream>
#include <map>
#include "test_stack_guard.hpp"
std::string free_function() {
std::cout << "free_function()" << std::endl;
return "test";
std::cout << "free_function()" << std::endl;
return "test";
}
std::vector<int> test_table_return_one() {
return{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
return{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
}
std::vector<std::pair<std::string, int>> test_table_return_two() {
return{ { "one", 1 },{ "two", 2 },{ "three", 3 } };
return{ { "one", 1 },{ "two", 2 },{ "three", 3 } };
}
std::map<std::string, std::string> test_table_return_three() {
return{ { "name", "Rapptz" },{ "friend", "ThePhD" },{ "project", "sol" } };
return{ { "name", "Rapptz" },{ "friend", "ThePhD" },{ "project", "sol" } };
}
struct object {
std::string operator() () {
std::cout << "member_test()" << std::endl;
return "test";
}
std::string operator() () {
std::cout << "member_test()" << std::endl;
return "test";
}
};
int plop_xyz(int x, int y, std::string z) {
std::cout << x << " " << y << " " << z << std::endl;
return 11;
std::cout << x << " " << y << " " << z << std::endl;
return 11;
}
TEST_CASE("tables/as-enums", "Making sure enums can be put in and gotten out as values") {
enum direction {
up,
down,
left,
right
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua["direction"] = lua.create_table_with(
"up", direction::up,
"down", direction::down,
"left", direction::left,
"right", direction::right
);
sol::object obj = lua["direction"]["up"];
bool isdir = obj.is<direction>();
REQUIRE(isdir);
auto dir = obj.as<direction>();
REQUIRE(dir == direction::up);
}
TEST_CASE("tables/as-enum-classes", "Making sure enums can be put in and gotten out as values") {
enum class direction {
up,
down,
left,
right
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua["direction"] = lua.create_table_with(
"up", direction::up,
"down", direction::down,
"left", direction::left,
"right", direction::right
);
sol::object obj = lua["direction"]["up"];
bool isdir = obj.is<direction>();
REQUIRE(isdir);
auto dir = obj.as<direction>();
REQUIRE(dir == direction::up);
}
TEST_CASE("tables/for-each", "Testing the use of for_each to get values from a lua table") {
sol::state lua;
lua.open_libraries(sol::lib::base);
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.script("arr = {\n"
"[0] = \"Hi\",\n"
"[1] = 123.45,\n"
"[2] = \"String value\",\n"
// Does nothing
//"[3] = nil,\n"
//"[nil] = 3,\n"
"[\"WOOF\"] = 123,\n"
"}");
sol::table tbl = lua[ "arr" ];
std::size_t tablesize = 4;
std::size_t iterations = 0;
auto fx = [&iterations](sol::object key, sol::object value) {
++iterations;
sol::type keytype = key.get_type();
switch (keytype) {
case sol::type::number:
switch (key.as<int>()) {
case 0:
REQUIRE((value.as<std::string>() == "Hi"));
break;
case 1:
REQUIRE((value.as<double>() == 123.45));
break;
case 2:
REQUIRE((value.as<std::string>() == "String value"));
break;
case 3:
REQUIRE((value.is<sol::nil_t>()));
break;
}
break;
case sol::type::string:
if (key.as<std::string>() == "WOOF") {
REQUIRE((value.as<double>() == 123));
}
break;
case sol::type::nil:
REQUIRE((value.as<double>() == 3));
break;
default:
break;
}
};
auto fxpair = [&fx](std::pair<sol::object, sol::object> kvp) { fx(kvp.first, kvp.second); };
tbl.for_each( fx );
REQUIRE(iterations == tablesize);
lua.script("arr = {\n"
"[0] = \"Hi\",\n"
"[1] = 123.45,\n"
"[2] = \"String value\",\n"
// Does nothing
//"[3] = nil,\n"
//"[nil] = 3,\n"
"[\"WOOF\"] = 123,\n"
"}");
sol::table tbl = lua["arr"];
std::size_t tablesize = 4;
std::size_t iterations = 0;
auto fx = [&iterations](sol::object key, sol::object value) {
++iterations;
sol::type keytype = key.get_type();
switch (keytype) {
case sol::type::number:
switch (key.as<int>()) {
case 0:
REQUIRE((value.as<std::string>() == "Hi"));
break;
case 1:
REQUIRE((value.as<double>() == 123.45));
break;
case 2:
REQUIRE((value.as<std::string>() == "String value"));
break;
case 3:
REQUIRE((value.is<sol::nil_t>()));
break;
}
break;
case sol::type::string:
if (key.as<std::string>() == "WOOF") {
REQUIRE((value.as<double>() == 123));
}
break;
case sol::type::nil:
REQUIRE((value.as<double>() == 3));
break;
default:
break;
}
};
auto fxpair = [&fx](std::pair<sol::object, sol::object> kvp) { fx(kvp.first, kvp.second); };
tbl.for_each(fx);
REQUIRE(iterations == tablesize);
iterations = 0;
tbl.for_each( fxpair );
REQUIRE(iterations == tablesize);
iterations = 0;
tbl.for_each(fxpair);
REQUIRE(iterations == tablesize);
}
TEST_CASE("tables/for-each-empty", "empty tables should not crash") {
sol::state lua;
lua.open_libraries(sol::lib::base);
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.script("arr = {}");
sol::table tbl = lua[ "arr" ];
REQUIRE(tbl.empty());
std::size_t tablesize = 0;
std::size_t iterations = 0;
auto fx = [&iterations](sol::object key, sol::object value) {
++iterations;
sol::type keytype = key.get_type();
switch (keytype) {
case sol::type::number:
switch (key.as<int>()) {
case 0:
REQUIRE((value.as<std::string>() == "Hi"));
break;
case 1:
REQUIRE((value.as<double>() == 123.45));
break;
case 2:
REQUIRE((value.as<std::string>() == "String value"));
break;
case 3:
REQUIRE((value.is<sol::nil_t>()));
break;
}
break;
case sol::type::string:
if (key.as<std::string>() == "WOOF") {
REQUIRE((value.as<double>() == 123));
}
break;
case sol::type::nil:
REQUIRE((value.as<double>() == 3));
break;
default:
break;
}
};
auto fxpair = [&fx](std::pair<sol::object, sol::object> kvp) { fx(kvp.first, kvp.second); };
tbl.for_each( fx );
REQUIRE(iterations == tablesize);
lua.script("arr = {}");
sol::table tbl = lua["arr"];
REQUIRE(tbl.empty());
std::size_t tablesize = 0;
std::size_t iterations = 0;
auto fx = [&iterations](sol::object key, sol::object value) {
++iterations;
sol::type keytype = key.get_type();
switch (keytype) {
case sol::type::number:
switch (key.as<int>()) {
case 0:
REQUIRE((value.as<std::string>() == "Hi"));
break;
case 1:
REQUIRE((value.as<double>() == 123.45));
break;
case 2:
REQUIRE((value.as<std::string>() == "String value"));
break;
case 3:
REQUIRE((value.is<sol::nil_t>()));
break;
}
break;
case sol::type::string:
if (key.as<std::string>() == "WOOF") {
REQUIRE((value.as<double>() == 123));
}
break;
case sol::type::nil:
REQUIRE((value.as<double>() == 3));
break;
default:
break;
}
};
auto fxpair = [&fx](std::pair<sol::object, sol::object> kvp) { fx(kvp.first, kvp.second); };
tbl.for_each(fx);
REQUIRE(iterations == tablesize);
iterations = 0;
tbl.for_each( fxpair );
REQUIRE(iterations == tablesize);
iterations = 0;
tbl.for_each(fxpair);
REQUIRE(iterations == tablesize);
iterations = 0;
for (const auto& kvp : tbl) {
fxpair(kvp);
++iterations;
}
REQUIRE(iterations == tablesize);
iterations = 0;
for (const auto& kvp : tbl) {
fxpair(kvp);
++iterations;
}
REQUIRE(iterations == tablesize);
}
TEST_CASE("tables/iterators", "Testing the use of iteratrs to get values from a lua table") {
sol::state lua;
lua.open_libraries(sol::lib::base);
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.script("arr = {\n"
"[0] = \"Hi\",\n"
"[1] = 123.45,\n"
"[2] = \"String value\",\n"
// Does nothing
//"[3] = nil,\n"
//"[nil] = 3,\n"
"[\"WOOF\"] = 123,\n"
"}");
sol::table tbl = lua[ "arr" ];
std::size_t tablesize = 4;
std::size_t iterations = 0;
lua.script("arr = {\n"
"[0] = \"Hi\",\n"
"[1] = 123.45,\n"
"[2] = \"String value\",\n"
// Does nothing
//"[3] = nil,\n"
//"[nil] = 3,\n"
"[\"WOOF\"] = 123,\n"
"}");
sol::table tbl = lua["arr"];
std::size_t tablesize = 4;
std::size_t iterations = 0;
int begintop = 0;
int endtop = 0;
{
test_stack_guard s(lua.lua_state(), begintop, endtop);
for (auto& kvp : tbl) {
[&iterations](sol::object key, sol::object value) {
++iterations;
sol::type keytype = key.get_type();
switch (keytype) {
case sol::type::number:
switch (key.as<int>()) {
case 0:
REQUIRE((value.as<std::string>() == "Hi"));
break;
case 1:
REQUIRE((value.as<double>() == 123.45));
break;
case 2:
REQUIRE((value.as<std::string>() == "String value"));
break;
case 3:
REQUIRE((value.is<sol::nil_t>()));
break;
}
break;
case sol::type::string:
if (key.as<std::string>() == "WOOF") {
REQUIRE((value.as<double>() == 123));
}
break;
case sol::type::nil:
REQUIRE((value.as<double>() == 3));
break;
default:
break;
}
}(kvp.first, kvp.second);
}
} REQUIRE(begintop == endtop);
REQUIRE(iterations == tablesize);
int begintop = 0;
int endtop = 0;
{
test_stack_guard s(lua.lua_state(), begintop, endtop);
for (auto& kvp : tbl) {
[&iterations](sol::object key, sol::object value) {
++iterations;
sol::type keytype = key.get_type();
switch (keytype) {
case sol::type::number:
switch (key.as<int>()) {
case 0:
REQUIRE((value.as<std::string>() == "Hi"));
break;
case 1:
REQUIRE((value.as<double>() == 123.45));
break;
case 2:
REQUIRE((value.as<std::string>() == "String value"));
break;
case 3:
REQUIRE((value.is<sol::nil_t>()));
break;
}
break;
case sol::type::string:
if (key.as<std::string>() == "WOOF") {
REQUIRE((value.as<double>() == 123));
}
break;
case sol::type::nil:
REQUIRE((value.as<double>() == 3));
break;
default:
break;
}
}(kvp.first, kvp.second);
}
} REQUIRE(begintop == endtop);
REQUIRE(iterations == tablesize);
}
TEST_CASE("tables/arbitrary-creation", "tables should be created from standard containers") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("test_one", test_table_return_one);
lua.set_function("test_two", test_table_return_two);
lua.set_function("test_three", test_table_return_three);
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("test_one", test_table_return_one);
lua.set_function("test_two", test_table_return_two);
lua.set_function("test_three", test_table_return_three);
REQUIRE_NOTHROW(lua.script("a = test_one()"));
REQUIRE_NOTHROW(lua.script("b = test_two()"));
REQUIRE_NOTHROW(lua.script("c = test_three()"));
REQUIRE_NOTHROW(lua.script("a = test_one()"));
REQUIRE_NOTHROW(lua.script("b = test_two()"));
REQUIRE_NOTHROW(lua.script("c = test_three()"));
REQUIRE_NOTHROW(lua.script("assert(#a == 10, 'error')"));
REQUIRE_NOTHROW(lua.script("assert(a[3] == 3, 'error')"));
REQUIRE_NOTHROW(lua.script("assert(b.one == 1, 'error')"));
REQUIRE_NOTHROW(lua.script("assert(b.three == 3, 'error')"));
REQUIRE_NOTHROW(lua.script("assert(c.name == 'Rapptz', 'error')"));
REQUIRE_NOTHROW(lua.script("assert(c.project == 'sol', 'error')"));
REQUIRE_NOTHROW(lua.script("assert(#a == 10, 'error')"));
REQUIRE_NOTHROW(lua.script("assert(a[3] == 3, 'error')"));
REQUIRE_NOTHROW(lua.script("assert(b.one == 1, 'error')"));
REQUIRE_NOTHROW(lua.script("assert(b.three == 3, 'error')"));
REQUIRE_NOTHROW(lua.script("assert(c.name == 'Rapptz', 'error')"));
REQUIRE_NOTHROW(lua.script("assert(c.project == 'sol', 'error')"));
auto&& a = lua.get<sol::table>("a");
auto&& b = lua.get<sol::table>("b");
auto&& c = lua.get<sol::table>("c");
auto&& a = lua.get<sol::table>("a");
auto&& b = lua.get<sol::table>("b");
auto&& c = lua.get<sol::table>("c");
REQUIRE(a.size() == 10ULL);
REQUIRE(a.get<int>(3) == 3);
REQUIRE(b.get<int>("one") == 1);
REQUIRE(b.get<int>("three") == 3);
REQUIRE(c.get<std::string>("name") == "Rapptz");
REQUIRE(c.get<std::string>("project") == "sol");
REQUIRE(a.size() == 10ULL);
REQUIRE(a.get<int>(3) == 3);
REQUIRE(b.get<int>("one") == 1);
REQUIRE(b.get<int>("three") == 3);
REQUIRE(c.get<std::string>("name") == "Rapptz");
REQUIRE(c.get<std::string>("project") == "sol");
}
TEST_CASE("tables/variables", "Check if tables and variables work as intended") {
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::os);
lua.get<sol::table>("os").set("name", "windows");
REQUIRE_NOTHROW(lua.script("assert(os.name == \"windows\")"));
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::os);
lua.get<sol::table>("os").set("name", "windows");
REQUIRE_NOTHROW(lua.script("assert(os.name == \"windows\")"));
}
TEST_CASE("tables/create", "Check if creating a table is kosher") {
sol::state lua;
lua["testtable"] = sol::table::create(lua.lua_state(), 0, 0, "Woof", "Bark", 1, 2, 3, 4);
sol::object testobj = lua["testtable"];
REQUIRE(testobj.is<sol::table>());
sol::table testtable = testobj.as<sol::table>();
REQUIRE((testtable["Woof"] == std::string("Bark")));
REQUIRE((testtable[1] == 2));
REQUIRE((testtable[3] == 4));
sol::state lua;
lua["testtable"] = sol::table::create(lua.lua_state(), 0, 0, "Woof", "Bark", 1, 2, 3, 4);
sol::object testobj = lua["testtable"];
REQUIRE(testobj.is<sol::table>());
sol::table testtable = testobj.as<sol::table>();
REQUIRE((testtable["Woof"] == std::string("Bark")));
REQUIRE((testtable[1] == 2));
REQUIRE((testtable[3] == 4));
}
TEST_CASE("tables/create-local", "Check if creating a table is kosher") {
sol::state lua;
lua["testtable"] = lua.create_table(0, 0, "Woof", "Bark", 1, 2, 3, 4);
sol::object testobj = lua["testtable"];
REQUIRE(testobj.is<sol::table>());
sol::table testtable = testobj.as<sol::table>();
REQUIRE((testtable["Woof"] == std::string("Bark")));
REQUIRE((testtable[1] == 2));
REQUIRE((testtable[3] == 4));
sol::state lua;
lua["testtable"] = lua.create_table(0, 0, "Woof", "Bark", 1, 2, 3, 4);
sol::object testobj = lua["testtable"];
REQUIRE(testobj.is<sol::table>());
sol::table testtable = testobj.as<sol::table>();
REQUIRE((testtable["Woof"] == std::string("Bark")));
REQUIRE((testtable[1] == 2));
REQUIRE((testtable[3] == 4));
}
TEST_CASE("tables/create-local-named", "Check if creating a table is kosher") {
sol::state lua;
sol::table testtable = lua.create_table("testtable", 0, 0, "Woof", "Bark", 1, 2, 3, 4);
sol::object testobj = lua["testtable"];
REQUIRE(testobj.is<sol::table>());
REQUIRE((testobj == testtable));
REQUIRE_FALSE((testobj != testtable));
REQUIRE((testtable["Woof"] == std::string("Bark")));
REQUIRE((testtable[1] == 2));
REQUIRE((testtable[3] == 4));
sol::state lua;
sol::table testtable = lua.create_table("testtable", 0, 0, "Woof", "Bark", 1, 2, 3, 4);
sol::object testobj = lua["testtable"];
REQUIRE(testobj.is<sol::table>());
REQUIRE((testobj == testtable));
REQUIRE_FALSE((testobj != testtable));
REQUIRE((testtable["Woof"] == std::string("Bark")));
REQUIRE((testtable[1] == 2));
REQUIRE((testtable[3] == 4));
}
TEST_CASE("tables/create-with-local", "Check if creating a table is kosher") {
sol::state lua;
lua["testtable"] = lua.create_table_with("Woof", "Bark", 1, 2, 3, 4);
sol::object testobj = lua["testtable"];
REQUIRE(testobj.is<sol::table>());
sol::table testtable = testobj.as<sol::table>();
REQUIRE((testtable["Woof"] == std::string("Bark")));
REQUIRE((testtable[1] == 2));
REQUIRE((testtable[3] == 4));
sol::state lua;
lua["testtable"] = lua.create_table_with("Woof", "Bark", 1, 2, 3, 4);
sol::object testobj = lua["testtable"];
REQUIRE(testobj.is<sol::table>());
sol::table testtable = testobj.as<sol::table>();
REQUIRE((testtable["Woof"] == std::string("Bark")));
REQUIRE((testtable[1] == 2));
REQUIRE((testtable[3] == 4));
}
TEST_CASE("tables/functions-variables", "Check if tables and function calls work as intended") {
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::os);
auto run_script = [] (sol::state& lua) -> void {
lua.script("assert(os.fun() == \"test\")");
};
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::os);
auto run_script = [](sol::state& lua) -> void {
lua.script("assert(os.fun() == \"test\")");
};
lua.get<sol::table>("os").set_function("fun",
[] () {
std::cout << "stateless lambda()" << std::endl;
return "test";
}
);
REQUIRE_NOTHROW(run_script(lua));
lua.get<sol::table>("os").set_function("fun",
[]() {
std::cout << "stateless lambda()" << std::endl;
return "test";
}
);
REQUIRE_NOTHROW(run_script(lua));
lua.get<sol::table>("os").set_function("fun", &free_function);
REQUIRE_NOTHROW(run_script(lua));
lua.get<sol::table>("os").set_function("fun", &free_function);
REQUIRE_NOTHROW(run_script(lua));
// l-value, canNOT optimise
// prefer value semantics unless wrapped with std::reference_wrapper
{
auto lval = object();
lua.get<sol::table>("os").set_function("fun", &object::operator(), lval);
}
REQUIRE_NOTHROW(run_script(lua));
// l-value, canNOT optimise
// prefer value semantics unless wrapped with std::reference_wrapper
{
auto lval = object();
lua.get<sol::table>("os").set_function("fun", &object::operator(), lval);
}
REQUIRE_NOTHROW(run_script(lua));
auto reflval = object();
lua.get<sol::table>("os").set_function("fun", &object::operator(), std::ref(reflval));
REQUIRE_NOTHROW(run_script(lua));
auto reflval = object();
lua.get<sol::table>("os").set_function("fun", &object::operator(), std::ref(reflval));
REQUIRE_NOTHROW(run_script(lua));
// stateful lambda: non-convertible, cannot be optimised
int breakit = 50;
lua.get<sol::table>("os").set_function("fun",
[&breakit] () {
std::cout << "stateful lambda()" << std::endl;
return "test";
}
);
REQUIRE_NOTHROW(run_script(lua));
// stateful lambda: non-convertible, cannot be optimised
int breakit = 50;
lua.get<sol::table>("os").set_function("fun",
[&breakit]() {
std::cout << "stateful lambda()" << std::endl;
return "test";
}
);
REQUIRE_NOTHROW(run_script(lua));
// r-value, cannot optimise
lua.get<sol::table>("os").set_function("fun", &object::operator(), object());
REQUIRE_NOTHROW(run_script(lua));
// r-value, cannot optimise
lua.get<sol::table>("os").set_function("fun", &object::operator(), object());
REQUIRE_NOTHROW(run_script(lua));
// r-value, cannot optimise
auto rval = object();
lua.get<sol::table>("os").set_function("fun", &object::operator(), std::move(rval));
REQUIRE_NOTHROW(run_script(lua));
// r-value, cannot optimise
auto rval = object();
lua.get<sol::table>("os").set_function("fun", &object::operator(), std::move(rval));
REQUIRE_NOTHROW(run_script(lua));
}
TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works properly") {
sol::state lua;
lua.open_libraries(sol::lib::base);
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.script("foo = 20\nbar = \"hello world\"");
// basic retrieval
std::string bar = lua["bar"];
int foo = lua["foo"];
REQUIRE(bar == "hello world");
REQUIRE(foo == 20);
// test operator= for stringification
// errors due to ambiguous operators
bar = lua["bar"];
lua.script("foo = 20\nbar = \"hello world\"");
// basic retrieval
std::string bar = lua["bar"];
int foo = lua["foo"];
REQUIRE(bar == "hello world");
REQUIRE(foo == 20);
// test operator= for stringification
// errors due to ambiguous operators
bar = lua["bar"];
// basic setting
lua["bar"] = 20.4;
lua["foo"] = "goodbye";
// basic setting
lua["bar"] = 20.4;
lua["foo"] = "goodbye";
// doesn't modify the actual values obviously.
REQUIRE(bar == "hello world");
REQUIRE(foo == 20);
// doesn't modify the actual values obviously.
REQUIRE(bar == "hello world");
REQUIRE(foo == 20);
// function setting
lua["test"] = plop_xyz;
REQUIRE_NOTHROW(lua.script("assert(test(10, 11, \"hello\") == 11)"));
// function setting
lua["test"] = plop_xyz;
REQUIRE_NOTHROW(lua.script("assert(test(10, 11, \"hello\") == 11)"));
// function retrieval
sol::function test = lua["test"];
REQUIRE(test.call<int>(10, 11, "hello") == 11);
// function retrieval
sol::function test = lua["test"];
REQUIRE(test.call<int>(10, 11, "hello") == 11);
// setting a lambda
lua["lamb"] = [](int x) {
return x * 2;
};
// setting a lambda
lua["lamb"] = [](int x) {
return x * 2;
};
REQUIRE_NOTHROW(lua.script("assert(lamb(220) == 440)"));
REQUIRE_NOTHROW(lua.script("assert(lamb(220) == 440)"));
// function retrieval of a lambda
sol::function lamb = lua["lamb"];
REQUIRE(lamb.call<int>(220) == 440);
// function retrieval of a lambda
sol::function lamb = lua["lamb"];
REQUIRE(lamb.call<int>(220) == 440);
// test const table retrieval
auto assert1 = [](const sol::table& t) {
std::string a = t["foo"];
double b = t["bar"];
REQUIRE(a == "goodbye");
REQUIRE(b == 20.4);
};
// test const table retrieval
auto assert1 = [](const sol::table& t) {
std::string a = t["foo"];
double b = t["bar"];
REQUIRE(a == "goodbye");
REQUIRE(b == 20.4);
};
REQUIRE_NOTHROW(assert1(lua.globals()));
REQUIRE_NOTHROW(assert1(lua.globals()));
}
TEST_CASE("tables/operator[]-valid", "Test if proxies on tables can lazily evaluate validity") {
sol::state lua;
bool isFullScreen = false;
auto fullscreennopers = lua["fullscreen"]["nopers"];
auto fullscreen = lua["fullscreen"];
REQUIRE_FALSE(fullscreennopers.valid());
REQUIRE_FALSE(fullscreen.valid());
sol::state lua;
bool isFullScreen = false;
auto fullscreennopers = lua["fullscreen"]["nopers"];
auto fullscreen = lua["fullscreen"];
REQUIRE_FALSE(fullscreennopers.valid());
REQUIRE_FALSE(fullscreen.valid());
lua["fullscreen"] = true;
lua["fullscreen"] = true;
REQUIRE_FALSE(fullscreennopers.valid());
REQUIRE(fullscreen.valid());
isFullScreen = lua["fullscreen"];
REQUIRE(isFullScreen);
REQUIRE_FALSE(fullscreennopers.valid());
REQUIRE(fullscreen.valid());
isFullScreen = lua["fullscreen"];
REQUIRE(isFullScreen);
lua["fullscreen"] = false;
REQUIRE_FALSE(fullscreennopers.valid());
REQUIRE(fullscreen.valid());
isFullScreen = lua["fullscreen"];
REQUIRE_FALSE(isFullScreen);
lua["fullscreen"] = false;
REQUIRE_FALSE(fullscreennopers.valid());
REQUIRE(fullscreen.valid());
isFullScreen = lua["fullscreen"];
REQUIRE_FALSE(isFullScreen);
}
TEST_CASE("tables/operator[]-optional", "Test if proxies on tables can lazily evaluate validity") {
sol::state lua;
sol::state lua;
sol::optional<int> test1 = lua["no_exist_yet"];
bool present = (bool)test1;
REQUIRE_FALSE(present);
sol::optional<int> test1 = lua["no_exist_yet"];
bool present = (bool)test1;
REQUIRE_FALSE(present);
lua["no_exist_yet"] = 262;
sol::optional<int> test2 = lua["no_exist_yet"];
present = (bool)test2;
REQUIRE(present);
REQUIRE(test2.value() == 262);
lua["no_exist_yet"] = 262;
sol::optional<int> test2 = lua["no_exist_yet"];
present = (bool)test2;
REQUIRE(present);
REQUIRE(test2.value() == 262);
sol::optional<int> nope = lua["nope"]["kek"]["hah"];
auto nope2 = lua.get<sol::optional<int>>(std::make_tuple("nope", "kek", "hah"));
present = (bool)nope;
REQUIRE_FALSE(present);
present = (bool)nope2;
REQUIRE_FALSE(present);
lua.create_named_table("nope", "kek", lua.create_table_with("hah", 1));
sol::optional<int> non_nope = lua["nope"]["kek"]["hah"].get<sol::optional<int>>();
sol::optional<int> non_nope2 = lua.get<sol::optional<int>>(std::make_tuple("nope", "kek", "hah"));
present = (bool)non_nope;
REQUIRE(present);
present = (bool)non_nope2;
REQUIRE(present);
REQUIRE(non_nope.value() == 1);
REQUIRE(non_nope2.value() == 1);
sol::optional<int> nope = lua["nope"]["kek"]["hah"];
auto nope2 = lua.get<sol::optional<int>>(std::make_tuple("nope", "kek", "hah"));
present = (bool)nope;
REQUIRE_FALSE(present);
present = (bool)nope2;
REQUIRE_FALSE(present);
lua.create_named_table("nope", "kek", lua.create_table_with("hah", 1));
sol::optional<int> non_nope = lua["nope"]["kek"]["hah"].get<sol::optional<int>>();
sol::optional<int> non_nope2 = lua.get<sol::optional<int>>(std::make_tuple("nope", "kek", "hah"));
present = (bool)non_nope;
REQUIRE(present);
present = (bool)non_nope2;
REQUIRE(present);
REQUIRE(non_nope.value() == 1);
REQUIRE(non_nope2.value() == 1);
std::cout << "Keys: nope, kek, hah" << std::endl;
lua.set(std::make_tuple("nope", "kek", "hah"), 35);
sol::optional<int> non_nope3 = lua["nope"]["kek"]["hah"].get<sol::optional<int>>();
sol::optional<int> non_nope4 = lua.get<sol::optional<int>>(std::make_tuple("nope", "kek", "hah"));
present = (bool)non_nope3;
REQUIRE(present);
present = (bool)non_nope4;
REQUIRE(present);
REQUIRE(non_nope3.value() == 35);
REQUIRE(non_nope4.value() == 35);
std::cout << "Keys: nope, kek, hah" << std::endl;
lua.set(std::make_tuple("nope", "kek", "hah"), 35);
sol::optional<int> non_nope3 = lua["nope"]["kek"]["hah"].get<sol::optional<int>>();
sol::optional<int> non_nope4 = lua.get<sol::optional<int>>(std::make_tuple("nope", "kek", "hah"));
present = (bool)non_nope3;
REQUIRE(present);
present = (bool)non_nope4;
REQUIRE(present);
REQUIRE(non_nope3.value() == 35);
REQUIRE(non_nope4.value() == 35);
}

File diff suppressed because it is too large Load Diff

538
tests.cpp
View File

@ -10,363 +10,363 @@
TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down into a value if we desire") {
sol::state lua;
int begintop = 0, endtop = 0;
sol::state lua;
int begintop = 0, endtop = 0;
sol::function scriptload = lua.load("t1 = {t2 = {t3 = 24}};");
scriptload();
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
int traversex24 = lua.traverse_get<int>("t1", "t2", "t3");
REQUIRE(traversex24 == 24);
} REQUIRE(begintop == endtop);
sol::function scriptload = lua.load("t1 = {t2 = {t3 = 24}};");
scriptload();
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
int traversex24 = lua.traverse_get<int>("t1", "t2", "t3");
REQUIRE(traversex24 == 24);
} REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
int x24 = lua["t1"]["t2"]["t3"];
REQUIRE(x24 == 24);
} REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
int x24 = lua["t1"]["t2"]["t3"];
REQUIRE(x24 == 24);
} REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua["t1"]["t2"]["t3"] = 64;
int traversex64 = lua.traverse_get<int>("t1", "t2", "t3");
REQUIRE(traversex64 == 64);
} REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua["t1"]["t2"]["t3"] = 64;
int traversex64 = lua.traverse_get<int>("t1", "t2", "t3");
REQUIRE(traversex64 == 64);
} REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
int x64 = lua["t1"]["t2"]["t3"];
REQUIRE(x64 == 64);
} REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
int x64 = lua["t1"]["t2"]["t3"];
REQUIRE(x64 == 64);
} REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua.traverse_set("t1", "t2", "t3", 13);
int traversex13 = lua.traverse_get<int>("t1", "t2", "t3");
REQUIRE(traversex13 == 13);
} REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua.traverse_set("t1", "t2", "t3", 13);
int traversex13 = lua.traverse_get<int>("t1", "t2", "t3");
REQUIRE(traversex13 == 13);
} REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
int x13 = lua["t1"]["t2"]["t3"];
REQUIRE(x13 == 13);
} REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
int x13 = lua["t1"]["t2"]["t3"];
REQUIRE(x13 == 13);
} REQUIRE(begintop == endtop);
}
TEST_CASE("simple/set", "Check if the set works properly.") {
sol::state lua;
int begintop = 0, endtop = 0;
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua.set("a", 9);
} REQUIRE(begintop == endtop);
REQUIRE_NOTHROW(lua.script("if a ~= 9 then error('wrong value') end"));
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua.set("d", "hello");
} REQUIRE(begintop == endtop);
REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end"));
sol::state lua;
int begintop = 0, endtop = 0;
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua.set("a", 9);
} REQUIRE(begintop == endtop);
REQUIRE_NOTHROW(lua.script("if a ~= 9 then error('wrong value') end"));
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua.set("d", "hello");
} REQUIRE(begintop == endtop);
REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end"));
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua.set("e", std::string("hello"), "f", true);
} REQUIRE(begintop == endtop);
REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end"));
REQUIRE_NOTHROW(lua.script("if f ~= true then error('wrong value') end"));
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua.set("e", std::string("hello"), "f", true);
} REQUIRE(begintop == endtop);
REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end"));
REQUIRE_NOTHROW(lua.script("if f ~= true then error('wrong value') end"));
}
TEST_CASE("simple/get", "Tests if the get function works properly.") {
sol::state lua;
int begintop = 0, endtop = 0;
sol::state lua;
int begintop = 0, endtop = 0;
lua.script("a = 9");
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
auto a = lua.get<int>("a");
REQUIRE(a == 9.0);
} REQUIRE(begintop == endtop);
lua.script("a = 9");
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
auto a = lua.get<int>("a");
REQUIRE(a == 9.0);
} REQUIRE(begintop == endtop);
lua.script("b = nil");
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
REQUIRE_NOTHROW(lua.get<sol::nil_t>("b"));
} REQUIRE(begintop == endtop);
lua.script("b = nil");
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
REQUIRE_NOTHROW(lua.get<sol::nil_t>("b"));
} REQUIRE(begintop == endtop);
lua.script("d = 'hello'");
lua.script("e = true");
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
std::string d;
bool e;
std::tie( d, e ) = lua.get<std::string, bool>("d", "e");
REQUIRE(d == "hello");
REQUIRE(e == true);
} REQUIRE(begintop == endtop);
lua.script("d = 'hello'");
lua.script("e = true");
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
std::string d;
bool e;
std::tie(d, e) = lua.get<std::string, bool>("d", "e");
REQUIRE(d == "hello");
REQUIRE(e == true);
} REQUIRE(begintop == endtop);
}
TEST_CASE("simple/set-get-global-integer", "Tests if the get function works properly with global integers") {
sol::state lua;
lua[1] = 25.4;
lua.script("b = 1");
double a = lua.get<double>(1);
double b = lua.get<double>("b");
REQUIRE(a == 25.4);
REQUIRE(b == 1);
sol::state lua;
lua[1] = 25.4;
lua.script("b = 1");
double a = lua.get<double>(1);
double b = lua.get<double>("b");
REQUIRE(a == 25.4);
REQUIRE(b == 1);
}
TEST_CASE("simple/get_or", "check if table.get_or works correctly") {
sol::state lua;
sol::state lua;
auto bob_table = lua.create_table("bob");
bob_table.set("is_set", 42);
auto bob_table = lua.create_table("bob");
bob_table.set("is_set", 42);
int is_set = bob_table.get_or("is_set", 3);
int is_not_set = bob_table.get_or("is_not_set", 22);
int is_set = bob_table.get_or("is_set", 3);
int is_not_set = bob_table.get_or("is_not_set", 22);
REQUIRE(is_set == 42);
REQUIRE(is_not_set == 22);
REQUIRE(is_set == 42);
REQUIRE(is_not_set == 22);
lua["joe"] = 55.6;
double bark = lua.get_or<double>("joe", 60);
REQUIRE(bark == 55.6);
lua["joe"] = 55.6;
double bark = lua.get_or<double>("joe", 60);
REQUIRE(bark == 55.6);
}
TEST_CASE("simple/proxy_get_or", "check if proxy.get_or works correctly") {
sol::state lua;
sol::state lua;
auto bob_table = lua.create_table("bob");
bob_table.set("is_set", 42);
auto bob_table = lua.create_table("bob");
bob_table.set("is_set", 42);
int is_set = bob_table["is_set"].get_or( 3 );
int is_not_set = bob_table[ "is_not_set" ].get_or( 22 );
int is_set = bob_table["is_set"].get_or(3);
int is_not_set = bob_table["is_not_set"].get_or(22);
REQUIRE(is_set == 42);
REQUIRE(is_not_set == 22);
REQUIRE(is_set == 42);
REQUIRE(is_not_set == 22);
lua["joe"] = 55.6;
double bark = lua["joe"].get_or<double>( 60 );
REQUIRE(bark == 55.6);
lua["joe"] = 55.6;
double bark = lua["joe"].get_or<double>(60);
REQUIRE(bark == 55.6);
}
TEST_CASE("simple/addition", "check if addition works and can be gotten through lua.get and lua.set") {
sol::state lua;
sol::state lua;
lua.set("b", 0.2);
lua.script("c = 9 + b");
auto c = lua.get<double>("c");
lua.set("b", 0.2);
lua.script("c = 9 + b");
auto c = lua.get<double>("c");
REQUIRE(c == 9.2);
REQUIRE(c == 9.2);
}
TEST_CASE("simple/if", "check if if statements work through lua") {
sol::state lua;
sol::state lua;
std::string program = "if true then f = 0.1 else f = 'test' end";
lua.script(program);
auto f = lua.get<double>("f");
std::string program = "if true then f = 0.1 else f = 'test' end";
lua.script(program);
auto f = lua.get<double>("f");
REQUIRE(f == 0.1);
REQUIRE((f == lua["f"]));
REQUIRE(f == 0.1);
REQUIRE((f == lua["f"]));
}
TEST_CASE("negative/basic_errors", "Check if error handling works correctly") {
sol::state lua;
sol::state lua;
REQUIRE_THROWS(lua.script("nil[5]"));
REQUIRE_THROWS(lua.script("nil[5]"));
}
TEST_CASE("libraries", "Check if we can open libraries") {
sol::state lua;
REQUIRE_NOTHROW(lua.open_libraries(sol::lib::base, sol::lib::os));
sol::state lua;
REQUIRE_NOTHROW(lua.open_libraries(sol::lib::base, sol::lib::os));
}
TEST_CASE("libraries2", "Check if we can open ALL the libraries") {
sol::state lua;
REQUIRE_NOTHROW(lua.open_libraries(sol::lib::base,
sol::lib::bit32,
sol::lib::coroutine,
sol::lib::debug,
sol::lib::ffi,
sol::lib::jit,
sol::lib::math,
sol::lib::os,
sol::lib::package,
sol::lib::string,
sol::lib::table));
sol::state lua;
REQUIRE_NOTHROW(lua.open_libraries(sol::lib::base,
sol::lib::bit32,
sol::lib::coroutine,
sol::lib::debug,
sol::lib::ffi,
sol::lib::jit,
sol::lib::math,
sol::lib::os,
sol::lib::package,
sol::lib::string,
sol::lib::table));
}
TEST_CASE("interop/null-to-nil-and-back", "nil should be the given type when a pointer from C++ is returned as nullptr, and nil should result in nullptr in connected C++ code") {
sol::state lua;
lua.open_libraries(sol::lib::base);
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("lol", []() -> int* {
return nullptr;
});
lua.set_function("rofl", [](int* x) {
std::cout << x << std::endl;
});
REQUIRE_NOTHROW(lua.script("x = lol()\n"
"rofl(x)\n"
"assert(x == nil)"));
lua.set_function("lol", []() -> int* {
return nullptr;
});
lua.set_function("rofl", [](int* x) {
std::cout << x << std::endl;
});
REQUIRE_NOTHROW(lua.script("x = lol()\n"
"rofl(x)\n"
"assert(x == nil)"));
}
TEST_CASE("utilities/this_state", "Ensure this_state argument can be gotten anywhere in the function.") {
struct bark {
int with_state(sol::this_state l, int a, int b) {
lua_State* L = l;
int c = lua_gettop(L);
return a + b + (c - c);
}
struct bark {
int with_state(sol::this_state l, int a, int b) {
lua_State* L = l;
int c = lua_gettop(L);
return a + b + (c - c);
}
static int with_state_2(int a, sol::this_state l, int b) {
std::cout << "inside with_state_2" << std::endl;
lua_State* L = l;
std::cout << "L is" << (void*)L << std::endl;
int c = lua_gettop(L);
return a * b + (c - c);
}
};
static int with_state_2(int a, sol::this_state l, int b) {
std::cout << "inside with_state_2" << std::endl;
lua_State* L = l;
std::cout << "L is" << (void*)L << std::endl;
int c = lua_gettop(L);
return a * b + (c - c);
}
};
sol::state lua;
std::cout << "created lua state" << std::endl;
lua.open_libraries(sol::lib::base);
lua.new_usertype<bark>("bark",
"with_state", &bark::with_state
);
sol::state lua;
std::cout << "created lua state" << std::endl;
lua.open_libraries(sol::lib::base);
lua.new_usertype<bark>("bark",
"with_state", &bark::with_state
);
std::cout << "setting b and with_state_2" << std::endl;
bark b;
lua.set("b", &b);
lua.set("with_state_2", bark::with_state_2);
std::cout << "finished setting" << std::endl;
std::cout << "getting fx" << std::endl;
sol::function fx = lua["with_state_2"];
std::cout << "calling fx" << std::endl;
int a = fx(25, 25);
std::cout << "finished setting fx" << std::endl;
std::cout << "calling a script" << std::endl;
lua.script("a = with_state_2(25, 25)");
std::cout << "calling c script" << std::endl;
lua.script("c = b:with_state(25, 25)");
std::cout << "getting a" << std::endl;
int la = lua["a"];
std::cout << "getting b" << std::endl;
int lc = lua["c"];
std::cout << "setting b and with_state_2" << std::endl;
bark b;
lua.set("b", &b);
lua.set("with_state_2", bark::with_state_2);
std::cout << "finished setting" << std::endl;
std::cout << "getting fx" << std::endl;
sol::function fx = lua["with_state_2"];
std::cout << "calling fx" << std::endl;
int a = fx(25, 25);
std::cout << "finished setting fx" << std::endl;
std::cout << "calling a script" << std::endl;
lua.script("a = with_state_2(25, 25)");
std::cout << "calling c script" << std::endl;
lua.script("c = b:with_state(25, 25)");
std::cout << "getting a" << std::endl;
int la = lua["a"];
std::cout << "getting b" << std::endl;
int lc = lua["c"];
REQUIRE(lc == 50);
REQUIRE(a == 625);
REQUIRE(la == 625);
REQUIRE(lc == 50);
REQUIRE(a == 625);
REQUIRE(la == 625);
}
TEST_CASE("object/conversions", "make sure all basic reference types can be made into objects") {
sol::state lua;
lua.open_libraries(sol::lib::base);
sol::state lua;
lua.open_libraries(sol::lib::base);
struct d {};
struct d {};
lua.script("function f () print('bark') end");
lua["d"] = d{};
lua["l"] = static_cast<void*>(nullptr);
lua.script("function f () print('bark') end");
lua["d"] = d{};
lua["l"] = static_cast<void*>(nullptr);
sol::table t = lua.create_table();
sol::thread th = sol::thread::create(lua);
sol::function f = lua["f"];
sol::protected_function pf = lua["f"];
sol::userdata ud = lua["d"];
sol::lightuserdata lud = lua["l"];
sol::table t = lua.create_table();
sol::thread th = sol::thread::create(lua);
sol::function f = lua["f"];
sol::protected_function pf = lua["f"];
sol::userdata ud = lua["d"];
sol::lightuserdata lud = lua["l"];
sol::object ot(t);
sol::object ot2 = ot;
sol::object oth(th);
sol::object of(f);
sol::object opf(pf);
sol::object od(ud);
sol::object ol(lud);
sol::object ot(t);
sol::object ot2 = ot;
sol::object oth(th);
sol::object of(f);
sol::object opf(pf);
sol::object od(ud);
sol::object ol(lud);
auto oni = sol::make_object(lua, 50);
auto ond = sol::make_object(lua, 50.0);
auto oni = sol::make_object(lua, 50);
auto ond = sol::make_object(lua, 50.0);
std::string somestring = "look at this text isn't it nice";
auto osl = sol::make_object(lua, "Bark bark bark");
auto os = sol::make_object(lua, somestring);
std::string somestring = "look at this text isn't it nice";
auto osl = sol::make_object(lua, "Bark bark bark");
auto os = sol::make_object(lua, somestring);
auto omn = sol::make_object(lua, sol::nil);
auto omn = sol::make_object(lua, sol::nil);
REQUIRE(ot.get_type() == sol::type::table);
REQUIRE(ot2.get_type() == sol::type::table);
REQUIRE(oth.get_type() == sol::type::thread);
REQUIRE(of.get_type() == sol::type::function);
REQUIRE(opf.get_type() == sol::type::function);
REQUIRE(od.get_type() == sol::type::userdata);
REQUIRE(ol.get_type() == sol::type::lightuserdata);
REQUIRE(oni.get_type() == sol::type::number);
REQUIRE(ond.get_type() == sol::type::number);
REQUIRE(osl.get_type() == sol::type::string);
REQUIRE(os.get_type() == sol::type::string);
REQUIRE(omn.get_type() == sol::type::nil);
REQUIRE(ot.get_type() == sol::type::table);
REQUIRE(ot2.get_type() == sol::type::table);
REQUIRE(oth.get_type() == sol::type::thread);
REQUIRE(of.get_type() == sol::type::function);
REQUIRE(opf.get_type() == sol::type::function);
REQUIRE(od.get_type() == sol::type::userdata);
REQUIRE(ol.get_type() == sol::type::lightuserdata);
REQUIRE(oni.get_type() == sol::type::number);
REQUIRE(ond.get_type() == sol::type::number);
REQUIRE(osl.get_type() == sol::type::string);
REQUIRE(os.get_type() == sol::type::string);
REQUIRE(omn.get_type() == sol::type::nil);
}
TEST_CASE("state/require_script", "opening strings as 'requires' clauses") {
std::string code = "return { modfunc = function () return 221 end }";
std::string code = "return { modfunc = function () return 221 end }";
sol::state lua;
sol::table thingy1 = lua.require_script("thingy", code);
sol::table thingy2 = lua.require_script("thingy", code);
sol::state lua;
sol::table thingy1 = lua.require_script("thingy", code);
sol::table thingy2 = lua.require_script("thingy", code);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
// must have loaded the same table
REQUIRE(thingy1 == thingy2);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
// must have loaded the same table
REQUIRE(thingy1 == thingy2);
}
TEST_CASE("state/require", "opening using a file") {
struct open {
static int open_func(lua_State* L) {
sol::state_view lua = L;
return sol::stack::push(L, lua.create_table_with("modfunc", sol::function_args([]() { return 221; })));
}
};
struct open {
static int open_func(lua_State* L) {
sol::state_view lua = L;
return sol::stack::push(L, lua.create_table_with("modfunc", sol::function_args([]() { return 221; })));
}
};
sol::state lua;
sol::table thingy1 = lua.require("thingy", open::open_func);
sol::table thingy2 = lua.require("thingy", open::open_func);
sol::state lua;
sol::table thingy1 = lua.require("thingy", open::open_func);
sol::table thingy2 = lua.require("thingy", open::open_func);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
// THIS IS ONLY REQUIRED IN LUA 5.3, FOR SOME REASON
// must have loaded the same table
// REQUIRE(thingy1 == thingy2);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
// THIS IS ONLY REQUIRED IN LUA 5.3, FOR SOME REASON
// must have loaded the same table
// REQUIRE(thingy1 == thingy2);
}
TEST_CASE("state/multi-require", "make sure that requires transfers across hand-rolled script implementation and standard requiref") {
struct open {
static int open_func(lua_State* L) {
sol::state_view lua = L;
return sol::stack::push(L, lua.create_table_with("modfunc", sol::function_args([]() { return 221; })));
}
};
struct open {
static int open_func(lua_State* L) {
sol::state_view lua = L;
return sol::stack::push(L, lua.create_table_with("modfunc", sol::function_args([]() { return 221; })));
}
};
std::string code = "return { modfunc = function () return 221 end }";
sol::state lua;
sol::table thingy1 = lua.require("thingy", open::open_func);
sol::table thingy2 = lua.require("thingy", open::open_func);
sol::table thingy3 = lua.require_script("thingy", code);
std::string code = "return { modfunc = function () return 221 end }";
sol::state lua;
sol::table thingy1 = lua.require("thingy", open::open_func);
sol::table thingy2 = lua.require("thingy", open::open_func);
sol::table thingy3 = lua.require_script("thingy", code);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
int val3 = thingy3["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
REQUIRE(val3 == 221);
// must have loaded the same table
// Lua is not obliged to give a shit. Thanks, Lua
//REQUIRE(thingy1 == thingy2);
// But we care, thankfully
//REQUIRE(thingy1 == thingy3);
REQUIRE(thingy2 == thingy3);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
int val3 = thingy3["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
REQUIRE(val3 == 221);
// must have loaded the same table
// Lua is not obliged to give a shit. Thanks, Lua
//REQUIRE(thingy1 == thingy2);
// But we care, thankfully
//REQUIRE(thingy1 == thingy3);
REQUIRE(thingy2 == thingy3);
}