Thanks to LUA_MULTRET, we can have normal function call syntax with sol::function that doesn't require the user to pass the arguments in directly with .call<Return1, Return2, Return3>( ... )

function result is meant to be transient, and therefore should not be regularly storeable by the user... but there is no way to make a "and you can't have anything but a temporary to this" type, as far as I can tell.
This commit is contained in:
ThePhD 2015-07-19 10:26:11 -04:00
parent 1b8bc7c1e9
commit 219d10b0b4
4 changed files with 59 additions and 16 deletions

1
.gitignore vendored
View File

@ -37,3 +37,4 @@ lua-5.3.0/
.vs/ .vs/
lua-5.1.5/ lua-5.1.5/
luajit-2.0.4/ luajit-2.0.4/
*.creator.user.*

View File

@ -33,14 +33,52 @@
#include <memory> #include <memory>
namespace sol { namespace sol {
class function_result {
private:
lua_State* L;
int index;
int returncount;
template <typename T, std::size_t I>
T get(types<T>, indices<I>) const {
return stack::get<T>(L, index);
}
template <typename... Ret, std::size_t... I>
std::tuple<Ret...> get(types<Ret...>, indices<I...>) const {
auto r = std::make_tuple(stack::get<Ret>(L, index + I)...);
return r;
}
public:
function_result() = default;
function_result(lua_State* L, int index = -1, int returncount = 0): L(L), index(index), returncount(returncount) {
}
function_result(const function_result&) = default;
function_result& operator=(const function_result&) = default;
function_result(function_result&&) = default;
function_result& operator=(function_result&&) = default;
template <typename T>
operator T () const {
auto tr = tuple_types<T>();
return get(tr, tr);
}
~function_result() {
lua_pop(L, returncount);
}
};
class function : public reference { class function : public reference {
private: private:
void luacall(std::size_t argcount, std::size_t resultcount) const { void luacall(std::ptrdiff_t argcount, std::ptrdiff_t resultcount) const {
lua_call(state(), static_cast<uint32_t>(argcount), static_cast<uint32_t>(resultcount)); lua_call(state(), static_cast<int>(argcount), static_cast<int>(resultcount));
} }
template<std::size_t... I, typename... Ret> template<std::size_t... I, typename... Ret>
std::tuple<Ret...> invoke(indices<I...>, types<Ret...>, std::size_t n) const { std::tuple<Ret...> invoke(indices<I...>, types<Ret...>, std::ptrdiff_t n) const {
luacall(n, sizeof...(Ret)); luacall(n, sizeof...(Ret));
const int nreturns = static_cast<int>(sizeof...(Ret)); const int nreturns = static_cast<int>(sizeof...(Ret));
const int stacksize = lua_gettop(state()); const int stacksize = lua_gettop(state());
@ -51,19 +89,23 @@ private:
} }
template<std::size_t I, typename Ret> template<std::size_t I, typename Ret>
Ret invoke(indices<I>, types<Ret>, std::size_t n) const { Ret invoke(indices<I>, types<Ret>, std::ptrdiff_t n) const {
luacall(n, 1); luacall(n, 1);
return stack::pop<Ret>(state()); return stack::pop<Ret>(state());
} }
template <std::size_t I> template <std::size_t I>
void invoke(indices<I>, types<void>, std::size_t n) const { void invoke(indices<I>, types<void>, std::ptrdiff_t n) const {
luacall(n, 0); luacall(n, 0);
} }
void invoke(indices<>, types<>, std::size_t n) const { function_result invoke(indices<>, types<>, std::ptrdiff_t n) const {
auto tr = types<void>(); const int stacksize = lua_gettop(state());
invoke(tr, tr, n); const int firstreturn = std::max(0, stacksize - static_cast<int>(n) - 1);
luacall(n, LUA_MULTRET);
const int poststacksize = lua_gettop(state());
const int returncount = poststacksize - firstreturn;
return function_result(state(), firstreturn + 1, returncount);
} }
public: public:
@ -75,8 +117,8 @@ public:
function& operator=(const function&) = default; function& operator=(const function&) = default;
template<typename... Args> template<typename... Args>
void operator()(Args&&... args) const { function_result operator()(Args&&... args) const {
call<>(std::forward<Args>(args)...); return call<>(std::forward<Args>(args)...);
} }
template<typename... Ret, typename... Args> template<typename... Ret, typename... Args>
@ -85,7 +127,7 @@ public:
} }
template<typename... Ret, typename... Args> template<typename... Ret, typename... Args>
ReturnType<Ret...> call(Args&&... args) const { auto call(Args&&... args) const -> decltype(invoke(types<Ret...>(), types<Ret...>(), 0)) {
push(); push();
int pushcount = stack::push_args(state(), std::forward<Args>(args)...); int pushcount = stack::push_args(state(), std::forward<Args>(args)...);
auto tr = types<Ret...>(); auto tr = types<Ret...>();

View File

@ -113,9 +113,9 @@ inline int push_args(lua_State* L, T&& t, Args&&... args) {
return pushcount; return pushcount;
} }
template<typename T, typename U = Unqualified<T>> template<typename T>
inline auto get(lua_State* L, int index = -1) -> decltype(getter<U>{}.get(L, index)) { inline auto get(lua_State* L, int index = -1) -> decltype(getter<Unqualified<T>>{}.get(L, index)) {
return getter<U>{}.get(L, index); return getter<Unqualified<T>>{}.get(L, index);
} }
template<typename T> template<typename T>
@ -619,7 +619,7 @@ inline void call(lua_State* L, types<void> tr, types<Args...> ta, Fx&& fx, FxArg
} }
inline call_syntax get_call_syntax(lua_State* L, const std::string& meta) { inline call_syntax get_call_syntax(lua_State* L, const std::string& meta) {
if (get<type>(L, 1) == type::table) { if (sol::stack::get<type>(L, 1) == type::table) {
if (luaL_newmetatable(L, meta.c_str()) == 0) { if (luaL_newmetatable(L, meta.c_str()) == 0) {
lua_settop(L, -2); lua_settop(L, -2);
return call_syntax::colon; return call_syntax::colon;

View File

@ -31,7 +31,7 @@ namespace sol {
class table : public reference { class table : public reference {
friend class state; friend class state;
template<typename T, typename U> template<typename T, typename U>
typename stack::get_return<T>::type single_get(U&& key) const { auto single_get(U&& key) const -> decltype(stack::get<T>(nullptr, 0)) {
push(); push();
stack::push(state(), std::forward<U>(key)); stack::push(state(), std::forward<U>(key));
lua_gettable(state(), -2); lua_gettable(state(), -2);