Let's do iiiit.

This commit is contained in:
ThePhD 2016-07-28 13:33:08 -04:00
parent 08b5204dee
commit de359acb3e
11 changed files with 75 additions and 161 deletions

View File

@ -74,18 +74,18 @@ Explanations for a few categories are below (rest are self-explanatory).
* optional: Support for getting an element, or potentially not (and not forcing the default construction of what amounts to a bogus/dead object). Usually comes with ``std(::experimental)::optional``. It's a fairly new class, so a hand-rolled class internal to the library with similar semantics is also acceptable
* tables: Some sort of abstraction for dealing with tables. Ideal support is ``mytable["some_key"] = value``, and everything that the syntax implies.
* table chaining: In conjunction with tables, having the ability to do nest deeply into tables ``mytable["key1"]["key2"]["key3"]``. Note that this becomes a tripping point for some libraries: crashing if ``"key1"`` doesn't exist while trying to access ``"key2"`` (Sol avoids this specifically when you use ``sol::optional``), and sometimes it's also a heavy performance bottleneck as expressions are not lazy-evaluated by a library.
* arbitrary keys: Letting C++ code use userdata, other tables, integers, etc. as keys for into a table without dropping to the plain API.
* user-defined types (udts): C++ types given form and function in lua code.
* table chaining: In conjunction with tables, having the ability to query deeply into tables ``mytable["key1"]["key2"]["key3"]``. Note that this becomes a tripping point for some libraries: crashing if ``"key1"`` doesn't exist while trying to access ``"key2"`` (Sol avoids this specifically when you use ``sol::optional``), and sometimes it's also a heavy performance bottleneck as expressions are not lazy-evaluated by a library.
* arbitrary keys: Letting C++ code use userdata, other tables, integers, etc. as keys for into a table.
* user-defined types (udts): C++ types given form and function in Lua code.
* udts - member functions: C++ member functions on a type, usually callable with ``my_object:foo(1)`` or similar in Lua.
* udts - variables: C++ member variables, manipulated by ``my_object.var = 24`` and friends
* function binding: Support for binding all types of functions. Lambdas, member functions, free functions, in different contexts, etc...
* protected function: Use of ``lua_pcall`` to call a function, which offers error-handling and trampolining
* multi-return: returning multiple values from and to lua
* variadic/variant argument: being able to accept "anything" from lua, and even return "anything" to lua
* inheritance: allowing some degree of subtyping or inheritance on classes / userdata from lua
* protected function: Use of ``lua_pcall`` to call a function, which offers error-handling and trampolining (as well as the ability to opt-in / opt-out of this behavior)
* multi-return: returning multiple values from and to Lua (generally through ``std::tuple<...>`` or in some other way)
* variadic/variant argument: being able to accept "anything" from Lua, and even return "anything" to Lua (``object`` abstraction, variadic arguments, etc...)
* inheritance: allowing some degree of subtyping or inheritance on classes / userdata from Lua - this generally means that you can retrieve a base pointer from Lua even if you hand the library a derived pointer
* overloading: the ability to call overloaded functions, matched based on arity or type (``foo( 1 )`` from lua calls a different function then ``foo( "bark" )``).
* lua thread: basic wrapping of the lua thread API; ties in with coroutine.
* Lua thread: basic wrapping of the lua thread API; ties in with coroutine.
* coroutines: allowing a function to be called multiple times, resuming the execution of a Lua coroutine each time
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+
@ -112,7 +112,7 @@ Explanations for a few categories are below (rest are self-explanatory).
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+
| function binding | ~ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ~ |
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+
| protected function | ~ | ✗ | ~ | ~ | ~ | ✔ | ~ | ✔ | ~ | ~ | ~ | ~ | ~ |
| protected call | ~ | ✗ | ~ | ~ | ~ | ✔ | ~ | ✔ | ~ | ~ | ~ | ~ | ~ |
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+
| multi-return | ~ | ✗ | ✔ | ✔ | ✔ | ✔ | ~ | ✔ | ✔ | ~ | ✔ | ~ | ✗ |
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+
@ -122,7 +122,7 @@ Explanations for a few categories are below (rest are self-explanatory).
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+
| overloading | ~ | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ | ✔ | ✔ | ✗ | ✗ |
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+
| lua thread | ~ | ✗ | ~ | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✗ | ✗ | ✔ | ✗ |
| Lua thread | ~ | ✗ | ~ | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✗ | ✗ | ✔ | ✗ |
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+
| coroutines | ~ | ✗ | ~ | ✔ | ✔ | ✔ | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ | ✗ |
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+

View File

@ -367,7 +367,7 @@ namespace sol {
int operator()(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start, F& f) {
const auto& metakey = usertype_traits<T>::metatable;
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
stack_reference userdataref(L, -1);
reference userdataref(L, -1);
T*& referencepointer = *pointerpointer;
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
referencepointer = obj;

View File

@ -23,9 +23,7 @@
#define SOL_INHERITANCE_HPP
#include "types.hpp"
#if defined(SOL_NO_RTTI) && defined(SOL_NO_EXCEPTIONS)
#include <atomic>
#endif // No Runtime Type Information and No Exceptions
namespace sol {
template <typename... Args>
@ -46,7 +44,6 @@ namespace sol {
template <typename T>
bool has_derived<T>::value = false;
#if defined(SOL_NO_RTTI) && defined(SOL_NO_EXCEPTIONS)
inline std::size_t unique_id() {
static std::atomic<std::size_t> x(0);
return ++x;
@ -59,7 +56,6 @@ namespace sol {
template <typename T>
const std::size_t id_for<T>::value = unique_id();
#endif // No Runtime Type Information / No Exceptions
inline decltype(auto) base_class_check_key() {
static const auto& key = u8"♡o。.(✿ฺ。 ✿ฺ)";
@ -71,112 +67,39 @@ namespace sol {
return key;
}
#ifndef SOL_NO_EXCEPTIONS
template <typename T>
void throw_as(void* p) {
throw static_cast<T*>(p);
}
using throw_cast = decltype(&throw_as<void>);
template <typename T>
inline T* catch_cast(void* p, throw_cast f) {
try {
f(static_cast<void*>(p));
}
catch (T* ptr) {
return ptr;
}
catch (...) {
return static_cast<T*>(p);
}
return static_cast<T*>(p);
}
template <typename T>
inline bool catch_check(throw_cast f) {
try {
f(nullptr);
}
catch (T*) {
return true;
}
catch (...) {
return false;
}
return false;
}
#elif !defined(SOL_NO_RTTI)
template <typename T, typename... Bases>
struct inheritance {
static bool type_check(types<>, const std::type_info&) {
static bool type_check_bases(types<>, std::size_t) {
return false;
}
template <typename Base, typename... Args>
static bool type_check(types<Base, Args...>, const std::type_info& ti) {
return ti != typeid(Base) || type_check(types<Bases...>(), ti);
}
static bool type_check(const std::type_info& ti) {
return ti != typeid(T) || type_check(types<Bases...>(), ti);
}
static void* type_cast(types<>, T*, const std::type_info& ti) {
return nullptr;
}
template <typename Base, typename... Args>
static void* type_cast(types<Base, Args...>, T* data, const std::type_info& ti) {
// Make sure to convert to T first, and then dynamic cast to the proper type
return ti != typeid(Base) ? type_cast(types<Bases...>(), data, ti) : static_cast<void*>(dynamic_cast<Base*>(static_cast<T*>(data)));
}
static void* type_cast(void* voiddata, const std::type_info& ti) {
T* data = static_cast<T*>(voiddata);
return static_cast<void*>(ti != typeid(T) ? type_cast(types<Bases...>(), data, ti) : data);
}
};
using inheritance_check_function = decltype(&inheritance<void>::type_check);
using inheritance_cast_function = decltype(&inheritance<void>::type_cast);
#else
template <typename T, typename... Bases>
struct inheritance {
static bool type_check(types<>, std::size_t) {
return false;
}
template <typename Base, typename... Args>
static bool type_check(types<Base, Args...>, std::size_t ti) {
return ti != id_for<Base>::value || type_check(types<Bases...>(), ti);
static bool type_check_bases(types<Base, Args...>, std::size_t ti) {
return ti != id_for<Base>::value || type_check_bases(types<Args...>(), ti);
}
static bool type_check(std::size_t ti) {
return ti != id_for<T>::value || type_check(types<Bases...>(), ti);
return ti != id_for<T>::value || type_check_bases(types<Bases...>(), ti);
}
static void* type_cast(types<>, T*, std::size_t) {
static void* type_cast_bases(types<>, T*, std::size_t) {
return nullptr;
}
template <typename Base, typename... Args>
static void* type_cast(types<Base, Args...>, T* data, std::size_t ti) {
static void* type_cast_bases(types<Base, Args...>, T* data, std::size_t ti) {
// Make sure to convert to T first, and then dynamic cast to the proper type
return ti != id_for<Base>::value ? type_cast(types<Bases...>(), data, ti) : static_cast<void*>(static_cast<Base*>(data));
return ti != id_for<Base>::value ? type_cast_bases(types<Args...>(), data, ti) : static_cast<void*>(static_cast<Base*>(data));
}
static void* type_cast(void* voiddata, std::size_t ti) {
T* data = static_cast<T*>(voiddata);
return static_cast<void*>(ti != id_for<T>::value ? type_cast(types<Bases...>(), data, ti) : data);
return static_cast<void*>(ti != id_for<T>::value ? type_cast_bases(types<Bases...>(), data, ti) : data);
}
};
using inheritance_check_function = decltype(&inheritance<void>::type_check);
using inheritance_cast_function = decltype(&inheritance<void>::type_cast);
#endif // No Exceptions and/or No Runtime Type Information
} // detail
} // sol

View File

@ -93,7 +93,9 @@ namespace sol {
object make_reference(lua_State* L, Args&&... args) {
int backpedal = stack::push<T>(L, std::forward<Args>(args)...);
object r = stack::get<sol::object>(L, -backpedal);
if (should_pop) {
lua_pop(L, backpedal);
}
return r;
}

View File

@ -27,6 +27,17 @@
namespace sol {
namespace stack {
template <bool top_level>
struct push_popper_n {
lua_State* L;
int t;
push_popper_n(lua_State* L, int x) : L(L), t(x) { }
~push_popper_n() { lua_pop(L, t); }
};
template <>
struct push_popper_n<true> {
push_popper_n(lua_State*, int) { }
};
template <bool top_level, typename T>
struct push_popper {
T t;
@ -42,6 +53,10 @@ namespace sol {
push_popper<top_level, T> push_pop(T&& x) {
return push_popper<top_level, T>(std::forward<T>(x));
}
template <bool top_level = false>
push_popper_n<top_level> pop_n(lua_State* L, int x) {
return push_popper_n<top_level>(L, x);
}
} // stack
namespace detail {
@ -135,9 +150,8 @@ namespace sol {
}
type get_type() const noexcept {
push();
auto pp = stack::push_pop(*this);
int result = lua_type(L, -1);
lua_pop(L, 1);
return static_cast<type>(result);
}

View File

@ -148,12 +148,14 @@ namespace sol {
template<bool check_args = stack_detail::default_check_arguments, typename... Args, typename Fx, typename... FxArgs>
inline int call_into_lua(types<void> tr, types<Args...> ta, lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) {
call<check_args>(tr, ta, L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
lua_settop(L, 0);
return 0;
}
template<bool check_args = stack_detail::default_check_arguments, typename Ret0, typename... Ret, typename... Args, typename Fx, typename... FxArgs, typename = std::enable_if_t<meta::neg<std::is_void<Ret0>>::value>>
inline int call_into_lua(types<Ret0, Ret...>, types<Args...> ta, lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) {
decltype(auto) r = call<check_args>(types<meta::return_type_t<Ret0, Ret...>>(), ta, L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
lua_settop(L, 0);
return push_reference(L, std::forward<decltype(r)>(r));
}
@ -167,11 +169,10 @@ namespace sol {
inline call_syntax get_call_syntax(lua_State* L, const std::string& key, int index = -2) {
luaL_getmetatable(L, key.c_str());
auto pn = pop_n(L, 1);
if (lua_compare(L, -1, index, LUA_OPEQ) == 1) {
lua_pop(L, 1);
return call_syntax::colon;
}
lua_pop(L, 1);
return call_syntax::dot;
}
@ -190,8 +191,8 @@ namespace sol {
inline void luajit_exception_handler(lua_State* L, int(*handler)(lua_State*, lua_CFunction) = detail::c_trampoline) {
#ifdef SOL_LUAJIT
lua_pushlightuserdata(L, (void*)handler);
auto pn = pop_n(L, 1);
luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON);
lua_pop(L, 1);
#else
(void)L;
(void)handler;

View File

@ -209,14 +209,14 @@ namespace sol {
static const auto& callkey = name_of(meta_function::call);
lua_getmetatable(L, index);
if (lua_isnoneornil(L, -1)) {
handler(L, index, t, type::function);
lua_pop(L, 1);
handler(L, index, t, type::function);
return false;
}
lua_getfield(L, -1, &callkey[0]);
if (lua_isnoneornil(L, -1)) {
handler(L, index, t, type::function);
lua_pop(L, 2);
handler(L, index, t, type::function);
return false;
}
// has call, is definitely a function
@ -274,30 +274,15 @@ namespace sol {
if (stack_detail::check_metatable<detail::unique_usertype<U>>(L))
return true;
bool success = false;
#ifndef SOL_NO_EXCEPTIONS
lua_getfield(L, -1, &detail::base_class_check_key()[0]);
if (type_of(L, -1) != type::nil) {
void* basecastdata = lua_touserdata(L, -1);
detail::throw_cast basecast = (detail::throw_cast)basecastdata;
success = detail::catch_check<T>(basecast);
}
#elif !defined(SOL_NO_RTTI)
lua_getfield(L, -1, &detail::base_class_check_key()[0]);
if (type_of(L, -1) != type::nil) {
void* basecastdata = lua_touserdata(L, -1);
detail::inheritance_check_function ic = (detail::inheritance_check_function)basecastdata;
success = ic(typeid(T));
}
#else
// Topkek
{
auto pn = stack::pop_n(L, 2);
lua_getfield(L, -1, &detail::base_class_check_key()[0]);
if (type_of(L, -1) != type::nil) {
void* basecastdata = lua_touserdata(L, -1);
detail::inheritance_check_function ic = (detail::inheritance_check_function)basecastdata;
success = ic(detail::id_for<T>::value);
}
#endif // No Runtime Type Information || Exceptions
lua_pop(L, 2);
}
if (!success) {
handler(L, index, type::userdata, indextype);
return false;

View File

@ -289,24 +289,6 @@ namespace sol {
}
static T* get_no_nil_from(lua_State* L, void* udata, int index = -1) {
#ifndef SOL_NO_EXCEPTIONS
if (detail::has_derived<T>::value && luaL_getmetafield(L, index, &detail::base_class_check_key()[0]) != 0) {
void* basecastdata = lua_touserdata(L, -1);
detail::throw_cast basecast = (detail::throw_cast)basecastdata;
// use the casting function to properly adjust the pointer for the desired T
udata = detail::catch_cast<T>(udata, basecast);
lua_pop(L, 1);
}
#elif !defined(SOL_NO_RTTI)
if (detail::has_derived<T>::value && luaL_getmetafield(L, index, &detail::base_class_cast_key()[0]) != 0) {
void* basecastdata = lua_touserdata(L, -1);
detail::inheritance_cast_function ic = (detail::inheritance_cast_function)basecastdata;
// use the casting function to properly adjust the pointer for the desired T
udata = ic(udata, typeid(T));
lua_pop(L, 1);
}
#else
// Lol, you motherfucker
if (detail::has_derived<T>::value && luaL_getmetafield(L, index, &detail::base_class_cast_key()[0]) != 0) {
void* basecastdata = lua_touserdata(L, -1);
detail::inheritance_cast_function ic = (detail::inheritance_cast_function)basecastdata;
@ -314,7 +296,6 @@ namespace sol {
udata = ic(udata, detail::id_for<T>::value);
lua_pop(L, 1);
}
#endif // No Runtime Type Information || Exceptions
T* obj = static_cast<T*>(udata);
return obj;
}

View File

@ -54,8 +54,8 @@ namespace sol {
sol::object key(base_t::lua_state(), -2);
sol::object value(base_t::lua_state(), -1);
std::pair<sol::object&, sol::object&> keyvalue(key, value);
auto pn = stack::pop_n(base_t::lua_state(), 1);
fx(keyvalue);
lua_pop(base_t::lua_state(), 1);
}
}
@ -66,8 +66,8 @@ namespace sol {
while (lua_next(base_t::lua_state(), -2)) {
sol::object key(base_t::lua_state(), -2);
sol::object value(base_t::lua_state(), -1);
auto pn = stack::pop_n(base_t::lua_state(), 1);
fx(key, value);
lua_pop(base_t::lua_state(), 1);
}
}
@ -231,8 +231,8 @@ namespace sol {
template <typename... Keys>
basic_table_core& traverse_set(Keys&&... keys) {
auto pp = stack::push_pop<is_global<Keys...>::value>(*this);
auto pn = stack::pop_n(base_t::lua_state(), static_cast<int>(sizeof...(Keys)-2));
traverse_set_deep<top_level>(std::forward<Keys>(keys)...);
lua_pop(base_t::lua_state(), static_cast<int>(sizeof...(Keys)-2));
return *this;
}

View File

@ -188,20 +188,11 @@ namespace sol {
return;
}
(void)detail::swallow{ 0, ((detail::has_derived<Bases>::value = true), 0)... };
#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.");
baseclasscheck = 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
baseclasscheck = (void*)&detail::inheritance<T, Bases...>::type_check;
baseclasscast = (void*)&detail::inheritance<T, Bases...>::type_cast;
}
template <std::size_t Idx, typename N, typename F, typename = std::enable_if_t<!meta::any_same<meta::unqualified_t<N>, base_classes_tag, call_construction>::value>>

View File

@ -627,6 +627,23 @@ TEST_CASE("usertype/private-constructible", "Check to make sure special snowflak
REQUIRE(expectednumkilled == factory_test::num_killed);
}
TEST_CASE("usertype/const-pointer", "Make sure const pointers can be taken") {
struct A { int x = 201; };
struct B {
int foo(const A* a) { return a->x; };
};
sol::state lua;
lua.new_usertype<B>("B",
"foo", &B::foo
);
lua.set("a", A());
lua.set("b", B());
lua.script("x = b:foo(a)");
int x = lua["x"];
REQUIRE(x == 201);
}
TEST_CASE("usertype/overloading", "Check if overloading works properly for usertypes") {
struct woof {
int var;