1
0
Fork 0
mirror of https://gitlab.com/niansa/SomeBot.git synced 2025-03-06 20:48:26 +01:00
SomeBot/LuaBridge3/Source/LuaBridge/detail/Invoke.h
2023-03-02 14:10:47 +01:00

228 lines
6 KiB
C++

// https://github.com/kunitoki/LuaBridge3
// Copyright 2021, Lucio Asnaghi
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "Errors.h"
#include "Stack.h"
#include "LuaRef.h"
#include "LuaException.h"
#include <vector>
#include <functional>
#include <optional>
#include <variant>
#include <tuple>
namespace luabridge {
//=================================================================================================
/**
* @brief Result of a lua invocation.
*/
class LuaResult
{
public:
/**
* @brief Get if the result was ok and didn't raise a lua error.
*/
explicit operator bool() const noexcept
{
return !m_ec;
}
/**
* @brief Return if the invocation was ok and didn't raise a lua error.
*/
bool wasOk() const noexcept
{
return !m_ec;
}
/**
* @brief Return if the invocation did raise a lua error.
*/
bool hasFailed() const noexcept
{
return !!m_ec;
}
/**
* @brief Return the error code, if any.
*
* In case the invcation didn't raise any lua error, the value returned equals to a
* default constructed std::error_code.
*/
std::error_code errorCode() const noexcept
{
return m_ec;
}
/**
* @brief Return the error message, if any.
*/
std::string errorMessage() const noexcept
{
if (std::holds_alternative<std::string>(m_data))
{
const auto& message = std::get<std::string>(m_data);
return message.empty() ? m_ec.message() : message;
}
return {};
}
/**
* @brief Return the number of return values.
*/
std::size_t size() const noexcept
{
if (std::holds_alternative<std::vector<LuaRef>>(m_data))
return std::get<std::vector<LuaRef>>(m_data).size();
return 0;
}
/**
* @brief Get a return value at a specific index.
*/
LuaRef operator[](std::size_t index) const
{
assert(m_ec == std::error_code());
if (std::holds_alternative<std::vector<LuaRef>>(m_data))
{
const auto& values = std::get<std::vector<LuaRef>>(m_data);
assert(index < values.size());
return values[index];
}
return LuaRef(m_L);
}
private:
template <class... Args>
friend LuaResult call(const LuaRef&, Args&&...);
static LuaResult errorFromStack(lua_State* L, std::error_code ec)
{
auto errorString = lua_tostring(L, -1);
lua_pop(L, 1);
return LuaResult(L, ec, errorString ? errorString : ec.message());
}
static LuaResult valuesFromStack(lua_State* L, int stackTop)
{
std::vector<LuaRef> values;
const int numReturnedValues = lua_gettop(L) - stackTop;
if (numReturnedValues > 0)
{
values.reserve(numReturnedValues);
for (int index = numReturnedValues; index > 0; --index)
values.emplace_back(LuaRef::fromStack(L, -index));
lua_pop(L, numReturnedValues);
}
return LuaResult(L, std::move(values));
}
LuaResult(lua_State* L, std::error_code ec, std::string_view errorString)
: m_L(L)
, m_ec(ec)
, m_data(std::string(errorString))
{
}
explicit LuaResult(lua_State* L, std::vector<LuaRef> values) noexcept
: m_L(L)
, m_data(std::move(values))
{
}
lua_State* m_L = nullptr;
std::error_code m_ec;
std::variant<std::vector<LuaRef>, std::string> m_data;
};
//=================================================================================================
/**
* @brief Safely call Lua code.
*
* These overloads allow Lua code to be called throught lua_pcall. The return value is provided as
* a LuaResult which will hold the return values or an error if the call failed.
*
* If an error occurs, a LuaException is thrown or if exceptions are disabled the FunctionResult will
* contain a error code and evaluate false.
*
* @note The function might throw a LuaException if the application is compiled with exceptions on
* and they are enabled globally by calling `enableExceptions` in two cases:
* - one of the arguments passed cannot be pushed in the stack, for example a unregistered C++ class
* - the lua invaction calls the panic handler, which is converted to a C++ exception
*
* @return A result object.
*/
template <class... Args>
LuaResult call(const LuaRef& object, Args&&... args)
{
lua_State* L = object.state();
const int stackTop = lua_gettop(L);
object.push();
{
const auto [result, index] = detail::push_arguments(L, std::forward_as_tuple(args...));
if (! result)
{
lua_pop(L, static_cast<int>(index) + 1);
return LuaResult(L, result, result.message());
}
}
int code = lua_pcall(L, sizeof...(Args), LUA_MULTRET, 0);
if (code != LUABRIDGE_LUA_OK)
{
auto ec = makeErrorCode(ErrorCode::LuaFunctionCallFailed);
#if LUABRIDGE_HAS_EXCEPTIONS
if (LuaException::areExceptionsEnabled())
LuaException::raise(L, ec);
#else
return LuaResult::errorFromStack(L, ec);
#endif
}
return LuaResult::valuesFromStack(L, stackTop);
}
//=============================================================================================
/**
* @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())
LuaException::raise(L, makeErrorCode(ErrorCode::LuaFunctionCallFailed));
#endif
return code;
}
//=============================================================================================
template <class Impl, class LuaRef>
template <class... Args>
LuaResult LuaRefBase<Impl, LuaRef>::operator()(Args&&... args) const
{
return call(*this, std::forward<Args>(args)...);
}
} // namespace luabridge