From febfdbadb7d47b83e98e75db5895cd7bdaeeb2f7 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sat, 9 Jul 2016 03:43:51 -0400 Subject: [PATCH] protect could use some additional optimizations for space, but I'm kinna tired. --- docs/source/api/api-top.rst | 2 + docs/source/api/protect.rst | 33 ++++++++++++++++ docs/source/api/readonly.rst | 11 ++++++ docs/source/safety.rst | 3 +- docs/source/tutorial/all-the-things.rst | 2 +- sol/function_types_overloaded.hpp | 2 +- sol/function_types_stateless.hpp | 50 +++++++++++++++++++++---- test_functions.cpp | 8 ++++ 8 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 docs/source/api/protect.rst create mode 100644 docs/source/api/readonly.rst diff --git a/docs/source/api/api-top.rst b/docs/source/api/api-top.rst index 2dcf1d12..620d1135 100644 --- a/docs/source/api/api-top.rst +++ b/docs/source/api/api-top.rst @@ -21,6 +21,8 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo stack_reference make_reference overload + protect + readonly property proxy resolve diff --git a/docs/source/api/protect.rst b/docs/source/api/protect.rst new file mode 100644 index 00000000..573fb7dd --- /dev/null +++ b/docs/source/api/protect.rst @@ -0,0 +1,33 @@ +protect +======= +Routine to mark a function / variable as requiring safety +--------------------------------------------------------- + +.. code-block:: cpp + + template + auto protect( T&& value ); + +``protect( my_func )`` allows you to protect a function call or member variable call when it is being set to Lua. It can be used with usertypes or when just setting a function into Sol. Below is an example that demonstrates that a call that would normally not error without :doc:`Safety features turn on<../safety>` that instead errors and makes the Lua safety-call wrapper ``pcall`` fail: + +.. code-block:: cpp + + struct protect_me { + int gen(int x) { + return x; + } + }; + + sol::state lua; + lua.open_libraries(sol::lib::base); + lua.new_usertype("protect_me", + "gen", sol::protect( &protect_me::gen ) + ); + + lua.script(R"__( + pm = protect_me.new() + value = pcall(pm.gen,pm) + )__"); + ); + bool value = lua["value"]; + // value == false diff --git a/docs/source/api/readonly.rst b/docs/source/api/readonly.rst new file mode 100644 index 00000000..bdf1718a --- /dev/null +++ b/docs/source/api/readonly.rst @@ -0,0 +1,11 @@ +readonly +======== +Routine to mark a member variable as read-only +---------------------------------------------- + +.. code-block:: cpp + + template + auto readonly( T&& value ); + +The goal of read-only is to protect a variable set on a usertype or set as a function into Lua. Simply wrap it around a ``&my_class::my_member_variable`` in the appropriate place to use it. diff --git a/docs/source/safety.rst b/docs/source/safety.rst index 9e798c73..382b3bbb 100644 --- a/docs/source/safety.rst +++ b/docs/source/safety.rst @@ -3,6 +3,8 @@ safety Sol was designed to be correct and fast, and in the pursuit of both uses the regular ``lua_to{x}`` functions of Lua rather than the checking versions (``lua_check{X}``) functions. The API defaults to paranoidly-safe alternatives if you have a ``#define SOL_CHECK_ARGUMENTS`` before you include Sol, or if you pass the ``SOL_CHECK_ARGUMENTS`` define on the build command for your build system. By default, it is off and remains off unless you define this, even in debug mode. The same goes for ``#define SOL_SAFE_USERTYPE``. +Note that you can obtain safety with regards to functions you bind by using the :doc:`protect` wrapper around function/variable bindings you set into Lua. + ``SOL_SAFE_USERTYPE`` triggers the following change: * If the userdata to a usertype function is nill, will trigger an error instead of letting things go through and letting the system segfault @@ -13,7 +15,6 @@ Sol was designed to be correct and fast, and in the pursuit of both uses the reg Remember that if you want these features, you must explicitly turn them on. Additionally, you can have basic boolean checks when using the API by just converting to a :doc:`sol::optional\` when necessary. Tests are compiled with this on to ensure everythign is going as expected. - Finally, some warnings that may help with errors when working with Sol: .. warning:: diff --git a/docs/source/tutorial/all-the-things.rst b/docs/source/tutorial/all-the-things.rst index 59e5a0cd..3ce46fbc 100644 --- a/docs/source/tutorial/all-the-things.rst +++ b/docs/source/tutorial/all-the-things.rst @@ -151,7 +151,7 @@ You can erase things by setting it to ``nullptr`` or ``sol::nil``. // second_try == 322 -Note that if its a :doc:`userdata/usertype<../doc/usertype>` for a C++ type, the destructor will run only when the garbage collector deems it appropriate to destroy the memory. If you are relying on the destructor being run when its set to ``sol::nil``, you're probably committing a mistake. +Note that if its a :doc:`userdata/usertype<../api/usertype>` for a C++ type, the destructor will run only when the garbage collector deems it appropriate to destroy the memory. If you are relying on the destructor being run when its set to ``sol::nil``, you're probably committing a mistake. tables ------ diff --git a/sol/function_types_overloaded.hpp b/sol/function_types_overloaded.hpp index f38c4428..cfcfdebe 100644 --- a/sol/function_types_overloaded.hpp +++ b/sol/function_types_overloaded.hpp @@ -45,7 +45,7 @@ namespace sol { template int call(types, index_value, types, types, lua_State* L, int, int) { auto& func = std::get(overloads); - return call_detail::call_wrapped(L, func); + return call_detail::call_wrapped(L, func); } int operator()(lua_State* L) { diff --git a/sol/function_types_stateless.hpp b/sol/function_types_stateless.hpp index 017ee471..0b880975 100644 --- a/sol/function_types_stateless.hpp +++ b/sol/function_types_stateless.hpp @@ -33,15 +33,16 @@ namespace sol { static int real_call(lua_State* L) { auto udata = stack::stack_detail::get_as_upvalues(L); - function_type* f = udata.first; - return call_detail::call_wrapped(L, f); + function_type* fx = udata.first; + int r = stack::call_into_lua(meta::tuple_types(), typename traits_type::args_list(), L, 1, fx); + return r; } static int call(lua_State* L) { return detail::static_trampoline<(&real_call)>(L); } - int operator()(lua_State* L) const { + int operator()(lua_State* L) { return call(L); } }; @@ -61,7 +62,10 @@ namespace sol { auto objdata = stack::stack_detail::get_as_upvalues(L, memberdata.second); function_type& memfx = memberdata.first; auto& item = *objdata.first; - return call_detail::call_wrapped(L, memfx, item); + auto fx = [&item, &memfx](auto&&... args) -> typename traits_type::return_type { + return (item.*memfx)(std::forward(args)...); + }; + return stack::call_into_lua(meta::tuple_types(), typename traits_type::args_list(), L, 1, fx); } static int call(lua_State* L) { @@ -73,6 +77,30 @@ namespace sol { } }; + template + int set_assignable(std::false_type, lua_State* L, M&, V&) { + lua_pop(L, N); + return luaL_error(L, "sol: cannot write to this type: copy assignment/constructor not available"); + } + + template + int set_assignable(std::true_type, lua_State* L, M& mem, V& var) { + (mem.*var) = stack::get(L, N); + lua_pop(L, N); + return 0; + } + + template + int set_variable(std::true_type, lua_State* L, M& mem, V& var) { + return set_assignable(std::is_assignable, R>(), L, mem, var); + } + + template + int set_variable(std::false_type, lua_State* L, M&, V&) { + lua_pop(L, N); + return luaL_error(L, "sol: cannot write to a const variable"); + } + template struct upvalue_member_variable { typedef std::remove_pointer_t> function_type; @@ -90,9 +118,11 @@ namespace sol { function_type& var = memberdata.first; switch (lua_gettop(L)) { case 0: - return call_detail::call_wrapped(L, var, mem); + stack::push(L, (mem.*var)); + return 1; case 1: - return call_detail::call_wrapped(L, var, mem); + set_variable<1, typename traits_type::return_type>(meta::neg>(), L, mem, var); + return 0; default: return luaL_error(L, "sol: incorrect number of arguments to member variable function"); } @@ -138,12 +168,16 @@ namespace sol { // Layout: // idx 1...n: verbatim data of member variable pointer auto memberdata = stack::stack_detail::get_as_upvalues(L, 1); + auto& mem = stack::get(L, 1); function_type& var = memberdata.first; switch (lua_gettop(L)) { case 1: - return call_detail::call_wrapped(L, var); + lua_pop(L, 1); + stack::push(L, (mem.*var)); + return 1; case 2: - return call_detail::call_wrapped(L, var); + set_variable<2, typename traits_type::return_type>(meta::neg>(), L, mem, var); + return 0; default: return luaL_error(L, "sol: incorrect number of arguments to member variable function"); } diff --git a/test_functions.cpp b/test_functions.cpp index 3403b8d9..c388db28 100644 --- a/test_functions.cpp +++ b/test_functions.cpp @@ -528,20 +528,28 @@ I = i(o1) J0 = j() j(24) J1 = j() + )"); + lua.script(R"( K0 = k(o2) k(o2, 1024) K1 = k(o2) + )"); + lua.script(R"( L0 = l(o1) l(o1, 678) L1 = l(o1) + )"); + lua.script(R"( M0 = m() m(256) M1 = m() + )"); + lua.script(R"( N = n(1, 2, 3) )"); int ob, A, B, C, D, F, G0, G1, H, I, J0, J1, K0, K1, L0, L1, M0, M1, N;