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:
ThePhD 2016-06-18 03:32:54 -04:00
parent 147aff1915
commit 29e4b82b4a
8 changed files with 286 additions and 105 deletions

View File

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

View 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

View File

@ -1,7 +1,7 @@
error
=====
the single exception type
-------------------------
the single error/exception type
-------------------------------
.. code-block:: cpp

View File

@ -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 <typename F, F fx>
inline int call_wrapper_variable(std::false_type, lua_State* L) {
typedef meta::bind_traits<meta::unqualified_t<F>> traits_type;
typedef typename traits_type::args_list args_list;
typedef meta::tuple_types<typename traits_type::return_type> return_type;
return stack::call_into_lua(return_type(), args_list(), L, 1, fx);
}
namespace function_detail {
template <typename F, F fx>
inline int call_wrapper_variable(std::false_type, lua_State* L) {
typedef meta::bind_traits<meta::unqualified_t<F>> traits_type;
typedef typename traits_type::args_list args_list;
typedef meta::tuple_types<typename traits_type::return_type> return_type;
return stack::call_into_lua(return_type(), args_list(), L, 1, fx);
}
template <typename R, typename V, V, typename T>
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 <typename R, typename V, V, typename T>
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 <typename R, typename V, V variable, typename T>
inline int call_set_assignable(std::true_type, lua_State* L, T&& mem) {
(mem.*variable) = stack::get<R>(L, 2);
lua_pop(L, 2);
return 0;
}
template <typename R, typename V, V variable, typename T>
inline int call_set_assignable(std::true_type, lua_State* L, T&& mem) {
(mem.*variable) = stack::get<R>(L, 2);
lua_pop(L, 2);
return 0;
}
template <typename R, typename V, V, typename T>
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 <typename R, typename V, V, typename T>
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 <typename R, typename V, V variable, typename T>
inline int call_set_variable(std::true_type, lua_State* L, T&& mem) {
return call_set_assignable<R, V, variable>(std::is_assignable<std::add_lvalue_reference_t<R>, R>(), L, std::forward<T>(mem));
}
template <typename R, typename V, V variable, typename T>
inline int call_set_variable(std::true_type, lua_State* L, T&& mem) {
return call_set_assignable<R, V, variable>(std::is_assignable<std::add_lvalue_reference_t<R>, R>(), L, std::forward<T>(mem));
}
template <typename V, V variable>
inline int call_wrapper_variable(std::true_type, lua_State* L) {
typedef meta::bind_traits<meta::unqualified_t<V>> traits_type;
typedef typename traits_type::object_type T;
typedef typename traits_type::return_type R;
auto& mem = stack::get<T>(L, 1);
switch (lua_gettop(L)) {
case 1: {
decltype(auto) r = (mem.*variable);
lua_pop(L, 1);
stack::push_reference(L, std::forward<decltype(r)>(r));
return 1; }
case 2:
return call_set_variable<R, V, variable>(meta::neg<std::is_const<R>>(), L, mem);
default:
return luaL_error(L, "incorrect number of arguments to member variable function call");
}
}
template <typename V, V variable>
inline int call_wrapper_variable(std::true_type, lua_State* L) {
typedef meta::bind_traits<meta::unqualified_t<V>> traits_type;
typedef typename traits_type::object_type T;
typedef typename traits_type::return_type R;
auto& mem = stack::get<T>(L, 1);
switch (lua_gettop(L)) {
case 1: {
decltype(auto) r = (mem.*variable);
lua_pop(L, 1);
stack::push_reference(L, std::forward<decltype(r)>(r));
return 1; }
case 2:
return call_set_variable<R, V, variable>(meta::neg<std::is_const<R>>(), L, mem);
default:
return luaL_error(L, "incorrect number of arguments to member variable function call");
}
}
template <typename F, F fx>
inline int call_wrapper_function(std::false_type, lua_State* L) {
return call_wrapper_variable<F, fx>(std::is_member_object_pointer<F>(), L);
}
template <typename F, F fx>
inline int call_wrapper_function(std::false_type, lua_State* L) {
return call_wrapper_variable<F, fx>(std::is_member_object_pointer<F>(), L);
}
template <typename F, F fx>
inline int call_wrapper_function(std::true_type, lua_State* L) {
typedef meta::bind_traits<meta::unqualified_t<F>> 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> return_type_list;
auto mfx = [&](auto&&... args) -> typename traits_type::return_type {
auto& member = stack::get<T>(L, 1);
return (member.*fx)(std::forward<decltype(args)>(args)...);
};
int n = stack::call_into_lua<1>(return_type_list(), args_list(), L, 2, mfx);
return n;
}
template <typename F, F fx>
inline int call_wrapper_function(std::true_type, lua_State* L) {
typedef meta::bind_traits<meta::unqualified_t<F>> 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> return_type_list;
auto mfx = [&](auto&&... args) -> typename traits_type::return_type {
auto& member = stack::get<T>(L, 1);
return (member.*fx)(std::forward<decltype(args)>(args)...);
};
int n = stack::call_into_lua<1>(return_type_list(), args_list(), L, 2, mfx);
return n;
}
template <typename F, F fx>
int call_wrapper_entry(lua_State* L) {
return call_wrapper_function<F, fx>(std::is_member_function_pointer<meta::unqualified_t<F>>(), L);
}
} // function_detail
template <typename F, F fx>
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 F, F fx>
inline int c_call(lua_State* 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>
inline int c_call(lua_State* L) {
#ifdef __clang__
return detail::trampoline(L, function_detail::call_wrapper_entry<F, fx>);
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 :<
}
return detail::static_trampoline<(&function_detail::call_wrapper_entry<F, fx>)>(L);
#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

View File

@ -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> {};

View File

@ -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)...);
}
};
};

View File

@ -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);
}

View File

@ -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
)__")
);
}