mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
Allow for "static functions" to be registered on userdata like other parts of the framework, as well as self-contained structs and lambas.
sol::readonly for the ability to make a variable set on a userdata readonly. No support for property yet: put on issues list for later
This commit is contained in:
parent
a9a1903339
commit
42f4455383
|
@ -87,7 +87,7 @@ Explanations for a few categories are below (rest are self-explanatory).
|
|||
+===========================+=============+============+==========+=========+==========+===========+===========+================+==========+==========+===========+=================+
|
||||
| optional | ~ | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
|
||||
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+
|
||||
| tables | ~ | ~ | ✔ | ✔ | ✔ | ✔ | ~ | ✔ | ✔ | ✗ | ✗ | ~ |
|
||||
| tables | ~ | ~ | ~ | ✔ | ✔ | ✔ | ~ | ✔ | ✔ | ✗ | ✗ | ~ |
|
||||
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+
|
||||
| table chaining | ~ | ~ | ~ | ✔ | ✔ | ✔ | ✗ | ✔ | ✔ | ✗ | ✗ | ~ |
|
||||
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+
|
||||
|
|
|
@ -41,7 +41,24 @@ Or using your favorite IDE / tool after setting up your include paths and librar
|
|||
|
||||
If you get an avalanche of errors (particularly referring to ``auto``), you may not have enabled C++14 / C++17 mode for your compiler. Add one of ``std=c++14``, ``std=c++1z`` OR ``std=c++1y`` to your compiler options. By default, this is always-on for VC++ compilers in Visual Studio and friends, but g++ and clang++ require a flag (unless you're on `GCC 6.0`_).
|
||||
|
||||
If this works, you're ready to start! The first line creates the ``lua_State`` and will hold onto it for the duration of the scope its declared in (e.g., from the opening ``{`` to the closing ``}``). It will automatically close / cleanup that lua state when it gets destructed. The second line opens a single lua-provided library, "base". There are several other libraries that come with lua that you can open by default, and those are included in the :ref:`sol::lib<lib-enum>` enumeration.
|
||||
If this works, you're ready to start! The first line creates the ``lua_State`` and will hold onto it for the duration of the scope its declared in (e.g., from the opening ``{`` to the closing ``}``). It will automatically close / cleanup that lua state when it gets destructed. The second line opens a single lua-provided library, "base". There are several other libraries that come with lua that you can open by default, and those are included in the :ref:`sol::lib<lib-enum>` enumeration. You can open multiple base libraries by specifying multiple ``sol::lib`` arguments:
|
||||
|
||||
.. code-block:: cpp
|
||||
:linenos:
|
||||
:caption: test.cpp: the first snippet
|
||||
:name: the-first-snippet
|
||||
|
||||
#include <sol.hpp>
|
||||
|
||||
int main (int argc, char* argv[]) {
|
||||
|
||||
sol::state lua;
|
||||
lua.open_libraries( sol::lib::base, sol::lib::coroutine, sol::lib::string, sol::lib::io );
|
||||
|
||||
lua.script( "print('bark bark bark!')" );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
If you're interested in integrating Sol with a project that already uses some other library or Lua in the codebase, check out the :doc:`existing example<existing>` to see how to work with Sol when you add it to a project!
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@ Writing gets a lot simpler. Even without scripting a file or a string, you can r
|
|||
sol::state lua;
|
||||
|
||||
// open those basic lua libraries again, like print() etc.
|
||||
lua.open_libs( sol::lib::base );
|
||||
lua.open_libraries( sol::lib::base );
|
||||
|
||||
// value in the global table
|
||||
lua["bark"] = 50;
|
||||
|
@ -136,7 +136,8 @@ Writing gets a lot simpler. Even without scripting a file or a string, you can r
|
|||
lua["bark"], "the key is 50 and this string is its value!"
|
||||
);
|
||||
|
||||
// Run a plain ol' string
|
||||
// Run a plain ol' string of lua code
|
||||
// Note you can interact with things set through Sol in C++ with lua!
|
||||
// Using a "Raw String Literal" to have multi-line goodness: http://en.cppreference.com/w/cpp/language/string_literal
|
||||
lua.script(R"(
|
||||
|
||||
|
|
|
@ -67,14 +67,14 @@ private:
|
|||
}
|
||||
|
||||
public:
|
||||
coroutine(lua_State* L, int index = -1) : reference(L, index) {
|
||||
#ifdef SOL_CHECK_ARGUMENTS
|
||||
type_assert(L, index, type::function);
|
||||
#endif // Safety
|
||||
}
|
||||
coroutine() noexcept = default;
|
||||
coroutine(const coroutine&) noexcept = default;
|
||||
coroutine& operator=(const coroutine&) noexcept = default;
|
||||
coroutine(lua_State* L, int index = -1) : reference(L, index) {
|
||||
#ifdef SOL_CHECK_ARGUMENTS
|
||||
stack::check<coroutine>(L, index, type_panic);
|
||||
#endif // Safety
|
||||
}
|
||||
|
||||
call_status status() const noexcept {
|
||||
return stats;
|
||||
|
|
|
@ -73,7 +73,7 @@ public:
|
|||
basic_function& operator=(basic_function&& ) = default;
|
||||
basic_function(lua_State* L, int index = -1): base_t(L, index) {
|
||||
#ifdef SOL_CHECK_ARGUMENTS
|
||||
type_assert(L, index, type::function);
|
||||
stack::check<basic_function>(L, index, type_panic);
|
||||
#endif // Safety
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,13 @@ function_arguments<Sig, Args...> function_args( Args&&... args ) {
|
|||
return function_arguments<Sig, Args...>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Allow someone to make a member variable readonly (const)
|
||||
template <typename R, typename T>
|
||||
auto readonly( R T::* v ) {
|
||||
typedef const R C;
|
||||
return static_cast<C T::*>( v );
|
||||
}
|
||||
|
||||
namespace stack {
|
||||
template<typename... Sigs>
|
||||
struct pusher<function_sig<Sigs...>> {
|
||||
|
@ -107,7 +114,7 @@ struct pusher<function_sig<Sigs...>> {
|
|||
select_reference_member_variable(is_reference(), L, std::forward<Fx>(fx), std::forward<T>(obj), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Fx, typename... Args>
|
||||
template <typename Fx>
|
||||
static void select_member_variable(std::true_type, lua_State* L, Fx&& fx) {
|
||||
typedef typename meta::bind_traits<meta::Unqualified<Fx>>::object_type C;
|
||||
lua_CFunction freefunc = &function_detail::upvalue_this_member_variable<C, Fx>::call;
|
||||
|
@ -145,7 +152,7 @@ struct pusher<function_sig<Sigs...>> {
|
|||
select_reference_member_function(is_reference(), L, std::forward<Fx>(fx), std::forward<T>(obj), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Fx, typename... Args>
|
||||
template <typename Fx>
|
||||
static void select_member_function(std::true_type, lua_State* L, Fx&& fx) {
|
||||
typedef typename meta::bind_traits<meta::Unqualified<Fx>>::object_type C;
|
||||
lua_CFunction freefunc = &function_detail::upvalue_this_member_function<C, Fx>::call;
|
||||
|
@ -211,7 +218,23 @@ struct pusher<function_arguments<T, Args...>> {
|
|||
template<typename Signature>
|
||||
struct pusher<std::function<Signature>> {
|
||||
static int push(lua_State* L, std::function<Signature> fx) {
|
||||
return pusher<function_sig<>>{}.push(L, std::move(fx));
|
||||
return pusher<function_sig<Signature>>{}.push(L, std::move(fx));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
struct pusher<Signature, std::enable_if_t<std::is_member_pointer<Signature>::value>> {
|
||||
template <typename F>
|
||||
static int push(lua_State* L, F&& f) {
|
||||
return pusher<function_sig<>>{}.push(L, std::forward<F>(f));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
struct pusher<Signature, std::enable_if_t<meta::And<std::is_function<Signature>, meta::Not<std::is_same<Signature, lua_CFunction>>, meta::Not<std::is_same<Signature, std::remove_pointer_t<lua_CFunction>>>>::value>> {
|
||||
template <typename F>
|
||||
static int push(lua_State* L, F&& f) {
|
||||
return pusher<function_sig<>>{}.push(L, std::forward<F>(f));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ struct constructor_match {
|
|||
constructor_match(T* obj) : obj(obj) {}
|
||||
|
||||
template <typename Fx, std::size_t I, typename... R, typename... Args>
|
||||
int operator()(types<Fx>, Index<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) const {
|
||||
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);
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ struct usertype_constructor_function : base_function {
|
|||
usertype_constructor_function(Functions... fxs) : overloads(fxs...) {}
|
||||
|
||||
template <typename Fx, std::size_t I, typename... R, typename... Args>
|
||||
int call(types<Fx>, Index<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) {
|
||||
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;
|
||||
|
|
|
@ -77,6 +77,30 @@ struct upvalue_member_function {
|
|||
}
|
||||
};
|
||||
|
||||
template <int N, typename R, typename M, typename V>
|
||||
int set_assignable(std::false_type, lua_State* L, M&, V&) {
|
||||
lua_pop(L, N);
|
||||
return luaL_error(L, "cannot write to this type: copy assignment/constructor not available");
|
||||
}
|
||||
|
||||
template <int N, typename R, typename M, typename V>
|
||||
int set_assignable(std::true_type, lua_State* L, M& mem, V& var) {
|
||||
(mem.*var) = stack::get<R>(L, N);
|
||||
lua_pop(L, N);
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <int N, typename R, typename M, typename V>
|
||||
int set_variable(std::true_type, lua_State* L, M& mem, V& var) {
|
||||
return set_assignable<N, R>(std::is_assignable<std::add_lvalue_reference_t<R>, R>(), L, mem, var);
|
||||
}
|
||||
|
||||
template <int N, typename R, typename M, typename V>
|
||||
int set_variable(std::false_type, lua_State* L, M&, V&) {
|
||||
lua_pop(L, N);
|
||||
return luaL_error(L, "cannot write to a const variable");
|
||||
}
|
||||
|
||||
template<typename T, typename Function>
|
||||
struct upvalue_member_variable {
|
||||
typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
|
||||
|
@ -97,8 +121,7 @@ struct upvalue_member_variable {
|
|||
stack::push(L, (mem.*var));
|
||||
return 1;
|
||||
case 1:
|
||||
(mem.*var) = stack::get<typename traits_type::return_type>(L, 1);
|
||||
lua_pop(L, 1);
|
||||
set_variable<1, typename traits_type::return_type>(meta::Not<std::is_const<typename traits_type::return_type>>(), L, mem, var);
|
||||
return 0;
|
||||
default:
|
||||
return luaL_error(L, "sol: incorrect number of arguments to member variable function");
|
||||
|
@ -158,8 +181,7 @@ struct upvalue_this_member_variable {
|
|||
stack::push(L, (mem.*var));
|
||||
return 1;
|
||||
case 2:
|
||||
(mem.*var) = stack::get<typename traits_type::return_type>(L, 2);
|
||||
lua_pop(L, 2);
|
||||
set_variable<2, typename traits_type::return_type>(meta::Not<std::is_const<typename traits_type::return_type>>(), L, mem, var);
|
||||
return 0;
|
||||
default:
|
||||
return luaL_error(L, "sol: incorrect number of arguments to member variable function");
|
||||
|
|
|
@ -26,6 +26,26 @@
|
|||
|
||||
namespace sol {
|
||||
namespace function_detail {
|
||||
template<typename Func>
|
||||
struct free_function : public base_function {
|
||||
typedef meta::Unwrapped<meta::Unqualified<Func>> Function;
|
||||
typedef meta::function_return_t<Function> return_type;
|
||||
typedef meta::function_args_t<Function> args_types;
|
||||
Function fx;
|
||||
|
||||
template<typename... Args>
|
||||
free_function(Args&&... args): fx(std::forward<Args>(args)...) {}
|
||||
|
||||
int call(lua_State* L) {
|
||||
return stack::call_into_lua(meta::tuple_types<return_type>(), args_types(), L, 1, fx);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L) override {
|
||||
auto f = [&](lua_State* L) -> int { return this->call(L);};
|
||||
return detail::trampoline(L, f);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Func>
|
||||
struct functor_function : public base_function {
|
||||
typedef meta::Unwrapped<meta::Unqualified<Func>> Function;
|
||||
|
|
|
@ -51,7 +51,7 @@ inline int overload_match_arity(types<Fx, Fxs...>, std::index_sequence<I, In...>
|
|||
typedef typename traits::args_type args_type;
|
||||
typedef typename args_type::indices args_indices;
|
||||
// compile-time eliminate any functions that we know ahead of time are of improper arity
|
||||
if (meta::find_in_pack_v<Index<traits::arity>, Index<M>...>::value) {
|
||||
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, fxarity, start, std::forward<Args>(args)...);
|
||||
}
|
||||
if (traits::arity != fxarity) {
|
||||
|
@ -60,7 +60,7 @@ inline int overload_match_arity(types<Fx, Fxs...>, std::index_sequence<I, In...>
|
|||
if (!stack::stack_detail::check_types<true>().check(args_type(), args_indices(), L, start, no_panic)) {
|
||||
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
|
||||
}
|
||||
return matchfx(types<Fx>(), Index<I>(), return_types(), args_type(), L, fxarity, start, std::forward<Args>(args)...);
|
||||
return matchfx(types<Fx>(), index_value<I>(), return_types(), args_type(), L, fxarity, start, std::forward<Args>(args)...);
|
||||
}
|
||||
} // internals
|
||||
|
||||
|
@ -90,7 +90,7 @@ struct overloaded_function : base_function {
|
|||
}
|
||||
|
||||
template <typename Fx, std::size_t I, typename... R, typename... Args>
|
||||
int call(types<Fx>, Index<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) {
|
||||
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);
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ struct usertype_overloaded_function : base_function {
|
|||
usertype_overloaded_function(std::tuple<Functions...> set) : overloads(std::move(set)) {}
|
||||
|
||||
template <typename Fx, std::size_t I, typename... R, typename... Args>
|
||||
int call(types<Fx>, Index<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) {
|
||||
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 = detail::ptr(stack::get<T>(L, 1));
|
||||
return stack::call_into_lua<0, false>(r, a, L, start, func);
|
||||
|
|
|
@ -64,7 +64,7 @@ struct usertype_function_core : public base_function {
|
|||
}
|
||||
|
||||
template<typename... Args, std::size_t Start>
|
||||
int operator()(types<void> tr, types<Args...> ta, Index<Start>, lua_State* L) {
|
||||
int operator()(types<void> tr, types<Args...> ta, index_value<Start>, lua_State* L) {
|
||||
stack::call_into_lua(tr, ta, L, static_cast<int>(Start), fx);
|
||||
int nargs = static_cast<int>(sizeof...(Args));
|
||||
lua_pop(L, nargs);
|
||||
|
@ -72,7 +72,7 @@ struct usertype_function_core : public base_function {
|
|||
}
|
||||
|
||||
template<typename... Ret, typename... Args, std::size_t Start>
|
||||
int operator()(types<Ret...> tr, types<Args...> ta, Index<Start>, lua_State* L) {
|
||||
int operator()(types<Ret...> tr, types<Args...> ta, index_value<Start>, lua_State* L) {
|
||||
decltype(auto) r = stack::call(tr, ta, L, static_cast<int>(Start), fx);
|
||||
int nargs = static_cast<int>(sizeof...(Args));
|
||||
lua_pop(L, nargs);
|
||||
|
@ -94,7 +94,7 @@ struct usertype_function : public usertype_function_core<Function, Tp> {
|
|||
|
||||
int prelude(lua_State* L) {
|
||||
this->fx.item = detail::ptr(stack::get<T>(L, 1));
|
||||
return static_cast<base_t&>(*this)(meta::tuple_types<return_type>(), args_type(), Index<2>(), L);
|
||||
return static_cast<base_t&>(*this)(meta::tuple_types<return_type>(), args_type(), index_value<2>(), L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L) override {
|
||||
|
@ -119,7 +119,7 @@ struct usertype_variable_function : public usertype_function_core<Function, Tp>
|
|||
}
|
||||
|
||||
int set_assignable(std::true_type, lua_State* L) {
|
||||
return static_cast<base_t&>(*this)(meta::tuple_types<void>(), args_type(), Index<3>(), L);
|
||||
return static_cast<base_t&>(*this)(meta::tuple_types<void>(), args_type(), index_value<3>(), L);
|
||||
}
|
||||
|
||||
int set_variable(std::false_type, lua_State* L) {
|
||||
|
@ -136,7 +136,7 @@ struct usertype_variable_function : public usertype_function_core<Function, Tp>
|
|||
this->fx.item = stack::get<T*>(L, 1);
|
||||
switch(argcount) {
|
||||
case 2:
|
||||
return static_cast<base_t&>(*this)(meta::tuple_types<return_type>(), types<>(), Index<2>(), L);
|
||||
return static_cast<base_t&>(*this)(meta::tuple_types<return_type>(), types<>(), index_value<2>(), L);
|
||||
case 3:
|
||||
return set_variable(meta::Not<std::is_const<return_type>>(), L);
|
||||
default:
|
||||
|
|
|
@ -137,7 +137,7 @@ public:
|
|||
basic_protected_function& operator=(basic_protected_function&& ) = default;
|
||||
basic_protected_function(lua_State* L, int index = -1): base_t(L, index), error_handler(get_default_handler()) {
|
||||
#ifdef SOL_CHECK_ARGUMENTS
|
||||
type_assert(L, index, type::function);
|
||||
stack::check<basic_protected_function>(L, index, type_panic);
|
||||
#endif // Safety
|
||||
}
|
||||
|
||||
|
|
|
@ -151,12 +151,60 @@ struct checker<T, type::lightuserdata, C> {
|
|||
template <typename T, typename C>
|
||||
struct checker<non_null<T>, type::userdata, C> : checker<T, lua_type_of<T>::value, C> {};
|
||||
|
||||
template <type X, typename C>
|
||||
struct checker<lua_CFunction, X, C> : stack_detail::basic_check<type::function, lua_iscfunction> {};
|
||||
template <type X, typename C>
|
||||
struct checker<std::remove_pointer_t<lua_CFunction>, X, C> : checker<lua_CFunction, X, C> {};
|
||||
template <type X, typename C>
|
||||
struct checker<c_closure, X, C> : checker<lua_CFunction, X, C> {};
|
||||
template <typename C>
|
||||
struct checker<lua_CFunction, type::function, C> : stack_detail::basic_check<type::function, lua_iscfunction> {};
|
||||
template <typename C>
|
||||
struct checker<std::remove_pointer_t<lua_CFunction>, type::function, C> : checker<lua_CFunction, type::function, C> {};
|
||||
template <typename C>
|
||||
struct checker<c_closure, type::function, C> : checker<lua_CFunction, type::function, C> {};
|
||||
|
||||
template <typename T, typename C>
|
||||
struct checker<T, type::function, C> {
|
||||
template <typename Handler>
|
||||
static bool check(lua_State* L, int index, Handler&& handler) {
|
||||
type t = type_of(L, index);
|
||||
if (t == type::function) {
|
||||
return true;
|
||||
}
|
||||
if (t != type::userdata && t != type::table) {
|
||||
handler(L, index, t, type::function);
|
||||
return false;
|
||||
}
|
||||
// Do advanced check for call-style userdata?
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
lua_getfield(L, -1, &callkey[0]);
|
||||
if (lua_isnoneornil(L, -1)) {
|
||||
handler(L, index, t, type::function);
|
||||
lua_pop(L, 2);
|
||||
return false;
|
||||
}
|
||||
// has call, is definitely a function
|
||||
lua_pop(L, 2);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename C>
|
||||
struct checker<T, type::table, C> {
|
||||
template <typename Handler>
|
||||
static bool check(lua_State* L, int index, Handler&& handler) {
|
||||
type t = type_of(L, index);
|
||||
if (t == type::table) {
|
||||
return true;
|
||||
}
|
||||
if (t != type::userdata) {
|
||||
handler(L, index, t, type::function);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename C>
|
||||
struct checker<T*, type::userdata, C> {
|
||||
|
|
|
@ -38,6 +38,13 @@ struct field_getter {
|
|||
}
|
||||
};
|
||||
|
||||
template <bool b, typename C>
|
||||
struct field_getter<metatable_key_t, b, C> {
|
||||
void get(lua_State* L, metatable_key_t, int tableindex = -1) {
|
||||
lua_getmetatable( L, tableindex );
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Args, bool b, typename C>
|
||||
struct field_getter<std::tuple<Args...>, b, C> {
|
||||
template <std::size_t... I, typename Keys>
|
||||
|
@ -117,6 +124,15 @@ struct field_setter {
|
|||
}
|
||||
};
|
||||
|
||||
template <bool b, typename C>
|
||||
struct field_setter<metatable_key_t, b, C> {
|
||||
template <typename Value>
|
||||
void set(lua_State* L, metatable_key_t, Value&& value, int tableindex = -2) {
|
||||
push(L, std::forward<Value>(value));
|
||||
lua_setmetatable(L, tableindex);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct field_setter<T, true, std::enable_if_t<meta::is_c_str<T>::value>> {
|
||||
template <typename Key, typename Value>
|
||||
|
|
|
@ -134,6 +134,13 @@ struct getter<nil_t> {
|
|||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct pusher<std::nullptr_t> {
|
||||
static std::nullptr_t get(lua_State*, int = -1) {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct getter<nullopt_t> {
|
||||
static nullopt_t get(lua_State*, int = -1) {
|
||||
|
|
|
@ -208,6 +208,14 @@ struct pusher<nil_t> {
|
|||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct pusher<metatable_key_t> {
|
||||
static int push(lua_State* L, metatable_key_t) {
|
||||
lua_pushlstring(L, "__mt", 4);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct pusher<std::remove_pointer_t<lua_CFunction>> {
|
||||
static int push(lua_State* L, lua_CFunction func, int n = 0) {
|
||||
|
|
|
@ -159,7 +159,7 @@ public:
|
|||
basic_table_core(stack_reference&& r) : basic_table_core(r.lua_state(), r.stack_index()) {}
|
||||
basic_table_core( lua_State* L, int index = -1 ) : base_t( L, index ) {
|
||||
#ifdef SOL_CHECK_ARGUMENTS
|
||||
type_assert( L, index, type::table );
|
||||
stack::check<basic_table_core>(L, index, type_panic);
|
||||
#endif // Safety
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
namespace sol {
|
||||
template<std::size_t I>
|
||||
using Index = std::integral_constant<std::size_t, I>;
|
||||
using index_value = std::integral_constant<std::size_t, I>;
|
||||
|
||||
namespace meta {
|
||||
template<typename T>
|
||||
|
@ -120,6 +120,9 @@ using Unqualified = std::remove_cv_t<std::remove_reference_t<T>>;
|
|||
template<typename T>
|
||||
using Unwrapped = typename unwrapped<T>::type;
|
||||
|
||||
template <std::size_t N, typename Tuple>
|
||||
using tuple_element = std::tuple_element<N, Unqualified<Tuple>>;
|
||||
|
||||
template <std::size_t N, typename Tuple>
|
||||
using tuple_element_t = std::tuple_element_t<N, Unqualified<Tuple>>;
|
||||
|
||||
|
@ -237,6 +240,15 @@ struct Function : Bool<meta_detail::is_function_impl<T>::value> {};
|
|||
|
||||
namespace meta_detail {
|
||||
|
||||
template <std::size_t I, typename T>
|
||||
struct void_tuple_element : meta::tuple_element<I, T> {};
|
||||
|
||||
template <std::size_t I>
|
||||
struct void_tuple_element<I, std::tuple<>> { typedef void type; };
|
||||
|
||||
template <std::size_t I, typename T>
|
||||
using void_tuple_element_t = typename void_tuple_element<I, T>::type;
|
||||
|
||||
template<typename Signature, bool b = has_deducible_signature<Signature>::value>
|
||||
struct fx_traits;
|
||||
|
||||
|
@ -258,7 +270,7 @@ struct fx_traits<R(T::*)(Args...), false> {
|
|||
typedef R return_type;
|
||||
typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
|
||||
template<std::size_t i>
|
||||
using arg = meta::tuple_element_t<i, args_tuple_type>;
|
||||
using arg_at = void_tuple_element_t<i, args_tuple_type>;
|
||||
};
|
||||
|
||||
template<typename T, typename R, typename... Args>
|
||||
|
@ -274,7 +286,7 @@ struct fx_traits<R(T::*)(Args...) const, false> {
|
|||
typedef R return_type;
|
||||
typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
|
||||
template<std::size_t i>
|
||||
using arg = meta::tuple_element_t<i, args_tuple_type>;
|
||||
using arg_at = void_tuple_element_t<i, args_tuple_type>;
|
||||
};
|
||||
|
||||
template<typename R, typename... Args>
|
||||
|
@ -289,7 +301,7 @@ struct fx_traits<R(Args...), false> {
|
|||
typedef R return_type;
|
||||
typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
|
||||
template<std::size_t i>
|
||||
using arg = meta::tuple_element_t<i, args_tuple_type>;
|
||||
using arg_at = void_tuple_element_t<i, args_tuple_type>;
|
||||
};
|
||||
|
||||
template<typename R, typename... Args>
|
||||
|
@ -304,7 +316,7 @@ struct fx_traits<R(*)(Args...), false> {
|
|||
typedef R return_type;
|
||||
typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
|
||||
template<std::size_t i>
|
||||
using arg = meta::tuple_element_t<i, args_tuple_type>;
|
||||
using arg_at = void_tuple_element_t<i, args_tuple_type>;
|
||||
};
|
||||
|
||||
template<typename Signature, bool b = std::is_member_object_pointer<Signature>::value>
|
||||
|
@ -326,7 +338,7 @@ struct callable_traits<R(T::*), true> {
|
|||
typedef R(*function_pointer_type)(Arg);
|
||||
typedef R(*free_function_pointer_type)(Arg);
|
||||
template<std::size_t i>
|
||||
using arg = meta::tuple_element_t<i, args_tuple_type>;
|
||||
using arg_at = void_tuple_element_t<i, args_tuple_type>;
|
||||
};
|
||||
} // meta_detail
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "compatibility.hpp"
|
||||
#include "traits.hpp"
|
||||
#include "optional.hpp"
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
namespace sol {
|
||||
|
@ -85,6 +86,8 @@ inline int c_trampoline(lua_State* L, lua_CFunction f) {
|
|||
}
|
||||
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; }
|
||||
|
||||
|
@ -168,6 +171,67 @@ enum class type : int {
|
|||
table | boolean | function | userdata | lightuserdata
|
||||
};
|
||||
|
||||
enum class meta_function {
|
||||
construct,
|
||||
index,
|
||||
new_index,
|
||||
mode,
|
||||
call,
|
||||
metatable,
|
||||
to_string,
|
||||
length,
|
||||
unary_minus,
|
||||
addition,
|
||||
subtraction,
|
||||
multiplication,
|
||||
division,
|
||||
modulus,
|
||||
power_of,
|
||||
involution = power_of,
|
||||
concatenation,
|
||||
equal_to,
|
||||
less_than,
|
||||
less_than_or_equal_to,
|
||||
garbage_collect,
|
||||
call_function = call,
|
||||
};
|
||||
|
||||
const std::array<std::string, 2> meta_variable_names = { {
|
||||
"__index",
|
||||
"__newindex",
|
||||
} };
|
||||
|
||||
const std::array<std::string, 21> meta_function_names = { {
|
||||
"new",
|
||||
"__index",
|
||||
"__newindex",
|
||||
"__mode",
|
||||
"__call",
|
||||
"__metatable",
|
||||
"__tostring",
|
||||
"__len",
|
||||
"__unm",
|
||||
"__add",
|
||||
"__sub",
|
||||
"__mul",
|
||||
"__div",
|
||||
"__mod",
|
||||
"__pow",
|
||||
"__concat",
|
||||
"__eq",
|
||||
"__lt",
|
||||
"__le",
|
||||
"__gc",
|
||||
} };
|
||||
|
||||
inline const std::string& name_of( meta_function mf ) {
|
||||
return meta_function_names[static_cast<int>(mf)];
|
||||
}
|
||||
|
||||
inline type type_of(lua_State* L, int index) {
|
||||
return static_cast<type>(lua_type(L, index));
|
||||
}
|
||||
|
||||
inline int type_panic(lua_State* L, int index, type expected, type actual) {
|
||||
return luaL_error(L, "stack index %d, expected %s, received %s", index,
|
||||
expected == type::poly ? "anything" : lua_typename(L, static_cast<int>(expected)),
|
||||
|
@ -195,13 +259,11 @@ inline void type_assert(lua_State* L, int index, type expected, type actual) {
|
|||
}
|
||||
|
||||
inline void type_assert(lua_State* L, int index, type expected) {
|
||||
int actual = lua_type(L, index);
|
||||
if(expected != type::poly && static_cast<int>(expected) != actual) {
|
||||
type_error(L, static_cast<int>(expected), actual);
|
||||
}
|
||||
type actual = type_of(L, index);
|
||||
type_assert(L, index, expected, actual);
|
||||
}
|
||||
|
||||
inline std::string type_name(lua_State*L, type t) {
|
||||
inline std::string type_name(lua_State* L, type t) {
|
||||
return lua_typename(L, static_cast<int>(t));
|
||||
}
|
||||
|
||||
|
@ -263,17 +325,8 @@ struct lua_type_of<nil_t> : std::integral_constant<type, type::nil> { };
|
|||
template <>
|
||||
struct lua_type_of<sol::error> : std::integral_constant<type, type::string> { };
|
||||
|
||||
template <>
|
||||
struct lua_type_of<table> : std::integral_constant<type, type::table> { };
|
||||
|
||||
template <>
|
||||
struct lua_type_of<global_table> : std::integral_constant<type, type::table> { };
|
||||
|
||||
template <>
|
||||
struct lua_type_of<stack_table> : std::integral_constant<type, type::table> { };
|
||||
|
||||
template <>
|
||||
struct lua_type_of<stack_global_table> : std::integral_constant<type, type::table> { };
|
||||
template <bool b, typename Base>
|
||||
struct lua_type_of<basic_table_core<b, Base>> : std::integral_constant<type, type::table> { };
|
||||
|
||||
template <>
|
||||
struct lua_type_of<reference> : std::integral_constant<type, type::poly> {};
|
||||
|
@ -305,6 +358,9 @@ struct lua_type_of<basic_userdata<Base>> : std::integral_constant<type, type::us
|
|||
template <>
|
||||
struct lua_type_of<lua_CFunction> : std::integral_constant<type, type::function> {};
|
||||
|
||||
template <>
|
||||
struct lua_type_of<std::remove_pointer_t<lua_CFunction>> : std::integral_constant<type, type::function> {};
|
||||
|
||||
template <typename Base>
|
||||
struct lua_type_of<basic_function<Base>> : std::integral_constant<type, type::function> {};
|
||||
|
||||
|
@ -364,10 +420,6 @@ template<typename T>
|
|||
inline type type_of() {
|
||||
return lua_type_of<meta::Unqualified<T>>::value;
|
||||
}
|
||||
|
||||
inline type type_of(lua_State* L, int index) {
|
||||
return static_cast<type>(lua_type(L, index));
|
||||
}
|
||||
} // sol
|
||||
|
||||
#endif // SOL_TYPES_HPP
|
||||
|
|
110
sol/usertype.hpp
110
sol/usertype.hpp
|
@ -34,60 +34,6 @@
|
|||
#include <map>
|
||||
|
||||
namespace sol {
|
||||
const std::array<std::string, 2> meta_variable_names = { {
|
||||
"__index",
|
||||
"__newindex",
|
||||
} };
|
||||
|
||||
const std::array<std::string, 21> meta_function_names = { {
|
||||
"new",
|
||||
"__index",
|
||||
"__newindex",
|
||||
"__mode",
|
||||
"__call",
|
||||
"__metatable",
|
||||
"__tostring",
|
||||
"__len",
|
||||
"__unm",
|
||||
"__add",
|
||||
"__sub",
|
||||
"__mul",
|
||||
"__div",
|
||||
"__mod",
|
||||
"__pow",
|
||||
"__concat",
|
||||
"__eq",
|
||||
"__lt",
|
||||
"__le",
|
||||
"__gc",
|
||||
"__call",
|
||||
} };
|
||||
|
||||
enum class meta_function {
|
||||
construct,
|
||||
index,
|
||||
new_index,
|
||||
mode,
|
||||
call,
|
||||
metatable,
|
||||
to_string,
|
||||
length,
|
||||
unary_minus,
|
||||
addition,
|
||||
subtraction,
|
||||
multiplication,
|
||||
division,
|
||||
modulus,
|
||||
power_of,
|
||||
involution = power_of,
|
||||
concatenation,
|
||||
equal_to,
|
||||
less_than,
|
||||
less_than_or_equal_to,
|
||||
garbage_collect,
|
||||
call_function,
|
||||
};
|
||||
|
||||
namespace usertype_detail {
|
||||
struct add_destructor_tag {};
|
||||
struct check_destructor_tag {};
|
||||
|
@ -151,6 +97,13 @@ inline void push_metatable(lua_State* L, bool needsindexfunction, std::vector<st
|
|||
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) {
|
||||
|
@ -193,7 +146,7 @@ inline void push_metatable(lua_State* L, bool needsindexfunction, std::vector<st
|
|||
// 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()));
|
||||
int up = push_upvalues(L, funcs);
|
||||
up = push_upvalues(L, funcs);
|
||||
functable.push_back({nullptr, nullptr});
|
||||
luaL_setfuncs(L, functable.data(), up);
|
||||
functable.pop_back();
|
||||
|
@ -244,12 +197,24 @@ private:
|
|||
return std::make_unique<function_detail::usertype_constructor_function<T, Functions...>>(std::move(func.set));
|
||||
}
|
||||
|
||||
template<typename Arg, typename... Args, typename Ret>
|
||||
std::unique_ptr<function_detail::base_function> make_function(const std::string&, Ret(*func)(Arg, Args...)) {
|
||||
typedef meta::Unqualified<std::remove_pointer_t<Arg>> Argu;
|
||||
static_assert(std::is_base_of<Argu, T>::value, "Any non-member-function must have a first argument which is covariant with the desired userdata type.");
|
||||
typedef std::decay_t<decltype(func)> function_type;
|
||||
return std::make_unique<function_detail::usertype_function<function_type, T>>(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<Fx>> function_type;
|
||||
return std::make_unique<function_detail::usertype_function<function_type, T>>(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<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 meta::bind_traits<Ret(Args...)> btraits;
|
||||
typedef typename btraits::template arg_at<0> Argu;
|
||||
typedef meta::Unqualified<std::remove_pointer_t<Argu>> Arg0;
|
||||
return make_free_function(std::is_base_of<Arg0, T>(), name, func);
|
||||
}
|
||||
|
||||
template<typename Base, typename Ret>
|
||||
|
@ -273,13 +238,24 @@ private:
|
|||
}
|
||||
|
||||
template<typename Fx>
|
||||
std::unique_ptr<function_detail::base_function> make_function(const std::string&, Fx&& func) {
|
||||
std::unique_ptr<function_detail::base_function> make_functor_function(std::true_type, const std::string&, Fx&& func) {
|
||||
typedef std::decay_t<meta::Unqualified<Fx>> function_type;
|
||||
return std::make_unique<function_detail::usertype_function<function_type, T>>(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<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<Fx> Fxu;
|
||||
typedef meta::tuple_element_t<0, typename meta::bind_traits<Fxu>::args_tuple_type> Arg0;
|
||||
typedef meta::Unqualified<std::remove_pointer_t<Arg0>> Argu;
|
||||
static_assert(std::is_base_of<Argu, T>::value, "Any non-member-function must have a first argument which is covariant with the desired usertype.");
|
||||
typedef std::decay_t<Fxu> function_type;
|
||||
return std::make_unique<function_detail::usertype_function<function_type, T>>(func);
|
||||
typedef meta::bind_traits<Fxu> btraits;
|
||||
typedef typename btraits::template arg_at<0> Argu;
|
||||
typedef meta::Unqualified<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, typename... Args>
|
||||
|
|
|
@ -442,6 +442,14 @@ TEST_CASE("functions/all-kinds", "Register all kinds of functions, make sure the
|
|||
}
|
||||
};
|
||||
|
||||
struct inner {
|
||||
const int z = 5653;
|
||||
};
|
||||
|
||||
struct nested {
|
||||
inner i;
|
||||
};
|
||||
|
||||
auto a = []() { return 500; };
|
||||
auto b = [&]() { return 501; };
|
||||
auto c = [&]() { return 502; };
|
||||
|
@ -568,6 +576,22 @@ N = n(1, 2, 3)
|
|||
REQUIRE( M1 == 256 );
|
||||
|
||||
REQUIRE( N == 13 );
|
||||
|
||||
// Work that compiler, WORK IT!
|
||||
lua.set("o", &test_1::bark);
|
||||
lua.set("p", test_1::x_bark);
|
||||
lua.set("q", sol::c_call<decltype(&test_1::bark_mem), &test_1::bark_mem>);
|
||||
lua.set("r", &test_2::a);
|
||||
lua.set("s", sol::readonly(&test_2::a));
|
||||
lua.set_function("t", sol::readonly(&test_2::a), test_2());
|
||||
lua.set_function("u", &nested::i, nested());
|
||||
lua.set("v", &nested::i);
|
||||
lua.set("nested", nested());
|
||||
lua.set("inner", inner());
|
||||
REQUIRE_THROWS(lua.script("s(o2, 2)"));
|
||||
REQUIRE_THROWS(lua.script("t(2)"));
|
||||
REQUIRE_THROWS(lua.script("u(inner)"));
|
||||
REQUIRE_THROWS(lua.script("v(nested, inner)"));
|
||||
}
|
||||
|
||||
TEST_CASE("simple/call-with-parameters", "Lua function is called with a few parameters from C++") {
|
||||
|
|
77
tests.cpp
77
tests.cpp
|
@ -181,6 +181,10 @@ int factory_test::num_saved = 0;
|
|||
int factory_test::num_killed = 0;
|
||||
const int factory_test::true_a = 156;
|
||||
|
||||
bool something() {
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down into a value if we desire") {
|
||||
|
||||
sol::state lua;
|
||||
|
@ -867,3 +871,76 @@ TEST_CASE("usertype/reference-and-constness", "Make sure constness compiles prop
|
|||
REQUIRE_THROWS(lua.script("f(n, 50)"));
|
||||
REQUIRE_THROWS(lua.script("o.n = 25"));
|
||||
}
|
||||
|
||||
TEST_CASE("usertype/readonly-and-static-functions", "Check if static functions can be called on userdata and from their originating (meta)tables") {
|
||||
struct bark {
|
||||
int var = 50;
|
||||
|
||||
void func() {}
|
||||
|
||||
int operator()(int x) {
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
sol::state lua;
|
||||
lua.open_libraries(sol::lib::base);
|
||||
lua.new_usertype<bark>("bark",
|
||||
"var", &bark::var,
|
||||
"var2", sol::readonly( &bark::var ),
|
||||
"something", something,
|
||||
"something2", [](int x, int y) { return x + y; },
|
||||
"func", &bark::func,
|
||||
sol::meta_function::call_function, &bark::operator()
|
||||
);
|
||||
|
||||
bark b;
|
||||
lua.set("b", &b);
|
||||
|
||||
sol::table b_table = lua["b"];
|
||||
sol::function member_func = b_table["func"];
|
||||
sol::function s = b_table["something"];
|
||||
sol::function s2 = b_table["something2"];
|
||||
|
||||
sol::table b_metatable = b_table[sol::metatable_key];
|
||||
bool isvalidmt = b_metatable.valid();
|
||||
REQUIRE(isvalidmt);
|
||||
sol::function b_call = b_metatable["__call"];
|
||||
sol::function b_as_function = lua["b"];
|
||||
|
||||
int x = b_as_function(1);
|
||||
int y = b_call(b, 1);
|
||||
bool z = s();
|
||||
int w = s2(2, 3);
|
||||
REQUIRE(x == 1);
|
||||
REQUIRE(y == 1);
|
||||
REQUIRE(z);
|
||||
REQUIRE(w == 5);
|
||||
|
||||
lua.script(R"(
|
||||
lx = b(1)
|
||||
ly = getmetatable(b).__call(b, 1)
|
||||
lz = b.something()
|
||||
lz2 = bark.something()
|
||||
lw = b.something2(2, 3)
|
||||
lw2 = bark.something2(2, 3)
|
||||
)");
|
||||
|
||||
int lx = lua["lx"];
|
||||
int ly = lua["ly"];
|
||||
bool lz = lua["lz"];
|
||||
int lw = lua["lw"];
|
||||
bool lz2 = lua["lz2"];
|
||||
int lw2 = lua["lw2"];
|
||||
REQUIRE(lx == 1);
|
||||
REQUIRE(ly == 1);
|
||||
REQUIRE(lz);
|
||||
REQUIRE(lz2);
|
||||
REQUIRE(lw == 5);
|
||||
REQUIRE(lw2 == 5);
|
||||
REQUIRE(lx == ly);
|
||||
REQUIRE(lz == lz2);
|
||||
REQUIRE(lw == lw2);
|
||||
|
||||
REQUIRE_THROWS(lua.script("b.var2 = 2"));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user