Merge pull request #27 from ThePhD/master

Adding std::function functionality for easier interop, interop with regular C++ classes at C++/lua boundary
This commit is contained in:
Danny 2014-05-30 21:02:33 -04:00
commit 56c6d41134
10 changed files with 199 additions and 64 deletions

View File

@ -33,9 +33,26 @@ namespace sol {
namespace detail { namespace detail {
#ifdef _MSC_VER #ifdef _MSC_VER
std::string demangle(const std::type_info& id) { std::string demangle(const std::type_info& id) {
return id.name();
}
#elif defined(__GNUC__) || defined(__clang__)
std::string demangle(const std::type_info& id) {
int status;
char* unmangled = abi::__cxa_demangle(id.name(), 0, 0, &status);
std::string realname = unmangled;
free(unmangled);
return realname;
}
#else
#error Compiler not supported for demangling
#endif // compilers
std::string lua_demangle(const std::type_info& id) {
std::string realname = demangle(id);
const static std::array<std::string, 2> removals = { "struct ", "class " }; const static std::array<std::string, 2> removals = { "struct ", "class " };
const static std::array<std::string, 2> replacements = { "::", "_" }; const static std::array<std::string, 2> replacements = { "::", "_" };
std::string realname = id.name();
for(std::size_t r = 0; r < removals.size(); ++r) { for(std::size_t r = 0; r < removals.size(); ++r) {
auto found = realname.find(removals[r]); auto found = realname.find(removals[r]);
while (found != std::string::npos) { while (found != std::string::npos) {
@ -52,20 +69,7 @@ std::string demangle(const std::type_info& id) {
} }
return realname; return realname;
} }
#elif defined(__GNUC__) || defined(__clang__)
std::string demangle(const std::type_info& id) {
int status;
char* unmangled = abi::__cxa_demangle(id.name(), 0, 0, &status);
std::string realname = unmangled;
free(unmangled);
return realname;
}
#else
#error Compiler not supported for demangling
#endif // compilers
} // detail } // detail
} // sol } // sol
#endif // SOL_DEMANGLE_HPP #endif // SOL_DEMANGLE_HPP

View File

@ -26,31 +26,32 @@
#include "tuple.hpp" #include "tuple.hpp"
#include "stack.hpp" #include "stack.hpp"
#include <cstdint> #include <cstdint>
#include <functional>
namespace sol { namespace sol {
class function : public reference { class function : public reference {
private: private:
void luacall (std::size_t argcount, std::size_t resultcount) { void luacall (std::size_t argcount, std::size_t resultcount) const {
lua_call(state(), static_cast<uint32_t>(argcount), static_cast<uint32_t>(resultcount)); lua_call(state(), static_cast<uint32_t>(argcount), static_cast<uint32_t>(resultcount));
} }
template<typename... Ret> template<typename... Ret>
std::tuple<Ret...> invoke(types<Ret...>, std::size_t n) { std::tuple<Ret...> invoke(types<Ret...>, std::size_t n) const {
luacall(n, sizeof...(Ret)); luacall(n, sizeof...(Ret));
return stack::pop_reverse_call(state(), std::make_tuple<Ret...>, types<Ret...>()); return stack::pop_reverse_call(state(), std::make_tuple<Ret...>, types<Ret...>());
} }
template<typename Ret> template<typename Ret>
Ret invoke(types<Ret>, std::size_t n) { Ret invoke(types<Ret>, std::size_t n) const {
luacall(n, 1); luacall(n, 1);
return stack::pop<Ret>(state()); return stack::pop<Ret>(state());
} }
void invoke(types<void>, std::size_t n) { void invoke(types<void>, std::size_t n) const {
luacall(n, 0); luacall(n, 0);
} }
void invoke(types<>, std::size_t n) { void invoke(types<>, std::size_t n) const {
luacall(n, 0); luacall(n, 0);
} }
@ -63,22 +64,58 @@ public:
function& operator=(const function&) = default; function& operator=(const function&) = default;
template<typename... Args> template<typename... Args>
void operator()(Args&&... args) { void operator()(Args&&... args) const {
call<>(std::forward<Args>(args)...); call<>(std::forward<Args>(args)...);
} }
template<typename... Ret, typename... Args> template<typename... Ret, typename... Args>
typename return_type<Ret...>::type operator()(types<Ret...>, Args&&... args) { typename return_type<Ret...>::type operator()(types<Ret...>, Args&&... args) const {
return call<Ret...>(std::forward<Args>(args)...); return call<Ret...>(std::forward<Args>(args)...);
} }
template<typename... Ret, typename... Args> template<typename... Ret, typename... Args>
typename return_type<Ret...>::type call(Args&&... args) { typename return_type<Ret...>::type call(Args&&... args) const {
push(); push();
stack::push_args(state(), std::forward<Args>(args)...); stack::push_args(state(), std::forward<Args>(args)...);
return invoke(types<Ret...>(), sizeof...(Args)); return invoke(types<Ret...>(), sizeof...(Args));
} }
}; };
namespace stack {
namespace detail {
template <typename Signature, typename... FxArgs, typename... Ret>
inline std::function<Signature> get_std_func(types<FxArgs...>, types<Ret...>, lua_State* L, int index = -1) {
typedef typename function_traits<Signature>::return_type return_t;
sol::function f(L, index);
auto fx = [ f, L, index ] (FxArgs&&... args) -> return_t {
return f(types<Ret...>(), std::forward<FxArgs>(args)...);
};
return std::move(fx);
}
template <typename Signature, typename... FxArgs>
inline std::function<Signature> get_std_func(types<FxArgs...>, types<void>, lua_State* L, int index = -1) {
sol::function f(L, index);
auto fx = [ f, L, index ] (FxArgs&&... args) -> void {
f(std::forward<FxArgs>(args)...);
};
return std::move(fx);
}
template <typename Signature, typename... FxArgs>
inline std::function<Signature> get_std_func(types<FxArgs...> t, types<>, lua_State* L, int index = -1) {
return get_std_func<Signature>(std::move(t), types<void>(), L, index);
}
template <typename Signature>
inline std::function<Signature> get(types<std::function<Signature>>, lua_State* L, int index = -1) {
typedef typename function_traits<Signature> fx_t;
typedef typename fx_t::args_type args_t;
typedef typename tuple_types<typename fx_t::return_type>::type ret_t;
return get_std_func<Signature>(args_t(), ret_t(), L, index);
}
} // detail
} // stack
} // sol } // sol
#endif // SOL_FUNCTION_HPP #endif // SOL_FUNCTION_HPP

View File

@ -34,7 +34,9 @@ struct static_function {
template<typename... Args> template<typename... Args>
static int typed_call(types<void>, types<Args...> t, function_type* fx, lua_State* L) { static int typed_call(types<void>, types<Args...> t, function_type* fx, lua_State* L) {
stack::pop_call(L, fx, t); stack::get_call(L, fx, t);
std::ptrdiff_t nargs = sizeof...(Args);
lua_pop(L, nargs);
return 0; return 0;
} }
@ -46,7 +48,9 @@ struct static_function {
template<typename... Ret, typename... Args> template<typename... Ret, typename... Args>
static int typed_call(types<Ret...>, types<Args...> t, function_type* fx, lua_State* L) { static int typed_call(types<Ret...>, types<Args...> t, function_type* fx, lua_State* L) {
typedef typename return_type<Ret...>::type return_type; typedef typename return_type<Ret...>::type return_type;
return_type r = stack::pop_call(L, fx, t); return_type r = stack::get_call(L, fx, t);
std::ptrdiff_t nargs = sizeof...(Args);
lua_pop(L, nargs);
stack::push(L, std::move(r)); stack::push(L, std::move(r));
return sizeof...(Ret); return sizeof...(Ret);
} }
@ -71,7 +75,9 @@ struct static_member_function {
template<typename... Args> template<typename... Args>
static int typed_call(types<void>, types<Args...>, T& item, function_type& ifx, lua_State* L) { static int typed_call(types<void>, types<Args...>, T& item, function_type& ifx, lua_State* L) {
auto fx = [&item, &ifx](Args&&... args) -> void { (item.*ifx)(std::forward<Args>(args)...); }; auto fx = [&item, &ifx](Args&&... args) -> void { (item.*ifx)(std::forward<Args>(args)...); };
stack::pop_call(L, fx, types<Args...>()); stack::get_call(L, fx, types<Args...>());
std::ptrdiff_t nargs = sizeof...(Args);
lua_pop(L, nargs);
return 0; return 0;
} }
@ -84,7 +90,9 @@ struct static_member_function {
static int typed_call(types<Ret...>, types<Args...>, T& item, function_type& ifx, lua_State* L) { static int typed_call(types<Ret...>, types<Args...>, T& item, function_type& ifx, lua_State* L) {
typedef typename return_type<Ret...>::type return_type; typedef typename return_type<Ret...>::type return_type;
auto fx = [&item, &ifx](Args&&... args) -> return_type { return (item.*ifx)(std::forward<Args>(args)...); }; auto fx = [&item, &ifx](Args&&... args) -> return_type { return (item.*ifx)(std::forward<Args>(args)...); };
return_type r = stack::pop_call(L, fx, types<Args...>()); return_type r = stack::get_call(L, fx, types<Args...>());
std::ptrdiff_t nargs = sizeof...(Args);
lua_pop(L, nargs);
stack::push(L, std::move(r)); stack::push(L, std::move(r));
return sizeof...(Ret); return sizeof...(Ret);
} }
@ -162,7 +170,9 @@ struct functor_function : public base_function {
template<typename... Args> template<typename... Args>
int operator()(types<void>, types<Args...> t, lua_State* L) { int operator()(types<void>, types<Args...> t, lua_State* L) {
stack::pop_call(L, fx, t); stack::get_call(L, fx, t);
std::ptrdiff_t nargs = sizeof...(Args);
lua_pop(L, nargs);
return 0; return 0;
} }
@ -174,7 +184,9 @@ struct functor_function : public base_function {
template<typename... Ret, typename... Args> template<typename... Ret, typename... Args>
int operator()(types<Ret...>, types<Args...> t, lua_State* L) { int operator()(types<Ret...>, types<Args...> t, lua_State* L) {
typedef typename return_type<Ret...>::type return_type; typedef typename return_type<Ret...>::type return_type;
return_type r = stack::pop_call(L, fx, t); return_type r = stack::get_call(L, fx, t);
std::ptrdiff_t nargs = sizeof...(Args);
lua_pop(L, nargs);
stack::push(L, r); stack::push(L, r);
return sizeof...(Ret); return sizeof...(Ret);
} }
@ -206,7 +218,7 @@ struct member_function : public base_function {
template<typename... Args> template<typename... Args>
int operator()(types<void>, types<Args...> t, lua_State* L) { int operator()(types<void>, types<Args...> t, lua_State* L) {
stack::pop_call(L, fx, t); stack::get_call(L, fx, t);
return 0; return 0;
} }
@ -218,7 +230,9 @@ struct member_function : public base_function {
template<typename... Ret, typename... Args> template<typename... Ret, typename... Args>
int operator()(types<Ret...>, types<Args...> t, lua_State* L) { int operator()(types<Ret...>, types<Args...> t, lua_State* L) {
typedef typename return_type<Ret...>::type return_type; typedef typename return_type<Ret...>::type return_type;
return_type r = stack::pop_call(L, fx, t); return_type r = stack::get_call(L, fx, t);
std::ptrdiff_t nargs = sizeof...(Args);
lua_pop(L, nargs);
stack::push(L, std::move(r)); stack::push(L, std::move(r));
return sizeof...(Ret); return sizeof...(Ret);
} }
@ -256,18 +270,6 @@ struct userdata_function : public base_function {
template<typename... FxArgs> template<typename... FxArgs>
userdata_function(FxArgs&&... fxargs): fx(std::forward<FxArgs>(fxargs)...) {} userdata_function(FxArgs&&... fxargs): fx(std::forward<FxArgs>(fxargs)...) {}
template<typename... Args>
int operator()(types<void>, types<Args...> t, lua_State* L) {
stack::pop_call(L, fx, t);
return 0;
}
template<typename... Args>
int operator()(types<>, types<Args...> t, lua_State* L) {
return (*this)(types<void>(), t, L);
}
template<typename Return, typename Raw = Unqualified<Return>> template<typename Return, typename Raw = Unqualified<Return>>
typename std::enable_if<std::is_same<T, Raw>::value, void>::type special_push(lua_State*, Return&&) { typename std::enable_if<std::is_same<T, Raw>::value, void>::type special_push(lua_State*, Return&&) {
// push nothing // push nothing
@ -278,15 +280,30 @@ struct userdata_function : public base_function {
stack::push(L, std::forward<Return>(r)); stack::push(L, std::forward<Return>(r));
} }
template<typename... Args>
int operator()(types<void>, types<Args...> t, lua_State* L) {
stack::get_call(L, 2, fx, t);
std::ptrdiff_t nargs = sizeof...(Args);
lua_pop(L, nargs);
return 0;
}
template<typename... Ret, typename... Args> template<typename... Ret, typename... Args>
int operator()(types<Ret...>, types<Args...> t, lua_State* L) { int operator()(types<Ret...>, types<Args...> t, lua_State* L) {
typedef typename return_type<Ret...>::type return_type; typedef typename return_type<Ret...>::type return_type;
return_type r = stack::pop_call(L, fx, t); return_type r = stack::get_call(L, 2, fx, t);
std::ptrdiff_t nargs = sizeof...(Args);
lua_pop(L, nargs);
// stack::push(L, std::move(r)); // stack::push(L, std::move(r));
special_push(L, r); special_push(L, r);
return sizeof...(Ret); return sizeof...(Ret);
} }
template<typename... Args>
int operator()(types<>, types<Args...> t, lua_State* L) {
return (*this)(types<void>(), t, L);
}
virtual int operator()(lua_State* L) override { virtual int operator()(lua_State* L) override {
fx.prepare(L); fx.prepare(L);
return (*this)(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), L); return (*this)(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), L);

View File

@ -61,16 +61,21 @@ inline type get(types<type>, lua_State* L, int index = -1) {
return static_cast<type>(lua_type(L, index)); return static_cast<type>(lua_type(L, index));
} }
template <typename T, typename U = typename std::remove_reference<T>::type>
inline U get_sol_type(std::true_type, types<T>, lua_State* L, int index = -1) {
return U(L, index);
}
template <typename T> template <typename T>
inline T& get(types<userdata<T>>, lua_State* L, int index = -1) { inline T& get_sol_type(std::false_type, types<T>, lua_State* L, int index = -1) {
userdata_t udata = get(types<userdata_t>{}, L, index); userdata_t udata = get(types<userdata_t>{}, L, index);
T* obj = static_cast<T*>(udata.value); T* obj = static_cast<T*>(udata.value);
return *obj; return *obj;
} }
template <typename T, typename U = typename std::remove_reference<T>::type> template <typename T, typename U = Unqualified<T>>
inline U get(types<T>, lua_State* L, int index = -1) { inline auto get(types<T> t, lua_State* L, int index = -1) -> decltype(get_sol_type(std::is_base_of<sol::reference, U>(), t, L, index)) {
return U(L, index); return get_sol_type(std::is_base_of<sol::reference, U>(), t, L, index);
} }
template<typename T> template<typename T>
@ -298,6 +303,11 @@ inline auto get_call(lua_State* L, int index, TFx&& fx, types<Args...> t) -> dec
return detail::ltr_get(L, index, std::forward<TFx>(fx), t, t); return detail::ltr_get(L, index, std::forward<TFx>(fx), t, t);
} }
template<typename... Args, typename TFx>
inline auto get_call(lua_State* L, TFx&& fx, types<Args...> t) -> decltype(detail::ltr_get(L, 1, std::forward<TFx>(fx), t, t)) {
return detail::ltr_get(L, 1, std::forward<TFx>(fx), t, t);
}
template<typename... Args, typename TFx> template<typename... Args, typename TFx>
inline auto pop_call(lua_State* L, TFx&& fx, types<Args...> t) -> decltype(detail::ltr_pop(L, std::forward<TFx>(fx), t, t)) { inline auto pop_call(lua_State* L, TFx&& fx, types<Args...> t) -> decltype(detail::ltr_pop(L, std::forward<TFx>(fx), t, t)) {
return detail::ltr_pop(L, std::forward<TFx>(fx), t, t); return detail::ltr_pop(L, std::forward<TFx>(fx), t, t);

View File

@ -28,12 +28,6 @@
namespace sol { namespace sol {
namespace detail { namespace detail {
template<class T, class...>
struct are_same : std::true_type {};
template<class T, class U, class... Args>
struct are_same<T, U, Args...> : std::integral_constant<bool, std::is_same<T, U>::value && are_same<T, Args...>::value> {};
inline int atpanic(lua_State* L) { inline int atpanic(lua_State* L) {
std::string err = lua_tostring(L, -1); std::string err = lua_tostring(L, -1);
throw error(err); throw error(err);
@ -70,7 +64,7 @@ public:
template<typename... Args> template<typename... Args>
void open_libraries(Args&&... args) { void open_libraries(Args&&... args) {
static_assert(detail::are_same<lib, Args...>::value, "all types must be libraries"); static_assert(are_same<lib, Args...>::value, "all types must be libraries");
if(sizeof...(args) == 0) { if(sizeof...(args) == 0) {
luaL_openlibs(L.get()); luaL_openlibs(L.get());
return; return;
@ -208,4 +202,4 @@ public:
}; };
} // sol } // sol
#endif // SOL_STATE_HPP #endif // SOL_STATE_HPP

View File

@ -26,6 +26,12 @@
#include <type_traits> #include <type_traits>
namespace sol { namespace sol {
template<class T, class...>
struct are_same : std::true_type { };
template<class T, class U, class... Args>
struct are_same<T, U, Args...> : std::integral_constant <bool, std::is_same<T, U>::value && are_same<T, Args...>::value> { };
template<typename T, typename R = void> template<typename T, typename R = void>
using EnableIf = typename std::enable_if<T::value, R>::type; using EnableIf = typename std::enable_if<T::value, R>::type;
@ -53,6 +59,12 @@ struct return_type<> : types<>{
typedef void type; typedef void type;
}; };
template<typename... Args>
struct is_tuple : std::false_type{ };
template<typename... Args>
struct is_tuple<std::tuple<Args...>> : std::true_type{ };
template <typename T, template <typename...> class Templ> template <typename T, template <typename...> class Templ>
struct is_specialization_of : std::false_type { }; struct is_specialization_of : std::false_type { };
template <typename... T, template <typename...> class Templ> template <typename... T, template <typename...> class Templ>
@ -147,4 +159,4 @@ struct function_traits<R(*)(Args...)> {
}; };
} // sol } // sol
#endif // SOL_TRAITS_HPP #endif // SOL_TRAITS_HPP

View File

@ -69,10 +69,10 @@ template<typename... Args>
struct reversed : reversed_<types<>, Args...>{}; struct reversed : reversed_<types<>, Args...>{};
template<typename... Args> template<typename... Args>
struct tuple_types : types<Args...>, std::false_type {}; struct tuple_types : types<Args...> {};
template<typename... Args> template<typename... Args>
struct tuple_types<std::tuple<Args...>> : types<Args...>, std::true_type {}; struct tuple_types<std::tuple<Args...>> : types<Args...> {};
template<typename... Tn> template<typename... Tn>
struct constructors {}; struct constructors {};
@ -81,4 +81,4 @@ const auto default_constructor = constructors<types<>>{};
} // sol } // sol
#endif // SOL_TUPLE_HPP #endif // SOL_TUPLE_HPP

View File

@ -160,4 +160,4 @@ 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; }
} // sol } // sol
#endif // SOL_TYPES_HPP #endif // SOL_TYPES_HPP

View File

@ -42,7 +42,7 @@ struct userdata_traits {
}; };
template<typename T> template<typename T>
const std::string userdata_traits<T>::name = detail::demangle(typeid(T)); const std::string userdata_traits<T>::name = detail::lua_demangle(typeid(T));
template<typename T> template<typename T>
const std::string userdata_traits<T>::metatable = std::string("sol.stateful.").append(name); const std::string userdata_traits<T>::metatable = std::string("sol.stateful.").append(name);

View File

@ -2,11 +2,39 @@
#include <catch.hpp> #include <catch.hpp>
#include <sol.hpp> #include <sol.hpp>
void test_free_func(std::function<void()> f) {
f();
}
void test_free_func2(std::function<int(int)> f, int arg1) {
int val = f(arg1);
assert(arg1 == val);
}
std::string free_function() { std::string free_function() {
std::cout << "free_function()" << std::endl; std::cout << "free_function()" << std::endl;
return "test"; return "test";
} }
struct self_test {
int bark;
self_test() : bark(100) {
}
void g(const std::string& str) {
std::cout << str << '\n';
bark += 1;
}
void f(const self_test& t) {
std::cout << "got test" << '\n';
assert(t.bark == bark);
assert(&t == this);
}
};
struct object { struct object {
std::string operator() () { std::string operator() () {
std::cout << "member_test()" << std::endl; std::cout << "member_test()" << std::endl;
@ -275,7 +303,7 @@ TEST_CASE("tables/functions_variables", "Check if tables and function calls work
std::cout << "stateless lambda()" << std::endl; std::cout << "stateless lambda()" << std::endl;
return "test"; return "test";
} }
); );
REQUIRE_NOTHROW(run_script(lua)); REQUIRE_NOTHROW(run_script(lua));
lua.get<sol::table>("os").set_function("fun", &free_function); lua.get<sol::table>("os").set_function("fun", &free_function);
@ -293,7 +321,7 @@ TEST_CASE("tables/functions_variables", "Check if tables and function calls work
std::cout << "stateless lambda()" << std::endl; std::cout << "stateless lambda()" << std::endl;
return "test"; return "test";
} }
); );
REQUIRE_NOTHROW(run_script(lua)); REQUIRE_NOTHROW(run_script(lua));
// r-value, cannot optimise // r-value, cannot optimise
@ -324,6 +352,25 @@ TEST_CASE("functions/return_order_and_multi_get", "Check if return order is in t
REQUIRE(tluaget == triple); REQUIRE(tluaget == triple);
} }
TEST_CASE("functions/sol::function to std::function", "check if conversion to std::function works properly and calls with correct arguments") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("testFunc", test_free_func);
lua.set_function("testFunc2", test_free_func2);
lua.script(
"testFunc(function() print(\"hello std::function\") end)"
);
lua.script(
"function m(a)\n"
" print(\"hello std::function with arg \", a)\n"
" return a\n"
"end\n"
"\n"
"testFunc2(m, 1)"
);
}
TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works properly") { TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works properly") {
sol::state lua; sol::state lua;
lua.open_libraries(sol::lib::base); lua.open_libraries(sol::lib::base);
@ -493,3 +540,17 @@ TEST_CASE("tables/userdata utility derived", "userdata classes must play nice wh
REQUIRE((lua.get<int>("dgn10") == 70)); REQUIRE((lua.get<int>("dgn10") == 70));
REQUIRE((lua.get<int>("dgn") == 7)); REQUIRE((lua.get<int>("dgn") == 7));
} }
TEST_CASE("tables/self-referential userdata", "userdata classes must play nice when C++ object types are requested for C++ code") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_userdata<self_test>("test", "g", &self_test::g, "f", &self_test::f);
lua.script(
"local a = test.new()\n"
"a:g(\"woof\")\n"
"a:f(a)\n"
);
}