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:
ThePhD 2016-04-11 01:02:43 -04:00
parent a9a1903339
commit 42f4455383
22 changed files with 432 additions and 129 deletions

View File

@ -87,7 +87,7 @@ Explanations for a few categories are below (rest are self-explanatory).
+===========================+=============+============+==========+=========+==========+===========+===========+================+==========+==========+===========+=================+ +===========================+=============+============+==========+=========+==========+===========+===========+================+==========+==========+===========+=================+
| optional | ~ | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | | optional | ~ | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+ +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+
| tables | ~ | ~ | | ✔ | ✔ | ✔ | ~ | ✔ | ✔ | ✗ | ✗ | ~ | | tables | ~ | ~ | ~ | ✔ | ✔ | ✔ | ~ | ✔ | ✔ | ✗ | ✗ | ~ |
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+ +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+
| table chaining | ~ | ~ | ~ | ✔ | ✔ | ✔ | ✗ | ✔ | ✔ | ✗ | ✗ | ~ | | table chaining | ~ | ~ | ~ | ✔ | ✔ | ✔ | ✗ | ✔ | ✔ | ✗ | ✗ | ~ |
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+ +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+

View File

@ -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 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! 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!

View File

@ -124,7 +124,7 @@ Writing gets a lot simpler. Even without scripting a file or a string, you can r
sol::state lua; sol::state lua;
// open those basic lua libraries again, like print() etc. // 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 // value in the global table
lua["bark"] = 50; 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!" 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 // Using a "Raw String Literal" to have multi-line goodness: http://en.cppreference.com/w/cpp/language/string_literal
lua.script(R"( lua.script(R"(

View File

@ -67,14 +67,14 @@ private:
} }
public: 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() noexcept = default;
coroutine(const coroutine&) noexcept = default; coroutine(const coroutine&) noexcept = default;
coroutine& operator=(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 { call_status status() const noexcept {
return stats; return stats;

View File

@ -73,7 +73,7 @@ public:
basic_function& operator=(basic_function&& ) = default; basic_function& operator=(basic_function&& ) = default;
basic_function(lua_State* L, int index = -1): base_t(L, index) { basic_function(lua_State* L, int index = -1): base_t(L, index) {
#ifdef SOL_CHECK_ARGUMENTS #ifdef SOL_CHECK_ARGUMENTS
type_assert(L, index, type::function); stack::check<basic_function>(L, index, type_panic);
#endif // Safety #endif // Safety
} }

View File

@ -46,6 +46,13 @@ function_arguments<Sig, Args...> function_args( Args&&... args ) {
return function_arguments<Sig, Args...>(std::forward<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 { namespace stack {
template<typename... Sigs> template<typename... Sigs>
struct pusher<function_sig<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)...); 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) { static void select_member_variable(std::true_type, lua_State* L, Fx&& fx) {
typedef typename meta::bind_traits<meta::Unqualified<Fx>>::object_type C; typedef typename meta::bind_traits<meta::Unqualified<Fx>>::object_type C;
lua_CFunction freefunc = &function_detail::upvalue_this_member_variable<C, Fx>::call; 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)...); 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) { static void select_member_function(std::true_type, lua_State* L, Fx&& fx) {
typedef typename meta::bind_traits<meta::Unqualified<Fx>>::object_type C; typedef typename meta::bind_traits<meta::Unqualified<Fx>>::object_type C;
lua_CFunction freefunc = &function_detail::upvalue_this_member_function<C, Fx>::call; 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> template<typename Signature>
struct pusher<std::function<Signature>> { struct pusher<std::function<Signature>> {
static int push(lua_State* L, std::function<Signature> fx) { 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));
} }
}; };

View File

@ -43,7 +43,7 @@ struct constructor_match {
constructor_match(T* obj) : obj(obj) {} constructor_match(T* obj) : obj(obj) {}
template <typename Fx, std::size_t I, typename... R, typename... Args> 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{}; default_construct func{};
return stack::call_into_lua<0, false>(r, a, L, start, func, obj); 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...) {} usertype_constructor_function(Functions... fxs) : overloads(fxs...) {}
template <typename Fx, std::size_t I, typename... R, typename... Args> 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; static const auto& meta = usertype_traits<T>::metatable;
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T))); T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
T*& referencepointer = *pointerpointer; T*& referencepointer = *pointerpointer;

View File

@ -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> template<typename T, typename Function>
struct upvalue_member_variable { struct upvalue_member_variable {
typedef std::remove_pointer_t<std::decay_t<Function>> function_type; typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
@ -97,8 +121,7 @@ struct upvalue_member_variable {
stack::push(L, (mem.*var)); stack::push(L, (mem.*var));
return 1; return 1;
case 1: case 1:
(mem.*var) = stack::get<typename traits_type::return_type>(L, 1); set_variable<1, typename traits_type::return_type>(meta::Not<std::is_const<typename traits_type::return_type>>(), L, mem, var);
lua_pop(L, 1);
return 0; return 0;
default: default:
return luaL_error(L, "sol: incorrect number of arguments to member variable function"); 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)); stack::push(L, (mem.*var));
return 1; return 1;
case 2: case 2:
(mem.*var) = stack::get<typename traits_type::return_type>(L, 2); set_variable<2, typename traits_type::return_type>(meta::Not<std::is_const<typename traits_type::return_type>>(), L, mem, var);
lua_pop(L, 2);
return 0; return 0;
default: default:
return luaL_error(L, "sol: incorrect number of arguments to member variable function"); return luaL_error(L, "sol: incorrect number of arguments to member variable function");

View File

@ -26,6 +26,26 @@
namespace sol { namespace sol {
namespace function_detail { 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> template<typename Func>
struct functor_function : public base_function { struct functor_function : public base_function {
typedef meta::Unwrapped<meta::Unqualified<Func>> Function; typedef meta::Unwrapped<meta::Unqualified<Func>> Function;

View File

@ -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 traits::args_type args_type;
typedef typename args_type::indices args_indices; typedef typename args_type::indices args_indices;
// compile-time eliminate any functions that we know ahead of time are of improper arity // 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)...); 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) { 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)) { 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 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 } // internals
@ -90,7 +90,7 @@ struct overloaded_function : base_function {
} }
template <typename Fx, std::size_t I, typename... R, typename... Args> 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); auto& func = std::get<I>(overloads);
return stack::call_into_lua<0, false>(r, a, L, start, func); 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)) {} usertype_overloaded_function(std::tuple<Functions...> set) : overloads(std::move(set)) {}
template <typename Fx, std::size_t I, typename... R, typename... Args> 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); auto& func = std::get<I>(overloads);
func.item = detail::ptr(stack::get<T>(L, 1)); func.item = detail::ptr(stack::get<T>(L, 1));
return stack::call_into_lua<0, false>(r, a, L, start, func); return stack::call_into_lua<0, false>(r, a, L, start, func);

View File

@ -64,7 +64,7 @@ struct usertype_function_core : public base_function {
} }
template<typename... Args, std::size_t Start> 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); stack::call_into_lua(tr, ta, L, static_cast<int>(Start), fx);
int nargs = static_cast<int>(sizeof...(Args)); int nargs = static_cast<int>(sizeof...(Args));
lua_pop(L, nargs); lua_pop(L, nargs);
@ -72,7 +72,7 @@ struct usertype_function_core : public base_function {
} }
template<typename... Ret, typename... Args, std::size_t Start> 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); decltype(auto) r = stack::call(tr, ta, L, static_cast<int>(Start), fx);
int nargs = static_cast<int>(sizeof...(Args)); int nargs = static_cast<int>(sizeof...(Args));
lua_pop(L, nargs); lua_pop(L, nargs);
@ -94,7 +94,7 @@ struct usertype_function : public usertype_function_core<Function, Tp> {
int prelude(lua_State* L) { int prelude(lua_State* L) {
this->fx.item = detail::ptr(stack::get<T>(L, 1)); 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 { 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) { 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) { 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); this->fx.item = stack::get<T*>(L, 1);
switch(argcount) { switch(argcount) {
case 2: 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: case 3:
return set_variable(meta::Not<std::is_const<return_type>>(), L); return set_variable(meta::Not<std::is_const<return_type>>(), L);
default: default:

View File

@ -137,7 +137,7 @@ public:
basic_protected_function& operator=(basic_protected_function&& ) = default; 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()) { basic_protected_function(lua_State* L, int index = -1): base_t(L, index), error_handler(get_default_handler()) {
#ifdef SOL_CHECK_ARGUMENTS #ifdef SOL_CHECK_ARGUMENTS
type_assert(L, index, type::function); stack::check<basic_protected_function>(L, index, type_panic);
#endif // Safety #endif // Safety
} }

View File

@ -151,12 +151,60 @@ struct checker<T, type::lightuserdata, C> {
template <typename T, typename C> template <typename T, typename C>
struct checker<non_null<T>, type::userdata, C> : checker<T, lua_type_of<T>::value, C> {}; struct checker<non_null<T>, type::userdata, C> : checker<T, lua_type_of<T>::value, C> {};
template <type X, typename C> template <typename C>
struct checker<lua_CFunction, X, C> : stack_detail::basic_check<type::function, lua_iscfunction> {}; struct checker<lua_CFunction, type::function, C> : stack_detail::basic_check<type::function, lua_iscfunction> {};
template <type X, typename C> template <typename C>
struct checker<std::remove_pointer_t<lua_CFunction>, X, C> : checker<lua_CFunction, X, C> {}; struct checker<std::remove_pointer_t<lua_CFunction>, type::function, C> : checker<lua_CFunction, type::function, C> {};
template <type X, typename C> template <typename C>
struct checker<c_closure, X, C> : checker<lua_CFunction, X, 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> template <typename T, typename C>
struct checker<T*, type::userdata, C> { struct checker<T*, type::userdata, C> {

View File

@ -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> template <typename... Args, bool b, typename C>
struct field_getter<std::tuple<Args...>, b, C> { struct field_getter<std::tuple<Args...>, b, C> {
template <std::size_t... I, typename Keys> 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> template <typename T>
struct field_setter<T, true, std::enable_if_t<meta::is_c_str<T>::value>> { struct field_setter<T, true, std::enable_if_t<meta::is_c_str<T>::value>> {
template <typename Key, typename Value> template <typename Key, typename Value>

View File

@ -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<> template<>
struct getter<nullopt_t> { struct getter<nullopt_t> {
static nullopt_t get(lua_State*, int = -1) { static nullopt_t get(lua_State*, int = -1) {

View File

@ -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<> template<>
struct pusher<std::remove_pointer_t<lua_CFunction>> { struct pusher<std::remove_pointer_t<lua_CFunction>> {
static int push(lua_State* L, lua_CFunction func, int n = 0) { static int push(lua_State* L, lua_CFunction func, int n = 0) {

View File

@ -159,7 +159,7 @@ public:
basic_table_core(stack_reference&& r) : basic_table_core(r.lua_state(), r.stack_index()) {} 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 ) { basic_table_core( lua_State* L, int index = -1 ) : base_t( L, index ) {
#ifdef SOL_CHECK_ARGUMENTS #ifdef SOL_CHECK_ARGUMENTS
type_assert( L, index, type::table ); stack::check<basic_table_core>(L, index, type_panic);
#endif // Safety #endif // Safety
} }

View File

@ -29,7 +29,7 @@
namespace sol { namespace sol {
template<std::size_t I> 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 { namespace meta {
template<typename T> template<typename T>
@ -120,6 +120,9 @@ using Unqualified = std::remove_cv_t<std::remove_reference_t<T>>;
template<typename T> template<typename T>
using Unwrapped = typename unwrapped<T>::type; 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> template <std::size_t N, typename Tuple>
using tuple_element_t = std::tuple_element_t<N, Unqualified<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 { 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> template<typename Signature, bool b = has_deducible_signature<Signature>::value>
struct fx_traits; struct fx_traits;
@ -258,7 +270,7 @@ struct fx_traits<R(T::*)(Args...), false> {
typedef R return_type; typedef R return_type;
typedef std::remove_pointer_t<free_function_pointer_type> signature_type; typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
template<std::size_t i> 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> template<typename T, typename R, typename... Args>
@ -274,7 +286,7 @@ struct fx_traits<R(T::*)(Args...) const, false> {
typedef R return_type; typedef R return_type;
typedef std::remove_pointer_t<free_function_pointer_type> signature_type; typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
template<std::size_t i> 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> template<typename R, typename... Args>
@ -289,7 +301,7 @@ struct fx_traits<R(Args...), false> {
typedef R return_type; typedef R return_type;
typedef std::remove_pointer_t<free_function_pointer_type> signature_type; typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
template<std::size_t i> 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> template<typename R, typename... Args>
@ -304,7 +316,7 @@ struct fx_traits<R(*)(Args...), false> {
typedef R return_type; typedef R return_type;
typedef std::remove_pointer_t<free_function_pointer_type> signature_type; typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
template<std::size_t i> 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> 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(*function_pointer_type)(Arg);
typedef R(*free_function_pointer_type)(Arg); typedef R(*free_function_pointer_type)(Arg);
template<std::size_t i> 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 } // meta_detail

View File

@ -25,6 +25,7 @@
#include "compatibility.hpp" #include "compatibility.hpp"
#include "traits.hpp" #include "traits.hpp"
#include "optional.hpp" #include "optional.hpp"
#include <array>
#include <string> #include <string>
namespace sol { namespace sol {
@ -85,6 +86,8 @@ inline int c_trampoline(lua_State* L, lua_CFunction f) {
} }
struct nil_t {}; struct nil_t {};
const nil_t nil {}; 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 true; }
inline bool operator!=(nil_t, nil_t) { return false; } inline bool operator!=(nil_t, nil_t) { return false; }
@ -168,6 +171,67 @@ enum class type : int {
table | boolean | function | userdata | lightuserdata 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) { 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, return luaL_error(L, "stack index %d, expected %s, received %s", index,
expected == type::poly ? "anything" : lua_typename(L, static_cast<int>(expected)), 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) { inline void type_assert(lua_State* L, int index, type expected) {
int actual = lua_type(L, index); type actual = type_of(L, index);
if(expected != type::poly && static_cast<int>(expected) != actual) { type_assert(L, index, expected, actual);
type_error(L, static_cast<int>(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)); 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 <> template <>
struct lua_type_of<sol::error> : std::integral_constant<type, type::string> { }; struct lua_type_of<sol::error> : std::integral_constant<type, type::string> { };
template <> template <bool b, typename Base>
struct lua_type_of<table> : std::integral_constant<type, type::table> { }; struct lua_type_of<basic_table_core<b, Base>> : 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 <> template <>
struct lua_type_of<reference> : std::integral_constant<type, type::poly> {}; 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 <> template <>
struct lua_type_of<lua_CFunction> : std::integral_constant<type, type::function> {}; 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> template <typename Base>
struct lua_type_of<basic_function<Base>> : std::integral_constant<type, type::function> {}; struct lua_type_of<basic_function<Base>> : std::integral_constant<type, type::function> {};
@ -364,10 +420,6 @@ template<typename T>
inline type type_of() { inline type type_of() {
return lua_type_of<meta::Unqualified<T>>::value; 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 } // sol
#endif // SOL_TYPES_HPP #endif // SOL_TYPES_HPP

View File

@ -34,60 +34,6 @@
#include <map> #include <map>
namespace sol { 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 { namespace usertype_detail {
struct add_destructor_tag {}; struct add_destructor_tag {};
struct check_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) { if (funcs.size() < 1 && metafunctable.size() < 2) {
return; 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 // Metamethods directly on the metatable itself
int metaup = push_upvalues(L, funcs); int metaup = push_upvalues(L, funcs);
switch (metastage) { 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 // Otherwise, we use quick, fast table indexing for methods
// gives us performance boost in calling them // gives us performance boost in calling them
lua_createtable(L, 0, static_cast<int>(functable.size())); 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}); functable.push_back({nullptr, nullptr});
luaL_setfuncs(L, functable.data(), up); luaL_setfuncs(L, functable.data(), up);
functable.pop_back(); functable.pop_back();
@ -244,14 +197,26 @@ private:
return std::make_unique<function_detail::usertype_constructor_function<T, Functions...>>(std::move(func.set)); return std::make_unique<function_detail::usertype_constructor_function<T, Functions...>>(std::move(func.set));
} }
template<typename Arg, typename... Args, typename Ret> template<typename Fx>
std::unique_ptr<function_detail::base_function> make_function(const std::string&, Ret(*func)(Arg, Args...)) { std::unique_ptr<function_detail::base_function> make_free_function(std::true_type, const std::string&, Fx&& func) {
typedef meta::Unqualified<std::remove_pointer_t<Arg>> Argu; typedef std::decay_t<meta::Unqualified<Fx>> function_type;
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); 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> template<typename Base, typename Ret>
std::unique_ptr<function_detail::base_function> make_variable_function(std::true_type, const std::string&, Ret Base::* func) { 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 function must be part of the class"); static_assert(std::is_base_of<Base, T>::value, "Any registered function must be part of the class");
@ -273,15 +238,26 @@ private:
} }
template<typename Fx> 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 meta::Unqualified<Fx> Fxu; typedef std::decay_t<meta::Unqualified<Fx>> function_type;
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); 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::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> template<std::size_t N, typename... Args>
void build_function(std::string funcname, constructors<Args...>) { void build_function(std::string funcname, constructors<Args...>) {
functionnames.push_back(std::move(funcname)); functionnames.push_back(std::move(funcname));

View File

@ -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 a = []() { return 500; };
auto b = [&]() { return 501; }; auto b = [&]() { return 501; };
auto c = [&]() { return 502; }; auto c = [&]() { return 502; };
@ -568,6 +576,22 @@ N = n(1, 2, 3)
REQUIRE( M1 == 256 ); REQUIRE( M1 == 256 );
REQUIRE( N == 13 ); 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++") { TEST_CASE("simple/call-with-parameters", "Lua function is called with a few parameters from C++") {

View File

@ -181,6 +181,10 @@ int factory_test::num_saved = 0;
int factory_test::num_killed = 0; int factory_test::num_killed = 0;
const int factory_test::true_a = 156; 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") { TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down into a value if we desire") {
sol::state lua; 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("f(n, 50)"));
REQUIRE_THROWS(lua.script("o.n = 25")); 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"));
}