mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
Formally introduce c_call
to the API with overloading ability
More documentation on how things work. Once more at a 'feature-complete' state. Continue to work on #116. Add testcase to ensure #108 is fixed. Then, prep release.
This commit is contained in:
parent
147aff1915
commit
29e4b82b4a
|
@ -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
|
||||
|
|
71
docs/source/api/c_call.rst
Normal file
71
docs/source/api/c_call.rst
Normal file
|
@ -0,0 +1,71 @@
|
|||
c_call
|
||||
======
|
||||
Templated type to transport functions through templates
|
||||
-------------------------------------------------------
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
template <typename Function, Function f>
|
||||
int c_call (lua_State* L);
|
||||
|
||||
template <typename... Functions>
|
||||
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<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<decltype(&f1), &f1>, sol::wrap<decltype(&f2), &f2>, sol::wrap<decltype(&fer::f3), &fer::f3>>);
|
||||
// singly-wrapped function
|
||||
lua.set("g", sol::c_call<sol::wrap<decltype(&f1), &f1>>);
|
||||
// without the 'sol::wrap' boilerplate
|
||||
lua.set("h", sol::c_call<decltype(&f2), &f2>);
|
||||
// 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
|
|
@ -1,7 +1,7 @@
|
|||
error
|
||||
=====
|
||||
the single exception type
|
||||
-------------------------
|
||||
the single error/exception type
|
||||
-------------------------------
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#ifndef SOL_FUNCTION_TYPES_TEMPLATED_HPP
|
||||
#define SOL_FUNCTION_TYPES_TEMPLATED_HPP
|
||||
|
||||
#include "stack.hpp"
|
||||
#include "call.hpp"
|
||||
|
||||
namespace sol {
|
||||
namespace function_detail {
|
||||
|
@ -101,6 +101,16 @@ namespace function_detail {
|
|||
int call_wrapper_entry(lua_State* L) {
|
||||
return call_wrapper_function<F, fx>(std::is_member_function_pointer<meta::unqualified_t<F>>(), L);
|
||||
}
|
||||
|
||||
template <typename... Fxs>
|
||||
struct c_call_matcher {
|
||||
template <typename Fx, std::size_t I, typename R, typename... Args>
|
||||
int operator()(types<Fx>, index_value<I>, types<R>, types<Args...>, lua_State* L, int, int) const {
|
||||
typedef meta::at_in_pack_t<I, Fxs...> target;
|
||||
return target::call(L);
|
||||
}
|
||||
};
|
||||
|
||||
} // function_detail
|
||||
|
||||
template <typename F, F fx>
|
||||
|
@ -109,8 +119,29 @@ inline int c_call(lua_State* L) {
|
|||
return detail::trampoline(L, function_detail::call_wrapper_entry<F, fx>);
|
||||
#else
|
||||
return detail::static_trampoline<(&function_detail::call_wrapper_entry<F, fx>)>(L);
|
||||
#endif // fuck you clang :<
|
||||
#endif // fuck you clang :c
|
||||
}
|
||||
|
||||
template <typename F, F f>
|
||||
struct wrap {
|
||||
typedef F type;
|
||||
typedef wrapper<F> wrapper;
|
||||
|
||||
static int call(lua_State* L) {
|
||||
return c_call<type, f>(L);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Fxs>
|
||||
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<typename Fxs::type...>(function_detail::c_call_matcher<Fxs...>(), L, lua_gettop(L), 1);
|
||||
}
|
||||
}
|
||||
|
||||
} // sol
|
||||
|
||||
#endif // SOL_FUNCTION_TYPES_TEMPLATED_HPP
|
||||
|
|
|
@ -159,6 +159,9 @@ using at_in_pack_t = typename at_in_pack<I, Args...>::type;
|
|||
template<std::size_t I, typename Arg, typename... Args>
|
||||
struct at_in_pack<I, Arg, Args...> : std::conditional<I == 0, Arg, at_in_pack_t<I - 1, Args...>> {};
|
||||
|
||||
template<typename Arg, typename... Args>
|
||||
struct at_in_pack<0, Arg, Args...> { typedef Arg type; };
|
||||
|
||||
namespace meta_detail {
|
||||
template<std::size_t Limit, std::size_t I, template<typename...> class Pred, typename... Ts>
|
||||
struct count_for_pack : std::integral_constant<std::size_t, 0> {};
|
||||
|
|
|
@ -33,15 +33,15 @@ namespace sol {
|
|||
typedef typename traits_type::args_list free_args_list;
|
||||
typedef typename traits_type::returns_list returns_list;
|
||||
|
||||
template <typename Fx, typename... Args>
|
||||
static decltype(auto) call(Fx&& fx, Args&&... args) {
|
||||
return fx(std::forward<Args>(args)...);
|
||||
template <typename... Args>
|
||||
static decltype(auto) call(F& f, Args&&... args) {
|
||||
return f(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
struct caller {
|
||||
template <typename Fx, typename... Args>
|
||||
decltype(auto) operator()(Fx&& fx, Args&&... args) const {
|
||||
return call(std::forward<Fx>(fx), std::forward<Args>(args)...);
|
||||
template <typename... Args>
|
||||
decltype(auto) operator()(F& fx, Args&&... args) const {
|
||||
return call(fx, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -58,15 +58,23 @@ namespace sol {
|
|||
return fx(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Fx, typename... Args>
|
||||
static decltype(auto) call(Fx&& fx, Args&&... args) {
|
||||
template <typename... Args>
|
||||
static decltype(auto) call(F& fx, Args&&... args) {
|
||||
return fx(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
struct caller {
|
||||
template <typename Fx, typename... Args>
|
||||
decltype(auto) operator()(Fx&& fx, Args&&... args) const {
|
||||
return call(std::forward<Fx>(fx), std::forward<Args>(args)...);
|
||||
template <typename... Args>
|
||||
decltype(auto) operator()(F& fx, Args&&... args) const {
|
||||
return call(fx, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <F fx>
|
||||
struct invoker {
|
||||
template <typename... Args>
|
||||
decltype(auto) operator()(Args&&... args) const {
|
||||
return invoke<fx>(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -85,20 +93,27 @@ namespace sol {
|
|||
return (mem.*fx)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Fx>
|
||||
static decltype(auto) call(Fx&& fx, object_type& mem) {
|
||||
static decltype(auto) call(F& fx, object_type& mem) {
|
||||
return (mem.*fx);
|
||||
}
|
||||
|
||||
template <typename Fx, typename Arg, typename... Args>
|
||||
static void call(Fx&& fx, object_type& mem, Arg&& arg, Args&&...) {
|
||||
template <typename Arg, typename... Args>
|
||||
static void call(F& fx, object_type& mem, Arg&& arg, Args&&...) {
|
||||
(mem.*fx) = std::forward<Arg>(arg);
|
||||
}
|
||||
|
||||
struct caller {
|
||||
template <typename Fx, typename... Args>
|
||||
decltype(auto) operator()(Fx&& fx, object_type& mem, Args&&... args) const {
|
||||
return call(std::forward<Fx>(fx), mem, std::forward<Args>(args)...);
|
||||
template <typename... Args>
|
||||
decltype(auto) operator()(F& fx, object_type& mem, Args&&... args) const {
|
||||
return call(fx, mem, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <F fx>
|
||||
struct invoker {
|
||||
template <typename... Args>
|
||||
decltype(auto) operator()(Args&&... args) const {
|
||||
return invoke<fx>(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -116,15 +131,23 @@ namespace sol {
|
|||
return (mem.*fx)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Fx, typename... Args>
|
||||
static R call(Fx&& fx, O& mem, Args&&... args) {
|
||||
template <typename... Args>
|
||||
static R call(F& fx, O& mem, Args&&... args) {
|
||||
return (mem.*fx)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
struct caller {
|
||||
template <typename Fx, typename... Args>
|
||||
decltype(auto) operator()(Fx&& fx, O& mem, Args&&... args) const {
|
||||
return call(std::forward<Fx>(fx), mem, std::forward<Args>(args)...);
|
||||
template <typename... Args>
|
||||
decltype(auto) operator()(F& fx, O& mem, Args&&... args) const {
|
||||
return call(fx, mem, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <F fx>
|
||||
struct invoker {
|
||||
template <typename... Args>
|
||||
decltype(auto) operator()(O& mem, Args&&... args) const {
|
||||
return invoke<fx>(mem, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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<decltype(&f1), &f1>, sol::wrap<decltype(&f2), &f2>, sol::wrap<decltype(&fer::f3), &fer::f3>>);
|
||||
lua.set("g", sol::c_call<sol::wrap<decltype(&f1), &f1>>);
|
||||
lua.set("h", sol::c_call<decltype(&f2), &f2>);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <catch.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
|
||||
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>("NoCopy", "val", sol::property(&NoCopy::get, &NoCopy::set));
|
||||
|
||||
REQUIRE_NOTHROW(
|
||||
lua.script(R"__(
|
||||
nocopy = NoCopy.new()
|
||||
nocopy.val = 5
|
||||
)__")
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user