diff --git a/docs/source/api/api-top.rst b/docs/source/api/api-top.rst index 9443ef85..a1dfda52 100644 --- a/docs/source/api/api-top.rst +++ b/docs/source/api/api-top.rst @@ -12,6 +12,7 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo compatibility coroutine + c_call error function protected_function diff --git a/docs/source/api/c_call.rst b/docs/source/api/c_call.rst new file mode 100644 index 00000000..36395eea --- /dev/null +++ b/docs/source/api/c_call.rst @@ -0,0 +1,71 @@ +c_call +====== +Templated type to transport functions through templates +------------------------------------------------------- + +.. code-block:: cpp + + template + int c_call (lua_State* L); + + template + int c_call (lua_State* L); + +The goal of ``sol::c_call<...>`` is to provide a way to wrap a function and transport it through a compile-time context. This enables faster speed at the cost of a much harder to read / poorer interface. ``sol::c_call`` expects a type for its first template argument, and a value of the previously provided type for the second template argument. To make a compile-time transported overloaded function, specify multiple functions in the same ``type, value`` pairing, but put it inside of a ``sol::wrap``. Note that is can also be placed into the argument list for a :doc:`usertype` as well. + +It is advisable for the user to consider making a macro to do the necessary ``decltype( &function_name, ), function_name``. Sol does not provide one because many codebases already have `one similar to this`_. + +Here's an example below of various ways to use ``sol::c_call``: + +.. code-block:: cpp + :linenos: + :caption: Compile-time transported function calls + + #include "sol.hpp" + + int f1(int) { return 32; } + int f2(int, int) { return 1; } + struct fer { + double f3(int, int) { + return 2.5; + } + }; + + + int main() { + + sol::state lua; + // overloaded function f + lua.set("f", sol::c_call, sol::wrap, sol::wrap>); + // singly-wrapped function + lua.set("g", sol::c_call>); + // without the 'sol::wrap' boilerplate + lua.set("h", sol::c_call); + // object used for the 'fer' member function call + lua.set("obj", fer()); + + // call them like any other bound function + lua.script("r1 = f(1)"); + lua.script("r2 = f(1, 2)"); + lua.script("r3 = f(obj, 1, 2)"); + lua.script("r4 = g(1)"); + lua.script("r5 = h(1, 2)"); + + // get the results and see + // if it worked out + int r1 = lua["r1"]; + // r1 == 32 + int r2 = lua["r2"]; + // r2 == 1 + double r3 = lua["r3"]; + // r3 == 2.5 + int r4 = lua["r4"]; + // r4 == 32 + int r5 = lua["r5"]; + // r5 == 1 + + return 0; + } + + +.. _one similar to this: http://stackoverflow.com/a/5628222/5280922 diff --git a/docs/source/api/error.rst b/docs/source/api/error.rst index 32677756..33212f57 100644 --- a/docs/source/api/error.rst +++ b/docs/source/api/error.rst @@ -1,7 +1,7 @@ error ===== -the single exception type -------------------------- +the single error/exception type +------------------------------- .. code-block:: cpp diff --git a/sol/function_types_templated.hpp b/sol/function_types_templated.hpp index b1fb363e..5baf52c6 100644 --- a/sol/function_types_templated.hpp +++ b/sol/function_types_templated.hpp @@ -22,95 +22,126 @@ #ifndef SOL_FUNCTION_TYPES_TEMPLATED_HPP #define SOL_FUNCTION_TYPES_TEMPLATED_HPP -#include "stack.hpp" +#include "call.hpp" namespace sol { -namespace function_detail { - template - inline int call_wrapper_variable(std::false_type, lua_State* L) { - typedef meta::bind_traits> traits_type; - typedef typename traits_type::args_list args_list; - typedef meta::tuple_types return_type; - return stack::call_into_lua(return_type(), args_list(), L, 1, fx); - } + namespace function_detail { + template + inline int call_wrapper_variable(std::false_type, lua_State* L) { + typedef meta::bind_traits> traits_type; + typedef typename traits_type::args_list args_list; + typedef meta::tuple_types return_type; + return stack::call_into_lua(return_type(), args_list(), L, 1, fx); + } - template - inline int call_set_assignable(std::false_type, T&&, lua_State* L) { - lua_pop(L, 2); - return luaL_error(L, "cannot write to this type: copy assignment/constructor not available"); - } + template + inline int call_set_assignable(std::false_type, T&&, lua_State* L) { + lua_pop(L, 2); + return luaL_error(L, "cannot write to this type: copy assignment/constructor not available"); + } - template - inline int call_set_assignable(std::true_type, lua_State* L, T&& mem) { - (mem.*variable) = stack::get(L, 2); - lua_pop(L, 2); - return 0; - } + template + inline int call_set_assignable(std::true_type, lua_State* L, T&& mem) { + (mem.*variable) = stack::get(L, 2); + lua_pop(L, 2); + return 0; + } - template - inline int call_set_variable(std::false_type, lua_State* L, T&&) { - lua_pop(L, 2); - return luaL_error(L, "cannot write to a const variable"); - } + template + inline int call_set_variable(std::false_type, lua_State* L, T&&) { + lua_pop(L, 2); + return luaL_error(L, "cannot write to a const variable"); + } - template - inline int call_set_variable(std::true_type, lua_State* L, T&& mem) { - return call_set_assignable(std::is_assignable, R>(), L, std::forward(mem)); - } + template + inline int call_set_variable(std::true_type, lua_State* L, T&& mem) { + return call_set_assignable(std::is_assignable, R>(), L, std::forward(mem)); + } - template - inline int call_wrapper_variable(std::true_type, lua_State* L) { - typedef meta::bind_traits> traits_type; - typedef typename traits_type::object_type T; - typedef typename traits_type::return_type R; - auto& mem = stack::get(L, 1); - switch (lua_gettop(L)) { - case 1: { - decltype(auto) r = (mem.*variable); - lua_pop(L, 1); - stack::push_reference(L, std::forward(r)); - return 1; } - case 2: - return call_set_variable(meta::neg>(), L, mem); - default: - return luaL_error(L, "incorrect number of arguments to member variable function call"); - } - } + template + inline int call_wrapper_variable(std::true_type, lua_State* L) { + typedef meta::bind_traits> traits_type; + typedef typename traits_type::object_type T; + typedef typename traits_type::return_type R; + auto& mem = stack::get(L, 1); + switch (lua_gettop(L)) { + case 1: { + decltype(auto) r = (mem.*variable); + lua_pop(L, 1); + stack::push_reference(L, std::forward(r)); + return 1; } + case 2: + return call_set_variable(meta::neg>(), L, mem); + default: + return luaL_error(L, "incorrect number of arguments to member variable function call"); + } + } - template - inline int call_wrapper_function(std::false_type, lua_State* L) { - return call_wrapper_variable(std::is_member_object_pointer(), L); - } + template + inline int call_wrapper_function(std::false_type, lua_State* L) { + return call_wrapper_variable(std::is_member_object_pointer(), L); + } - template - inline int call_wrapper_function(std::true_type, lua_State* L) { - typedef meta::bind_traits> traits_type; - typedef typename traits_type::object_type T; - typedef typename traits_type::args_list args_list; - typedef typename traits_type::return_type return_type; - typedef meta::tuple_types return_type_list; - auto mfx = [&](auto&&... args) -> typename traits_type::return_type { - auto& member = stack::get(L, 1); - return (member.*fx)(std::forward(args)...); - }; - int n = stack::call_into_lua<1>(return_type_list(), args_list(), L, 2, mfx); - return n; - } + template + inline int call_wrapper_function(std::true_type, lua_State* L) { + typedef meta::bind_traits> traits_type; + typedef typename traits_type::object_type T; + typedef typename traits_type::args_list args_list; + typedef typename traits_type::return_type return_type; + typedef meta::tuple_types return_type_list; + auto mfx = [&](auto&&... args) -> typename traits_type::return_type { + auto& member = stack::get(L, 1); + return (member.*fx)(std::forward(args)...); + }; + int n = stack::call_into_lua<1>(return_type_list(), args_list(), L, 2, mfx); + return n; + } - template - int call_wrapper_entry(lua_State* L) { - return call_wrapper_function(std::is_member_function_pointer>(), L); - } -} // function_detail + template + int call_wrapper_entry(lua_State* L) { + return call_wrapper_function(std::is_member_function_pointer>(), L); + } -template -inline int c_call(lua_State* L) { + template + struct c_call_matcher { + template + int operator()(types, index_value, types, types, lua_State* L, int, int) const { + typedef meta::at_in_pack_t target; + return target::call(L); + } + }; + + } // function_detail + + template + inline int c_call(lua_State* L) { #ifdef __clang__ - return detail::trampoline(L, function_detail::call_wrapper_entry); + return detail::trampoline(L, function_detail::call_wrapper_entry); #else - return detail::static_trampoline<(&function_detail::call_wrapper_entry)>(L); -#endif // fuck you clang :< -} + return detail::static_trampoline<(&function_detail::call_wrapper_entry)>(L); +#endif // fuck you clang :c + } + + template + struct wrap { + typedef F type; + typedef wrapper wrapper; + + static int call(lua_State* L) { + return c_call(L); + } + }; + + template + inline int c_call(lua_State* L) { + if (sizeof...(Fxs) < 2) { + return meta::at_in_pack_t<0, Fxs...>::call(L); + } + else { + return call_detail::overload_match_arity(function_detail::c_call_matcher(), L, lua_gettop(L), 1); + } + } + } // sol #endif // SOL_FUNCTION_TYPES_TEMPLATED_HPP diff --git a/sol/traits.hpp b/sol/traits.hpp index c1a965b2..a887fe6b 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -159,6 +159,9 @@ using at_in_pack_t = typename at_in_pack::type; template struct at_in_pack : std::conditional> {}; +template +struct at_in_pack<0, Arg, Args...> { typedef Arg type; }; + namespace meta_detail { template class Pred, typename... Ts> struct count_for_pack : std::integral_constant {}; diff --git a/sol/wrapper.hpp b/sol/wrapper.hpp index 791503f6..3ad82934 100644 --- a/sol/wrapper.hpp +++ b/sol/wrapper.hpp @@ -33,15 +33,15 @@ namespace sol { typedef typename traits_type::args_list free_args_list; typedef typename traits_type::returns_list returns_list; - template - static decltype(auto) call(Fx&& fx, Args&&... args) { - return fx(std::forward(args)...); + template + static decltype(auto) call(F& f, Args&&... args) { + return f(std::forward(args)...); } struct caller { - template - decltype(auto) operator()(Fx&& fx, Args&&... args) const { - return call(std::forward(fx), std::forward(args)...); + template + decltype(auto) operator()(F& fx, Args&&... args) const { + return call(fx, std::forward(args)...); } }; }; @@ -58,15 +58,23 @@ namespace sol { return fx(std::forward(args)...); } - template - static decltype(auto) call(Fx&& fx, Args&&... args) { + template + static decltype(auto) call(F& fx, Args&&... args) { return fx(std::forward(args)...); } struct caller { - template - decltype(auto) operator()(Fx&& fx, Args&&... args) const { - return call(std::forward(fx), std::forward(args)...); + template + decltype(auto) operator()(F& fx, Args&&... args) const { + return call(fx, std::forward(args)...); + } + }; + + template + struct invoker { + template + decltype(auto) operator()(Args&&... args) const { + return invoke(std::forward(args)...); } }; }; @@ -85,20 +93,27 @@ namespace sol { return (mem.*fx)(std::forward(args)...); } - template - static decltype(auto) call(Fx&& fx, object_type& mem) { + static decltype(auto) call(F& fx, object_type& mem) { return (mem.*fx); } - template - static void call(Fx&& fx, object_type& mem, Arg&& arg, Args&&...) { + template + static void call(F& fx, object_type& mem, Arg&& arg, Args&&...) { (mem.*fx) = std::forward(arg); } struct caller { - template - decltype(auto) operator()(Fx&& fx, object_type& mem, Args&&... args) const { - return call(std::forward(fx), mem, std::forward(args)...); + template + decltype(auto) operator()(F& fx, object_type& mem, Args&&... args) const { + return call(fx, mem, std::forward(args)...); + } + }; + + template + struct invoker { + template + decltype(auto) operator()(Args&&... args) const { + return invoke(std::forward(args)...); } }; }; @@ -116,15 +131,23 @@ namespace sol { return (mem.*fx)(std::forward(args)...); } - template - static R call(Fx&& fx, O& mem, Args&&... args) { + template + static R call(F& fx, O& mem, Args&&... args) { return (mem.*fx)(std::forward(args)...); } struct caller { - template - decltype(auto) operator()(Fx&& fx, O& mem, Args&&... args) const { - return call(std::forward(fx), mem, std::forward(args)...); + template + decltype(auto) operator()(F& fx, O& mem, Args&&... args) const { + return call(fx, mem, std::forward(args)...); + } + }; + + template + struct invoker { + template + decltype(auto) operator()(O& mem, Args&&... args) const { + return invoke(mem, std::forward(args)...); } }; }; diff --git a/test_functions.cpp b/test_functions.cpp index 7f1e08aa..7e1760ec 100644 --- a/test_functions.cpp +++ b/test_functions.cpp @@ -78,6 +78,14 @@ void func_3(int, int, int) { } +int f1(int) { return 32; } +int f2(int, int) { return 1; } +struct fer { + double f3(int, int) { + return 2.5; + } +}; + TEST_CASE("functions/overload-resolution", "Check if overloaded function resolution templates compile/work") { sol::state lua; lua.open_libraries(sol::lib::base); @@ -837,3 +845,28 @@ TEST_CASE("functions/overloading", "Check if overloading works properly for regu REQUIRE_THROWS(lua.script("func(1,2,'meow')")); } +TEST_CASE("overloading/c_call", "Make sure that overloading works with c_call functionality") { + sol::state lua; + lua.set("f", sol::c_call, sol::wrap, sol::wrap>); + lua.set("g", sol::c_call>); + lua.set("h", sol::c_call); + lua.set("obj", fer()); + + lua.script("r1 = f(1)"); + lua.script("r2 = f(1, 2)"); + lua.script("r3 = f(obj, 1, 2)"); + lua.script("r4 = g(1)"); + lua.script("r5 = h(1, 2)"); + + int r1 = lua["r1"]; + int r2 = lua["r2"]; + double r3 = lua["r3"]; + int r4 = lua["r4"]; + int r5 = lua["r5"]; + + REQUIRE(r1 == 32); + REQUIRE(r2 == 1); + REQUIRE(r3 == 2.5); + REQUIRE(r4 == 32); + REQUIRE(r5 == 1); +} diff --git a/test_usertypes.cpp b/test_usertypes.cpp index 3f27bb71..372281ba 100644 --- a/test_usertypes.cpp +++ b/test_usertypes.cpp @@ -4,6 +4,7 @@ #include #include +#include struct vars { vars() { @@ -909,9 +910,6 @@ TEST_CASE("usertype/no_constructor", "make sure lua types cannot be constructed } TEST_CASE("usertype/coverage", "try all the things") { - -/* SOMETHING IS VERY WRONG WITH LUAJIT: NEED TO INVESTIGATE*/ -#if 0 sol::state lua; lua.open_libraries(sol::lib::base); @@ -998,5 +996,26 @@ print(e.bark) REQUIRE_THROWS(lua.script("e.readonlybark = 24")); REQUIRE_THROWS(lua.script("e.readonlypropbark = 500")); REQUIRE_THROWS(lua.script("y = e.writeonlypropbark")); -#endif // LUAJIT IS WEIRD AGAIN + +} + +TEST_CASE("usertype/copyability", "make sure user can write to a class variable even if the class itself isn't copy-safe") { + struct NoCopy + { + int get() const { return _you_can_copy_me; } + void set(int val) { _you_can_copy_me = val; } + + int _you_can_copy_me; + std::mutex _haha_you_cant_copy_me; + }; + + sol::state lua; + lua.new_usertype("NoCopy", "val", sol::property(&NoCopy::get, &NoCopy::set)); + + REQUIRE_NOTHROW( + lua.script(R"__( +nocopy = NoCopy.new() +nocopy.val = 5 + )__") + ); } \ No newline at end of file