// https://github.com/kunitoki/LuaBridge3 // Copyright 2021, kunitoki // SPDX-License-Identifier: MIT #pragma once #include "Config.h" #include "Errors.h" #include "Stack.h" #include "LuaRef.h" #include "LuaException.h" #include #include #include #include namespace luabridge { //================================================================================================= namespace detail { template bool is_handler_valid(const F& f) noexcept { if constexpr (std::is_pointer_v>) return f != nullptr; else if constexpr (std::is_constructible_v>) return static_cast(f); else return true; } template TypeResult decode_tuple_result(lua_State* L, int first_result_index, std::index_sequence) { auto results = std::make_tuple( Stack>::get(L, first_result_index + static_cast(Indices))...); std::error_code ec; const bool ok = (([&]() { const auto& element = std::get(results); if (! element) { ec = element.error(); return false; } return true; }()) && ...); if (! ok) return ec; return Tuple{ std::move(*std::get(results))... }; } template TypeResult decode_call_result(lua_State* L, int first_result_index, int num_returned_values) { if constexpr (std::is_same_v || std::is_same_v>) { if (num_returned_values != 0) return makeErrorCode(ErrorCode::InvalidTableSizeInCast); return {}; } else if constexpr (is_tuple_v) { constexpr auto expected_size = static_cast(std::tuple_size_v); if (num_returned_values != expected_size) return makeErrorCode(ErrorCode::InvalidTableSizeInCast); return decode_tuple_result(L, first_result_index, std::make_index_sequence{}); } else { if (num_returned_values < 1) return makeErrorCode(ErrorCode::InvalidTypeCast); return Stack::get(L, first_result_index); } } } // namespace detail //================================================================================================= /** * @brief Safely call Lua code and decode the return values to R. */ template TypeResult callWithHandler(const Ref& object, F&& errorHandler, Args&&... args) { static_assert(std::is_same_v, detail::remove_cvref_t> || std::is_invocable_r_v); static constexpr bool isValidHandler = !std::is_same_v, detail::remove_cvref_t>; lua_State* L = object.state(); const StackRestore stackRestore(L); const int initialTop = lua_gettop(L); bool hasHandler = false; if constexpr (isValidHandler) { hasHandler = detail::is_handler_valid(errorHandler); if (hasHandler) detail::push_function(L, std::forward(errorHandler), ""); } object.push(); { const auto [result, index] = detail::push_arguments(L, std::forward_as_tuple(args...)); if (! result) return result.error(); } const int messageHandlerIndex = hasHandler ? (initialTop + 1) : 0; const int code = lua_pcall(L, sizeof...(Args), LUA_MULTRET, messageHandlerIndex); if (code != LUABRIDGE_LUA_OK) { auto ec = makeErrorCode(ErrorCode::LuaFunctionCallFailed); #if LUABRIDGE_HAS_EXCEPTIONS if constexpr (! isValidHandler) { if (LuaException::areExceptionsEnabled(L)) LuaException::raise(L, ec); } #endif lua_pop(L, 1); return ec; } if (hasHandler) lua_remove(L, initialTop + 1); const int firstResultIndex = initialTop + 1; const int numReturnedValues = lua_gettop(L) - initialTop; return detail::decode_call_result(L, firstResultIndex, numReturnedValues); } template TypeResult callWithHandler(const Ref& object, F&& errorHandler, Args&&... args) { return callWithHandler(object, std::forward(errorHandler), std::forward(args)...); } template TypeResult call(const Ref& object, Args&&... args) { return callWithHandler(object, std::ignore, std::forward(args)...); } template class LuaFunction; template class LuaFunction { public: LuaFunction() = default; explicit LuaFunction(const LuaRef& function) : m_function(function) { } explicit LuaFunction(LuaRef&& function) : m_function(std::move(function)) { } [[nodiscard]] TypeResult operator()(Args... args) const { return call(std::forward(args)...); } [[nodiscard]] TypeResult call(Args... args) const { return luabridge::call(m_function, std::forward(args)...); } template [[nodiscard]] TypeResult callWithHandler(F&& errorHandler, Args... args) const { return luabridge::callWithHandler(m_function, std::forward(errorHandler), std::forward(args)...); } [[nodiscard]] bool isValid() const { return m_function.isCallable(); } [[nodiscard]] const LuaRef& ref() const { return m_function; } private: LuaRef m_function; }; //============================================================================================= /** * @brief Wrapper for `lua_pcall` that throws if exceptions are enabled. */ inline int pcall(lua_State* L, int nargs = 0, int nresults = 0, int msgh = 0) { const int code = lua_pcall(L, nargs, nresults, msgh); #if LUABRIDGE_HAS_EXCEPTIONS if (code != LUABRIDGE_LUA_OK && LuaException::areExceptionsEnabled(L)) LuaException::raise(L, makeErrorCode(ErrorCode::LuaFunctionCallFailed)); #endif return code; } //============================================================================================= template template TypeResult LuaRefBase::call(Args&&... args) const { return luabridge::call(impl(), std::forward(args)...); } template template TypeResult LuaRefBase::operator()(Args&&... args) const { return call(std::forward(args)...); } template template TypeResult LuaRefBase::callWithHandler(F&& errorHandler, Args&&... args) const { return luabridge::callWithHandler(impl(), std::forward(errorHandler), std::forward(args)...); } template template TypeResult LuaRefBase::callWithHandler(F&& errorHandler, Args&&... args) const { return callWithHandler(std::forward(errorHandler), std::forward(args)...); } template template LuaFunction LuaRefBase::callable() const { const StackRestore stackRestore(m_L); impl().push(m_L); return LuaFunction(LuaRef::fromStack(m_L)); } } // namespace luabridge