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

9746 lines
239 KiB
C++

// https://github.com/kunitoki/LuaBridge3
// Copyright 2023, Lucio Asnaghi
// SPDX-License-Identifier: MIT
// clang-format off
#pragma once
#include <array>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <functional>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <system_error>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
// Begin File: Source/LuaBridge/detail/Config.h
#if !(__cplusplus >= 201703L || (defined(_MSC_VER) && _HAS_CXX17))
#error LuaBridge 3 requires a compliant C++17 compiler, or C++17 has not been enabled !
#endif
#if defined(_MSC_VER)
#if _CPPUNWIND || _HAS_EXCEPTIONS
#define LUABRIDGE_HAS_EXCEPTIONS 1
#else
#define LUABRIDGE_HAS_EXCEPTIONS 0
#endif
#elif defined(__clang__)
#if __EXCEPTIONS && __has_feature(cxx_exceptions)
#define LUABRIDGE_HAS_EXCEPTIONS 1
#else
#define LUABRIDGE_HAS_EXCEPTIONS 0
#endif
#elif defined(__GNUC__)
#if defined(__cpp_exceptions) || defined(__EXCEPTIONS)
#define LUABRIDGE_HAS_EXCEPTIONS 1
#else
#define LUABRIDGE_HAS_EXCEPTIONS 0
#endif
#endif
#if defined(LUAU_FASTMATH_BEGIN)
#define LUABRIDGE_ON_LUAU 1
#elif defined(LUAJIT_VERSION)
#define LUABRIDGE_ON_LUAJIT 1
#elif defined(LUA_VERSION_NUM)
#define LUABRIDGE_ON_LUA 1
#else
#error "Lua headers must be included prior to LuaBridge ones"
#endif
#if defined(__OBJC__)
#define LUABRIDGE_ON_OBJECTIVE_C 1
#endif
#if !defined(LUABRIDGE_SAFE_STACK_CHECKS)
#define LUABRIDGE_SAFE_STACK_CHECKS 1
#endif
#if !defined(LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE) && LUABRIDGE_HAS_EXCEPTIONS
#define LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE 1
#endif
// End File: Source/LuaBridge/detail/Config.h
// Begin File: Source/LuaBridge/detail/LuaHelpers.h
namespace luabridge {
template <class... Args>
constexpr void unused(Args&&...)
{
}
#if LUABRIDGE_ON_LUAU
inline int luaL_ref(lua_State* L, int idx)
{
assert(idx == LUA_REGISTRYINDEX);
const int ref = lua_ref(L, -1);
lua_pop(L, 1);
return ref;
}
inline void luaL_unref(lua_State* L, int idx, int ref)
{
unused(idx);
lua_unref(L, ref);
}
template <class T>
inline void* lua_newuserdata_x(lua_State* L, size_t sz)
{
return lua_newuserdatadtor(L, sz, [](void* x)
{
T* object = static_cast<T*>(x);
object->~T();
});
}
inline void lua_pushcfunction_x(lua_State *L, lua_CFunction fn)
{
lua_pushcfunction(L, fn, "");
}
inline void lua_pushcclosure_x(lua_State* L, lua_CFunction fn, int n)
{
lua_pushcclosure(L, fn, "", n);
}
inline int lua_error_x(lua_State* L)
{
lua_error(L);
return 0;
}
inline int lua_getstack_info_x(lua_State* L, int level, const char* what, lua_Debug* ar)
{
return lua_getinfo(L, level, what, ar);
}
#else
using ::luaL_ref;
using ::luaL_unref;
template <class T>
inline void* lua_newuserdata_x(lua_State* L, size_t sz)
{
return lua_newuserdata(L, sz);
}
inline void lua_pushcfunction_x(lua_State *L, lua_CFunction fn)
{
lua_pushcfunction(L, fn);
}
inline void lua_pushcclosure_x(lua_State* L, lua_CFunction fn, int n)
{
lua_pushcclosure(L, fn, n);
}
inline int lua_error_x(lua_State* L)
{
return lua_error(L);
}
inline int lua_getstack_info_x(lua_State* L, int level, const char* what, lua_Debug* ar)
{
lua_getstack(L, level, ar);
return lua_getinfo(L, what, ar);
}
#endif
#if LUA_VERSION_NUM < 503
inline lua_Number to_numberx(lua_State* L, int idx, int* isnum)
{
lua_Number n = lua_tonumber(L, idx);
if (isnum)
*isnum = (n != 0 || lua_isnumber(L, idx));
return n;
}
inline lua_Integer to_integerx(lua_State* L, int idx, int* isnum)
{
int ok = 0;
lua_Number n = to_numberx(L, idx, &ok);
if (ok)
{
const auto int_n = static_cast<lua_Integer>(n);
if (n == static_cast<lua_Number>(int_n))
{
if (isnum)
*isnum = 1;
return int_n;
}
}
if (isnum)
*isnum = 0;
return 0;
}
#endif
#if LUA_VERSION_NUM < 502
using lua_Unsigned = std::make_unsigned_t<lua_Integer>;
#if ! LUABRIDGE_ON_LUAU
inline int lua_absindex(lua_State* L, int idx)
{
if (idx > LUA_REGISTRYINDEX && idx < 0)
return lua_gettop(L) + idx + 1;
else
return idx;
}
#endif
inline void lua_rawgetp(lua_State* L, int idx, const void* p)
{
idx = lua_absindex(L, idx);
luaL_checkstack(L, 1, "not enough stack slots");
lua_pushlightuserdata(L, const_cast<void*>(p));
lua_rawget(L, idx);
}
inline void lua_rawsetp(lua_State* L, int idx, const void* p)
{
idx = lua_absindex(L, idx);
luaL_checkstack(L, 1, "not enough stack slots");
lua_pushlightuserdata(L, const_cast<void*>(p));
lua_insert(L, -2);
lua_rawset(L, idx);
}
#define LUA_OPEQ 1
#define LUA_OPLT 2
#define LUA_OPLE 3
inline int lua_compare(lua_State* L, int idx1, int idx2, int op)
{
switch (op)
{
case LUA_OPEQ:
return lua_equal(L, idx1, idx2);
case LUA_OPLT:
return lua_lessthan(L, idx1, idx2);
case LUA_OPLE:
return lua_equal(L, idx1, idx2) || lua_lessthan(L, idx1, idx2);
default:
return 0;
}
}
inline int get_length(lua_State* L, int idx)
{
return static_cast<int>(lua_objlen(L, idx));
}
#else
inline int get_length(lua_State* L, int idx)
{
lua_len(L, idx);
const int len = static_cast<int>(luaL_checknumber(L, -1));
lua_pop(L, 1);
return len;
}
#endif
#ifndef LUA_OK
#define LUABRIDGE_LUA_OK 0
#else
#define LUABRIDGE_LUA_OK LUA_OK
#endif
template <class T, class ErrorType>
std::error_code throw_or_error_code(ErrorType error)
{
#if LUABRIDGE_HAS_EXCEPTIONS
throw T(makeErrorCode(error).message().c_str());
#else
return makeErrorCode(error);
#endif
}
template <class T, class ErrorType>
std::error_code throw_or_error_code(lua_State* L, ErrorType error)
{
#if LUABRIDGE_HAS_EXCEPTIONS
throw T(L, makeErrorCode(error));
#else
return unused(L), makeErrorCode(error);
#endif
}
template <class T, class... Args>
void throw_or_assert(Args&&... args)
{
#if LUABRIDGE_HAS_EXCEPTIONS
throw T(std::forward<Args>(args)...);
#else
unused(std::forward<Args>(args)...);
assert(false);
#endif
}
template <class T>
void pushunsigned(lua_State* L, T value)
{
static_assert(std::is_unsigned_v<T>);
lua_pushinteger(L, static_cast<lua_Integer>(value));
}
inline lua_Number tonumber(lua_State* L, int idx, int* isnum)
{
#if ! LUABRIDGE_ON_LUAU && LUA_VERSION_NUM > 502
return lua_tonumberx(L, idx, isnum);
#else
return to_numberx(L, idx, isnum);
#endif
}
inline lua_Integer tointeger(lua_State* L, int idx, int* isnum)
{
#if ! LUABRIDGE_ON_LUAU && LUA_VERSION_NUM > 502
return lua_tointegerx(L, idx, isnum);
#else
return to_integerx(L, idx, isnum);
#endif
}
inline constexpr char main_thread_name[] = "__luabridge_main_thread";
inline void register_main_thread(lua_State* threadL)
{
#if LUA_VERSION_NUM < 502
if (threadL == nullptr)
lua_pushnil(threadL);
else
lua_pushthread(threadL);
lua_setglobal(threadL, main_thread_name);
#else
unused(threadL);
#endif
}
inline lua_State* main_thread(lua_State* threadL)
{
#if LUA_VERSION_NUM < 502
lua_getglobal(threadL, main_thread_name);
if (lua_isthread(threadL, -1))
{
auto L = lua_tothread(threadL, -1);
lua_pop(threadL, 1);
return L;
}
assert(false);
lua_pop(threadL, 1);
return threadL;
#else
lua_rawgeti(threadL, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
lua_State* L = lua_tothread(threadL, -1);
lua_pop(threadL, 1);
return L;
#endif
}
inline void rawgetfield(lua_State* L, int index, char const* key)
{
assert(lua_istable(L, index));
index = lua_absindex(L, index);
lua_pushstring(L, key);
lua_rawget(L, index);
}
inline void rawsetfield(lua_State* L, int index, char const* key)
{
assert(lua_istable(L, index));
index = lua_absindex(L, index);
lua_pushstring(L, key);
lua_insert(L, -2);
lua_rawset(L, index);
}
[[nodiscard]] inline bool isfulluserdata(lua_State* L, int index)
{
return lua_isuserdata(L, index) && !lua_islightuserdata(L, index);
}
[[nodiscard]] inline bool equalstates(lua_State* L1, lua_State* L2)
{
return lua_topointer(L1, LUA_REGISTRYINDEX) == lua_topointer(L2, LUA_REGISTRYINDEX);
}
[[nodiscard]] inline int table_length(lua_State* L, int index)
{
assert(lua_istable(L, index));
int items_count = 0;
lua_pushnil(L);
while (lua_next(L, index) != 0)
{
++items_count;
lua_pop(L, 1);
}
return items_count;
}
template <class T>
[[nodiscard]] T* align(void* ptr) noexcept
{
const auto address = reinterpret_cast<size_t>(ptr);
const auto offset = address % alignof(T);
const auto aligned_address = (offset == 0) ? address : (address + alignof(T) - offset);
return reinterpret_cast<T*>(aligned_address);
}
template <class T>
[[nodiscard]] constexpr size_t maximum_space_needed_to_align() noexcept
{
return sizeof(T) + alignof(T) - 1;
}
template <class T>
int lua_deleteuserdata_aligned(lua_State* L)
{
assert(isfulluserdata(L, 1));
T* aligned = align<T>(lua_touserdata(L, 1));
aligned->~T();
return 0;
}
template <class T, class... Args>
void* lua_newuserdata_aligned(lua_State* L, Args&&... args)
{
#if LUABRIDGE_ON_LUAU
void* pointer = lua_newuserdatadtor(L, maximum_space_needed_to_align<T>(), [](void* x)
{
T* aligned = align<T>(x);
aligned->~T();
});
#else
void* pointer = lua_newuserdata_x<T>(L, maximum_space_needed_to_align<T>());
lua_newtable(L);
lua_pushcfunction_x(L, &lua_deleteuserdata_aligned<T>);
rawsetfield(L, -2, "__gc");
lua_setmetatable(L, -2);
#endif
T* aligned = align<T>(pointer);
new (aligned) T(std::forward<Args>(args)...);
return pointer;
}
inline int raise_lua_error(lua_State *L, const char *fmt, ...)
{
va_list argp;
va_start(argp, fmt);
bool pushed_error = false;
for (int level = 2; level > 0; --level)
{
lua_Debug ar;
#if LUABRIDGE_ON_LUAU
if (lua_getinfo(L, level, "sl", &ar) == 0)
continue;
#else
if (lua_getstack(L, level, &ar) == 0 || lua_getinfo(L, "Sl", &ar) == 0)
continue;
#endif
if (ar.currentline <= 0)
continue;
lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline);
pushed_error = true;
break;
}
if (! pushed_error)
lua_pushliteral(L, "");
lua_pushvfstring(L, fmt, argp);
va_end(argp);
lua_concat(L, 2);
return lua_error_x(L);
}
template <class U = lua_Integer, class T>
constexpr bool is_integral_representable_by(T value)
{
constexpr bool same_signedness = (std::is_unsigned_v<T> && std::is_unsigned_v<U>)
|| (!std::is_unsigned_v<T> && !std::is_unsigned_v<U>);
if constexpr (sizeof(T) == sizeof(U))
{
if constexpr (same_signedness)
return true;
if constexpr (std::is_unsigned_v<T>)
return value <= static_cast<T>(std::numeric_limits<U>::max());
return value >= static_cast<T>(std::numeric_limits<U>::min())
&& static_cast<U>(value) <= std::numeric_limits<U>::max();
}
if constexpr (sizeof(T) < sizeof(U))
{
return static_cast<U>(value) >= std::numeric_limits<U>::min()
&& static_cast<U>(value) <= std::numeric_limits<U>::max();
}
if constexpr (std::is_unsigned_v<T>)
return value <= static_cast<T>(std::numeric_limits<U>::max());
return value >= static_cast<T>(std::numeric_limits<U>::min())
&& value <= static_cast<T>(std::numeric_limits<U>::max());
}
template <class U = lua_Integer>
bool is_integral_representable_by(lua_State* L, int index)
{
int isValid = 0;
const auto value = tointeger(L, index, &isValid);
return isValid ? is_integral_representable_by<U>(value) : false;
}
template <class U = lua_Number, class T>
constexpr bool is_floating_point_representable_by(T value)
{
if constexpr (sizeof(T) == sizeof(U))
return true;
if constexpr (sizeof(T) < sizeof(U))
return static_cast<U>(value) >= -std::numeric_limits<U>::max()
&& static_cast<U>(value) <= std::numeric_limits<U>::max();
return value >= static_cast<T>(-std::numeric_limits<U>::max())
&& value <= static_cast<T>(std::numeric_limits<U>::max());
}
template <class U = lua_Number>
bool is_floating_point_representable_by(lua_State* L, int index)
{
int isValid = 0;
const auto value = tonumber(L, index, &isValid);
return isValid ? is_floating_point_representable_by<U>(value) : false;
}
}
// End File: Source/LuaBridge/detail/LuaHelpers.h
// Begin File: Source/LuaBridge/detail/Errors.h
namespace luabridge {
namespace detail {
static inline constexpr char error_lua_stack_overflow[] = "stack overflow";
}
enum class ErrorCode
{
ClassNotRegistered = 1,
LuaStackOverflow,
LuaFunctionCallFailed,
IntegerDoesntFitIntoLuaInteger,
FloatingPointDoesntFitIntoLuaNumber,
InvalidTypeCast,
InvalidTableSizeInCast
};
namespace detail {
struct ErrorCategory : std::error_category
{
const char* name() const noexcept override
{
return "luabridge";
}
std::string message(int ev) const override
{
switch (static_cast<ErrorCode>(ev))
{
case ErrorCode::ClassNotRegistered:
return "The class is not registered in LuaBridge";
case ErrorCode::LuaStackOverflow:
return "The lua stack has overflow";
case ErrorCode::LuaFunctionCallFailed:
return "The lua function invocation raised an error";
case ErrorCode::IntegerDoesntFitIntoLuaInteger:
return "The native integer can't fit inside a lua integer";
case ErrorCode::FloatingPointDoesntFitIntoLuaNumber:
return "The native floating point can't fit inside a lua number";
case ErrorCode::InvalidTypeCast:
return "The lua object can't be casted to desired type";
case ErrorCode::InvalidTableSizeInCast:
return "The lua table has different size than expected";
default:
return "Unknown error";
}
}
static const ErrorCategory& getInstance() noexcept
{
static ErrorCategory category;
return category;
}
};
}
inline std::error_code makeErrorCode(ErrorCode e)
{
return { static_cast<int>(e), detail::ErrorCategory::getInstance() };
}
inline std::error_code make_error_code(ErrorCode e)
{
return { static_cast<int>(e), detail::ErrorCategory::getInstance() };
}
}
namespace std {
template <> struct is_error_code_enum<luabridge::ErrorCode> : true_type {};
}
// End File: Source/LuaBridge/detail/Errors.h
// Begin File: Source/LuaBridge/detail/Expected.h
#if LUABRIDGE_HAS_EXCEPTIONS
#endif
namespace luabridge {
namespace detail {
using std::swap;
template <class T, class... Args>
T* construct_at(T* ptr, Args&&... args) noexcept(std::is_nothrow_constructible<T, Args...>::value)
{
return static_cast<T*>(::new (const_cast<void*>(static_cast<const void*>(ptr))) T(std::forward<Args>(args)...));
}
template <class T, class U, class = void>
struct is_swappable_with_impl : std::false_type
{
};
template <class T, class U>
struct is_swappable_with_impl<T, U, std::void_t<decltype(swap(std::declval<T>(), std::declval<U>()))>>
: std::true_type
{
};
template <class T, class U>
struct is_nothrow_swappable_with_impl
{
static constexpr bool value = noexcept(swap(std::declval<T>(), std::declval<U>())) && noexcept(swap(std::declval<U>(), std::declval<T>()));
using type = std::bool_constant<value>;
};
template <class T, class U>
struct is_swappable_with
: std::conjunction<
is_swappable_with_impl<std::add_lvalue_reference_t<T>, std::add_lvalue_reference_t<U>>,
is_swappable_with_impl<std::add_lvalue_reference_t<U>, std::add_lvalue_reference_t<T>>>::type
{
};
template <class T, class U>
struct is_nothrow_swappable_with
: std::conjunction<is_swappable_with<T, U>, is_nothrow_swappable_with_impl<T, U>>::type
{
};
template <class T>
struct is_nothrow_swappable
: std::is_nothrow_swappable_with<std::add_lvalue_reference_t<T>, std::add_lvalue_reference_t<T>>
{
};
}
template <class T, class E>
class Expected;
struct UnexpectType
{
constexpr UnexpectType() = default;
};
static constexpr const auto& unexpect = UnexpectType();
namespace detail {
template <class T, class E, bool = std::is_default_constructible_v<T>, bool = (std::is_void_v<T> || std::is_trivial_v<T>) && std::is_trivial_v<E>>
union expected_storage
{
public:
template <class U = T, class = std::enable_if_t<std::is_default_constructible_v<U>>>
constexpr expected_storage() noexcept
: value_()
{
}
template <class... Args>
constexpr explicit expected_storage(std::in_place_t, Args&&... args) noexcept
: value_(std::forward<Args>(args)...)
{
}
template <class... Args>
constexpr explicit expected_storage(UnexpectType, Args&&... args) noexcept
: error_(std::forward<Args>(args)...)
{
}
~expected_storage() = default;
constexpr const T& value() const noexcept
{
return value_;
}
constexpr T& value() noexcept
{
return value_;
}
constexpr const E& error() const noexcept
{
return error_;
}
constexpr E& error() noexcept
{
return error_;
}
private:
T value_;
E error_;
};
template <class E>
union expected_storage<void, E, true, true>
{
public:
constexpr expected_storage() noexcept
: dummy_(0)
{
}
template <class... Args>
constexpr explicit expected_storage(UnexpectType, Args&&... args) noexcept
: error_(std::forward<Args>(args)...)
{
}
~expected_storage() = default;
constexpr const E& error() const noexcept
{
return error_;
}
constexpr E& error() noexcept
{
return error_;
}
private:
char dummy_;
E error_;
};
template <class T, class E>
union expected_storage<T, E, true, false>
{
public:
constexpr expected_storage() noexcept(std::is_nothrow_default_constructible_v<T>)
: value_()
{
}
template <class... Args>
constexpr explicit expected_storage(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
: value_(std::forward<Args>(args)...)
{
}
template <class... Args>
constexpr explicit expected_storage(UnexpectType, Args&&... args) noexcept(std::is_nothrow_constructible_v<E, Args...>)
: error_(std::forward<Args>(args)...)
{
}
~expected_storage()
{
}
constexpr const T& value() const noexcept
{
return value_;
}
constexpr T& value() noexcept
{
return value_;
}
constexpr const E& error() const noexcept
{
return error_;
}
constexpr E& error() noexcept
{
return error_;
}
private:
T value_;
E error_;
};
template <class T, class E>
union expected_storage<T, E, false, false>
{
public:
constexpr explicit expected_storage() noexcept
: dummy_(0)
{
}
template <class... Args>
constexpr explicit expected_storage(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
: value_(std::forward<Args>(args)...)
{
}
template <class... Args>
constexpr explicit expected_storage(UnexpectType, Args&&... args) noexcept(std::is_nothrow_constructible_v<E, Args...>)
: error_(std::forward<Args>(args)...)
{
}
~expected_storage()
{
}
constexpr const T& value() const noexcept
{
return value_;
}
constexpr T& value() noexcept
{
return value_;
}
constexpr const E& error() const noexcept
{
return error_;
}
constexpr E& error() noexcept
{
return error_;
}
private:
char dummy_;
T value_;
E error_;
};
template <class E>
union expected_storage<void, E, true, false>
{
public:
constexpr expected_storage() noexcept
: dummy_(0)
{
}
template <class... Args>
constexpr explicit expected_storage(UnexpectType, Args&&... args) noexcept(std::is_nothrow_constructible_v<E, Args...>)
: error_(std::forward<Args>(args)...)
{
}
~expected_storage()
{
}
constexpr const E& error() const noexcept
{
return error_;
}
constexpr E& error() noexcept
{
return error_;
}
private:
char dummy_;
E error_;
};
template <class T, class E, bool IsCopyConstructible, bool IsMoveConstructible>
class ExpectedBaseTrivial
{
using this_type = ExpectedBaseTrivial<T, E, IsCopyConstructible, IsMoveConstructible>;
protected:
using storage_type = expected_storage<T, E>;
constexpr ExpectedBaseTrivial() noexcept
: valid_(true)
{
}
template <class... Args>
constexpr ExpectedBaseTrivial(std::in_place_t, Args&&... args) noexcept
: storage_(std::in_place, std::forward<Args>(args)...)
, valid_(true)
{
}
template <class... Args>
constexpr ExpectedBaseTrivial(UnexpectType, Args&&... args) noexcept
: storage_(unexpect, std::forward<Args>(args)...)
, valid_(false)
{
}
ExpectedBaseTrivial(const ExpectedBaseTrivial& other) noexcept
{
if (other.valid_)
{
construct(std::in_place, other.value());
}
else
{
construct(unexpect, other.error());
}
}
ExpectedBaseTrivial(ExpectedBaseTrivial&& other) noexcept
{
if (other.valid_)
{
construct(std::in_place, std::move(other.value()));
}
else
{
construct(unexpect, std::move(other.error()));
}
}
~ExpectedBaseTrivial() noexcept = default;
constexpr const T& value() const noexcept
{
return storage_.value();
}
constexpr T& value() noexcept
{
return storage_.value();
}
constexpr const E& error() const noexcept
{
return storage_.error();
}
constexpr E& error() noexcept
{
return storage_.error();
}
constexpr const T* valuePtr() const noexcept
{
return std::addressof(value());
}
constexpr T* valuePtr() noexcept
{
return std::addressof(value());
}
constexpr const E* errorPtr() const noexcept
{
return std::addressof(error());
}
constexpr E* errorPtr() noexcept
{
return std::addressof(error());
}
constexpr bool valid() const noexcept
{
return valid_;
}
template <class... Args>
inline T& construct(std::in_place_t, Args&&... args) noexcept
{
valid_ = true;
return *detail::construct_at(valuePtr(), std::forward<Args>(args)...);
}
template <class... Args>
inline E& construct(UnexpectType, Args&&... args) noexcept
{
valid_ = false;
return *detail::construct_at(errorPtr(), std::forward<Args>(args)...);
}
inline void destroy() noexcept
{
}
private:
storage_type storage_;
bool valid_;
};
template <class T, class E, bool IsCopyConstructible, bool IsMoveConstructible>
class ExpectedBaseNonTrivial
{
using this_type = ExpectedBaseNonTrivial<T, E, IsCopyConstructible, IsMoveConstructible>;
protected:
using storage_type = expected_storage<T, E>;
constexpr ExpectedBaseNonTrivial() noexcept(std::is_nothrow_default_constructible_v<storage_type>)
: valid_(true)
{
}
template <class... Args>
constexpr ExpectedBaseNonTrivial(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<storage_type, std::in_place_t, Args...>)
: storage_(std::in_place, std::forward<Args>(args)...)
, valid_(true)
{
}
template <class... Args>
constexpr ExpectedBaseNonTrivial(UnexpectType, Args&&... args) noexcept(std::is_nothrow_constructible_v<storage_type, UnexpectType, Args...>)
: storage_(unexpect, std::forward<Args>(args)...)
, valid_(false)
{
}
ExpectedBaseNonTrivial(const ExpectedBaseNonTrivial& other)
{
if (other.valid_)
{
construct(std::in_place, other.value());
}
else
{
construct(unexpect, other.error());
}
}
ExpectedBaseNonTrivial(ExpectedBaseNonTrivial&& other)
{
if (other.valid_)
{
construct(std::in_place, std::move(other.value()));
}
else
{
construct(unexpect, std::move(other.error()));
}
}
~ExpectedBaseNonTrivial() noexcept(noexcept(std::declval<this_type>().destroy()))
{
destroy();
}
constexpr const T& value() const noexcept
{
return storage_.value();
}
constexpr T& value() noexcept
{
return storage_.value();
}
constexpr const E& error() const noexcept
{
return storage_.error();
}
constexpr E& error() noexcept
{
return storage_.error();
}
constexpr const T* valuePtr() const noexcept
{
return std::addressof(value());
}
constexpr T* valuePtr() noexcept
{
return std::addressof(value());
}
constexpr const E* errorPtr() const noexcept
{
return std::addressof(error());
}
constexpr E* errorPtr() noexcept
{
return std::addressof(error());
}
constexpr bool valid() const noexcept
{
return valid_;
}
template <class... Args>
inline T& construct(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
{
valid_ = true;
return *detail::construct_at(valuePtr(), std::forward<Args>(args)...);
}
template <class... Args>
inline E& construct(UnexpectType, Args&&... args) noexcept(std::is_nothrow_constructible_v<E, Args...>)
{
valid_ = false;
return *detail::construct_at(errorPtr(), std::forward<Args>(args)...);
}
inline void destroy() noexcept(std::is_nothrow_destructible_v<T>&& std::is_nothrow_destructible_v<E>)
{
if (valid_)
{
std::destroy_at(valuePtr());
}
else
{
std::destroy_at(errorPtr());
}
}
private:
storage_type storage_;
bool valid_;
};
template <class T, class E, bool IsMoveConstructible>
class ExpectedBaseNonTrivial<T, E, false, IsMoveConstructible>
{
using this_type = ExpectedBaseNonTrivial<T, E, false, IsMoveConstructible>;
protected:
using storage_type = expected_storage<T, E>;
constexpr ExpectedBaseNonTrivial() noexcept(std::is_nothrow_default_constructible_v<storage_type>)
: valid_(true)
{
}
template <class... Args>
constexpr ExpectedBaseNonTrivial(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<storage_type, std::in_place_t, Args...>)
: storage_(std::in_place, std::forward<Args>(args)...)
, valid_(true)
{
}
template <class... Args>
constexpr ExpectedBaseNonTrivial(UnexpectType, Args&&... args) noexcept(std::is_nothrow_constructible_v<storage_type, UnexpectType, Args...>)
: storage_(unexpect, std::forward<Args>(args)...)
, valid_(false)
{
}
ExpectedBaseNonTrivial(const ExpectedBaseNonTrivial& other) = delete;
ExpectedBaseNonTrivial(ExpectedBaseNonTrivial&& other)
{
if (other.valid_)
{
construct(std::in_place, std::move(other.value()));
}
else
{
construct(unexpect, std::move(other.error()));
}
}
~ExpectedBaseNonTrivial() noexcept(noexcept(std::declval<this_type>().destroy()))
{
destroy();
}
constexpr const T& value() const noexcept
{
return storage_.value();
}
constexpr T& value() noexcept
{
return storage_.value();
}
constexpr const E& error() const noexcept
{
return storage_.error();
}
constexpr E& error() noexcept
{
return storage_.error();
}
constexpr const T* valuePtr() const noexcept
{
return std::addressof(value());
}
constexpr T* valuePtr() noexcept
{
return std::addressof(value());
}
constexpr const E* errorPtr() const noexcept
{
return std::addressof(error());
}
constexpr E* errorPtr() noexcept
{
return std::addressof(error());
}
constexpr bool valid() const noexcept
{
return valid_;
}
template <class... Args>
inline T& construct(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
{
valid_ = true;
return *detail::construct_at(valuePtr(), std::forward<Args>(args)...);
}
template <class... Args>
inline E& construct(UnexpectType, Args&&... args) noexcept(std::is_nothrow_constructible_v<E, Args...>)
{
valid_ = false;
return *detail::construct_at(errorPtr(), std::forward<Args>(args)...);
}
inline void destroy() noexcept(std::is_nothrow_destructible_v<T>&& std::is_nothrow_destructible_v<E>)
{
if (valid_)
{
std::destroy_at(valuePtr());
}
else
{
std::destroy_at(errorPtr());
}
}
private:
storage_type storage_;
bool valid_;
};
template <class T, class E, bool IsCopyConstructible>
class ExpectedBaseNonTrivial<T, E, IsCopyConstructible, false>
{
using this_type = ExpectedBaseNonTrivial<T, E, IsCopyConstructible, false>;
protected:
using storage_type = expected_storage<T, E>;
template <class U = storage_type, class = std::enable_if_t<std::is_default_constructible_v<U>>>
constexpr ExpectedBaseNonTrivial() noexcept(std::is_nothrow_default_constructible_v<storage_type>)
: valid_(true)
{
}
template <class... Args>
constexpr ExpectedBaseNonTrivial(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<storage_type, std::in_place_t, Args...>)
: storage_(std::in_place, std::forward<Args>(args)...)
, valid_(true)
{
}
template <class... Args>
constexpr ExpectedBaseNonTrivial(UnexpectType, Args&&... args) noexcept(std::is_nothrow_constructible_v<storage_type, UnexpectType, Args...>)
: storage_(unexpect, std::forward<Args>(args)...)
, valid_(false)
{
}
ExpectedBaseNonTrivial(const ExpectedBaseNonTrivial& other)
{
if (other.valid_)
{
construct(std::in_place, other.value());
}
else
{
construct(unexpect, other.error());
}
}
ExpectedBaseNonTrivial(ExpectedBaseNonTrivial&& other) = delete;
~ExpectedBaseNonTrivial() noexcept(noexcept(std::declval<this_type>().destroy()))
{
destroy();
}
constexpr const T& value() const noexcept
{
return storage_.value();
}
constexpr T& value() noexcept
{
return storage_.value();
}
constexpr const E& error() const noexcept
{
return storage_.error();
}
constexpr E& error() noexcept
{
return storage_.error();
}
constexpr const T* valuePtr() const noexcept
{
return std::addressof(value());
}
constexpr T* valuePtr() noexcept
{
return std::addressof(value());
}
constexpr const E* errorPtr() const noexcept
{
return std::addressof(error());
}
constexpr E* errorPtr() noexcept
{
return std::addressof(error());
}
constexpr bool valid() const noexcept
{
return valid_;
}
template <class... Args>
inline T& construct(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
{
valid_ = true;
return *detail::construct_at(valuePtr(), std::forward<Args>(args)...);
}
template <class... Args>
inline E& construct(UnexpectType, Args&&... args) noexcept(std::is_nothrow_constructible_v<E, Args...>)
{
valid_ = false;
return *detail::construct_at(errorPtr(), std::forward<Args>(args)...);
}
inline void destroy() noexcept(std::is_nothrow_destructible_v<T>&& std::is_nothrow_destructible_v<E>)
{
if (valid_)
{
std::destroy_at(valuePtr());
}
else
{
std::destroy_at(errorPtr());
}
}
private:
storage_type storage_;
bool valid_;
};
template <class T, class E>
class ExpectedBaseNonTrivial<T, E, false, false>
{
using this_type = ExpectedBaseNonTrivial<T, E, false, false>;
protected:
using storage_type = expected_storage<T, E>;
template <class U = storage_type, class = std::enable_if_t<std::is_default_constructible_v<U>>>
constexpr ExpectedBaseNonTrivial() noexcept(std::is_nothrow_default_constructible_v<storage_type>)
: valid_(true)
{
}
template <class... Args>
constexpr ExpectedBaseNonTrivial(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<storage_type, std::in_place_t, Args...>)
: storage_(std::in_place, std::forward<Args>(args)...)
, valid_(true)
{
}
template <class... Args>
constexpr ExpectedBaseNonTrivial(UnexpectType, Args&&... args) noexcept(std::is_nothrow_constructible_v<storage_type, UnexpectType, Args...>)
: storage_(unexpect, std::forward<Args>(args)...)
, valid_(false)
{
}
ExpectedBaseNonTrivial(const ExpectedBaseNonTrivial& other) = delete;
ExpectedBaseNonTrivial(ExpectedBaseNonTrivial&& other) = delete;
~ExpectedBaseNonTrivial() noexcept(noexcept(std::declval<this_type>().destroy()))
{
destroy();
}
constexpr const T& value() const noexcept
{
return storage_.value();
}
constexpr T& value() noexcept
{
return storage_.value();
}
constexpr const E& error() const noexcept
{
return storage_.error();
}
constexpr E& error() noexcept
{
return storage_.error();
}
constexpr const T* valuePtr() const noexcept
{
return std::addressof(value());
}
constexpr T* valuePtr() noexcept
{
return std::addressof(value());
}
constexpr const E* errorPtr() const noexcept
{
return std::addressof(error());
}
constexpr E* errorPtr() noexcept
{
return std::addressof(error());
}
constexpr bool valid() const noexcept
{
return valid_;
}
template <class... Args>
inline T& construct(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
{
valid_ = true;
return *detail::construct_at(valuePtr(), std::forward<Args>(args)...);
}
template <class... Args>
inline E& construct(UnexpectType, Args&&... args) noexcept(std::is_nothrow_constructible_v<E, Args...>)
{
valid_ = false;
return *detail::construct_at(errorPtr(), std::forward<Args>(args)...);
}
inline void destroy() noexcept(std::is_nothrow_destructible_v<T>&& std::is_nothrow_destructible_v<E>)
{
if (valid_)
{
std::destroy_at(valuePtr());
}
else
{
std::destroy_at(errorPtr());
}
}
private:
storage_type storage_;
bool valid_;
};
template <class T, class E, bool IsCopyConstructible, bool IsMoveConstructible>
using ExpectedBase = std::conditional_t<
(std::is_void_v<T> || std::is_trivially_destructible_v<T>) && std::is_trivially_destructible_v<E>,
ExpectedBaseTrivial<T, E, IsCopyConstructible, IsMoveConstructible>,
ExpectedBaseNonTrivial<T, E, IsCopyConstructible, IsMoveConstructible>>;
}
template <class E>
class Unexpected
{
static_assert(!std::is_reference_v<E> && !std::is_void_v<E>, "Unexpected type can't be a reference or void");
public:
Unexpected() = delete;
constexpr explicit Unexpected(E&& e) noexcept(std::is_nothrow_move_constructible_v<E>)
: error_(std::move(e))
{
}
constexpr explicit Unexpected(const E& e) noexcept(std::is_nothrow_copy_constructible_v<E>)
: error_(e)
{
}
constexpr const E& value() const& noexcept
{
return error_;
}
constexpr E& value() & noexcept
{
return error_;
}
constexpr const E&& value() const&& noexcept
{
return std::move(error_);
}
constexpr E&& value() && noexcept
{
return std::move(error_);
}
private:
E error_;
};
template <class E>
constexpr bool operator==(const Unexpected<E>& lhs, const Unexpected<E>& rhs) noexcept
{
return lhs.value() == rhs.value();
}
template <class E>
constexpr bool operator!=(const Unexpected<E>& lhs, const Unexpected<E>& rhs) noexcept
{
return lhs.value() != rhs.value();
}
template <class E>
constexpr inline Unexpected<std::decay_t<E>> makeUnexpected(E&& error) noexcept(std::is_nothrow_constructible_v<Unexpected<std::decay_t<E>>, E>)
{
return Unexpected<std::decay_t<E>>{ std::forward<E>(error) };
}
#if LUABRIDGE_HAS_EXCEPTIONS
template <class E>
class BadExpectedAccess;
template <>
class BadExpectedAccess<void> : public std::exception
{
public:
explicit BadExpectedAccess() noexcept
{
}
};
template <class E>
class BadExpectedAccess : public BadExpectedAccess<void>
{
public:
explicit BadExpectedAccess(E error) noexcept(std::is_nothrow_constructible_v<E, E&&>)
: error_(std::move(error))
{
}
const E& error() const& noexcept
{
return error_;
}
E& error() & noexcept
{
return error_;
}
E&& error() && noexcept
{
return std::move(error_);
}
private:
E error_;
};
#endif
template <class T>
struct is_expected : std::false_type
{
};
template <class T, class E>
struct is_expected<Expected<T, E>> : std::true_type
{
};
template <class T>
struct is_unexpected : std::false_type
{
};
template <class E>
struct is_unexpected<Unexpected<E>> : std::true_type
{
};
template <class T, class E>
class Expected : public detail::ExpectedBase<T, E, std::is_copy_constructible_v<T>, std::is_move_constructible_v<T>>
{
static_assert(!std::is_reference_v<E> && !std::is_void_v<E>, "Unexpected type can't be a reference or void");
using base_type = detail::ExpectedBase<T, E, std::is_copy_constructible_v<T>, std::is_move_constructible_v<T>>;
using this_type = Expected<T, E>;
public:
using value_type = T;
using error_type = E;
using unexpected_type = Unexpected<E>;
template <class U>
struct rebind
{
using type = Expected<U, error_type>;
};
template <class U = T, class = std::enable_if_t<std::is_default_constructible_v<U>>>
constexpr Expected() noexcept(std::is_nothrow_default_constructible_v<base_type>)
: base_type()
{
}
constexpr Expected(const Expected& other) noexcept(std::is_nothrow_copy_constructible_v<base_type>) = default;
constexpr Expected(Expected&& other) noexcept(std::is_nothrow_move_constructible_v<base_type>) = default;
template <class U, class G>
Expected(const Expected<U, G>& other)
{
if (other.hasValue())
{
this->construct(std::in_place, other.value());
}
else
{
this->construct(unexpect, other.error());
}
}
template <class U, class G>
Expected(Expected<U, G>&& other)
{
if (other.hasValue())
{
this->construct(std::in_place, std::move(other.value()));
}
else
{
this->construct(unexpect, std::move(other.error()));
}
}
template <class U = T, std::enable_if_t<!std::is_void_v<T> && std::is_constructible_v<T, U&&> && !std::is_same_v<std::decay_t<U>, std::in_place_t> && !is_expected<std::decay_t<U>>::value && !is_unexpected<std::decay_t<U>>::value, int> = 0>
constexpr Expected(U&& value) noexcept(std::is_nothrow_constructible_v<base_type, std::in_place_t, U>)
: base_type(std::in_place, std::forward<U>(value))
{
}
template <class... Args>
constexpr explicit Expected(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<base_type, std::in_place_t, Args...>)
: base_type(std::in_place, std::forward<Args>(args)...)
{
}
template <class U, class... Args>
constexpr explicit Expected(std::in_place_t, std::initializer_list<U> ilist, Args&&... args) noexcept(std::is_nothrow_constructible_v<base_type, std::in_place_t, std::initializer_list<U>, Args...>)
: base_type(std::in_place, ilist, std::forward<Args>(args)...)
{
}
template <class G = E>
constexpr Expected(const Unexpected<G>& u) noexcept(std::is_nothrow_constructible_v<base_type, UnexpectType, const G&>)
: base_type(unexpect, u.value())
{
}
template <class G = E>
constexpr Expected(Unexpected<G>&& u) noexcept(std::is_nothrow_constructible_v<base_type, UnexpectType, G&&>)
: base_type(unexpect, std::move(u.value()))
{
}
template <class... Args>
constexpr explicit Expected(UnexpectType, Args&&... args) noexcept(std::is_nothrow_constructible_v<base_type, UnexpectType, Args...>)
: base_type(unexpect, std::forward<Args>(args)...)
{
}
template <class U, class... Args>
constexpr explicit Expected(UnexpectType, std::initializer_list<U> ilist, Args&&... args) noexcept(std::is_nothrow_constructible_v<base_type, UnexpectType, std::initializer_list<U>, Args...>)
: base_type(unexpect, ilist, std::forward<Args>(args)...)
{
}
Expected& operator=(const Expected& other)
{
if (other.hasValue())
{
assign(std::in_place, other.value());
}
else
{
assign(unexpect, other.error());
}
return *this;
}
Expected& operator=(Expected&& other)
{
if (other.hasValue())
{
assign(std::in_place, std::move(other.value()));
}
else
{
assign(unexpect, std::move(other.error()));
}
return *this;
}
template <class U = T, std::enable_if_t<!is_expected<std::decay_t<U>>::value && !is_unexpected<std::decay_t<U>>::value, int> = 0>
Expected& operator=(U&& value)
{
assign(std::in_place, std::forward<U>(value));
return *this;
}
template <class G = E>
Expected& operator=(const Unexpected<G>& u)
{
assign(unexpect, u.value());
return *this;
}
template <class G = E>
Expected& operator=(Unexpected<G>&& u)
{
assign(unexpect, std::move(u.value()));
return *this;
}
template <class... Args>
T& emplace(Args&&... args) noexcept(noexcept(std::declval<this_type>().assign(std::in_place, std::forward<Args>(args)...)))
{
return assign(std::in_place, std::forward<Args>(args)...);
}
template <class U, class... Args>
T& emplace(std::initializer_list<U> ilist, Args&&... args) noexcept(noexcept(std::declval<this_type>().assign(std::in_place, ilist, std::forward<Args>(args)...)))
{
return assign(std::in_place, ilist, std::forward<Args>(args)...);
}
void swap(Expected& other) noexcept(detail::is_nothrow_swappable<value_type>::value && detail::is_nothrow_swappable<error_type>::value)
{
using std::swap;
if (hasValue())
{
if (other.hasValue())
{
swap(value(), other.value());
}
else
{
E error = std::move(other.error());
other.assign(std::in_place, std::move(value()));
assign(unexpect, std::move(error));
}
}
else
{
if (other.hasValue())
{
other.swap(*this);
}
else
{
swap(error(), other.error());
}
}
}
constexpr const T* operator->() const
{
return base_type::valuePtr();
}
constexpr T* operator->()
{
return base_type::valuePtr();
}
constexpr const T& operator*() const&
{
return value();
}
constexpr T& operator*() &
{
return value();
}
constexpr const T&& operator*() const&&
{
return std::move(value());
}
constexpr T&& operator*() &&
{
return std::move(value());
}
constexpr explicit operator bool() const noexcept
{
return hasValue();
}
constexpr bool hasValue() const noexcept
{
return base_type::valid();
}
constexpr const T& value() const&
{
#if LUABRIDGE_HAS_EXCEPTIONS
if (!hasValue())
throw BadExpectedAccess<E>(error());
#endif
return base_type::value();
}
constexpr T& value() &
{
#if LUABRIDGE_HAS_EXCEPTIONS
if (!hasValue())
throw BadExpectedAccess<E>(error());
#endif
return base_type::value();
}
constexpr const T&& value() const&& noexcept
{
#if LUABRIDGE_HAS_EXCEPTIONS
if (!hasValue())
throw BadExpectedAccess<E>(error());
#endif
return std::move(base_type::value());
}
constexpr T&& value() &&
{
#if LUABRIDGE_HAS_EXCEPTIONS
if (!hasValue())
throw BadExpectedAccess<E>(error());
#endif
return std::move(base_type::value());
}
constexpr const E& error() const& noexcept
{
return base_type::error();
}
constexpr E& error() & noexcept
{
return base_type::error();
}
constexpr const E&& error() const&& noexcept
{
return std::move(base_type::error());
}
constexpr E&& error() && noexcept
{
return std::move(base_type::error());
}
template <class U>
constexpr T valueOr(U&& defaultValue) const&
{
return hasValue() ? value() : static_cast<T>(std::forward<U>(defaultValue));
}
template <class U>
T valueOr(U&& defaultValue) &&
{
return hasValue() ? std::move(value()) : static_cast<T>(std::forward<U>(defaultValue));
}
private:
template <class Tag, class... Args>
auto assign(Tag tag, Args&&... args) noexcept(noexcept(std::declval<this_type>().destroy()) && noexcept(std::declval<this_type>().construct(tag, std::forward<Args>(args)...)))
-> decltype(std::declval<this_type>().construct(tag, std::forward<Args>(args)...))
{
this->destroy();
return this->construct(tag, std::forward<Args>(args)...);
}
};
template <class E>
class Expected<void, E> : public detail::ExpectedBase<void, E, std::is_copy_constructible_v<E>, std::is_move_constructible_v<E>>
{
static_assert(!std::is_reference_v<E> && !std::is_void_v<E>, "Unexpected type can't be a reference or void");
using base_type = detail::ExpectedBase<void, E, std::is_copy_constructible_v<E>, std::is_move_constructible_v<E>>;
using this_type = Expected<void, E>;
public:
using value_type = void;
using error_type = E;
using unexpected_type = Unexpected<E>;
template <class U>
struct rebind
{
using type = Expected<U, error_type>;
};
constexpr Expected() = default;
constexpr Expected(const Expected& other) = default;
constexpr Expected(Expected&& other) = default;
template <class G>
Expected(const Expected<void, G>& other)
{
if (other.hasValue())
{
this->valid_ = true;
}
else
{
this->construct(unexpect, other.error());
}
}
template <class G>
Expected(Expected<void, G>&& other)
{
if (other.hasValue())
{
this->valid_ = true;
}
else
{
this->construct(unexpect, std::move(other.error()));
}
}
template <class G = E>
constexpr Expected(const Unexpected<G>& u)
: base_type(unexpect, u.value())
{
}
template <class G = E>
constexpr Expected(Unexpected<G>&& u)
: base_type(unexpect, std::move(u.value()))
{
}
template <class... Args>
constexpr explicit Expected(UnexpectType, Args&&... args)
: base_type(unexpect, std::forward<Args>(args)...)
{
}
template <class U, class... Args>
constexpr explicit Expected(UnexpectType, std::initializer_list<U> ilist, Args&&... args)
: base_type(unexpect, ilist, std::forward<Args>(args)...)
{
}
Expected& operator=(const Expected& other)
{
if (other.hasValue())
{
assign(std::in_place);
}
else
{
assign(unexpect, other.error());
}
return *this;
}
Expected& operator=(Expected&& other)
{
if (other.hasValue())
{
assign(std::in_place);
}
else
{
assign(unexpect, std::move(other.error()));
}
return *this;
}
template <class G = E>
Expected& operator=(const Unexpected<G>& u)
{
assign(unexpect, u.value());
return *this;
}
template <class G = E>
Expected& operator=(Unexpected<G>&& u)
{
assign(unexpect, std::move(u.value()));
return *this;
}
void swap(Expected& other) noexcept(detail::is_nothrow_swappable<error_type>::value)
{
using std::swap;
if (hasValue())
{
if (!other.hasValue())
{
assign(unexpect, std::move(other.error()));
other.assign(std::in_place);
}
}
else
{
if (other.hasValue())
{
other.swap(*this);
}
else
{
swap(error(), other.error());
}
}
}
constexpr explicit operator bool() const noexcept
{
return hasValue();
}
constexpr bool hasValue() const noexcept
{
return base_type::valid();
}
constexpr const E& error() const& noexcept
{
return base_type::error();
}
constexpr E& error() & noexcept
{
return base_type::error();
}
constexpr const E&& error() const&& noexcept
{
return std::move(base_type::error());
}
constexpr E&& error() && noexcept
{
return std::move(base_type::error());
}
private:
template <class Tag, class... Args>
void assign(Tag tag, Args&&... args) noexcept(noexcept(std::declval<this_type>().destroy()) && noexcept(std::declval<this_type>().construct(tag, std::forward<Args>(args)...)))
{
this->destroy();
this->construct(tag, std::forward<Args>(args)...);
}
};
template <class T, class E>
constexpr bool operator==(const Expected<T, E>& lhs, const Expected<T, E>& rhs)
{
return (lhs && rhs) ? *lhs == *rhs : ((!lhs && !rhs) ? lhs.error() == rhs.error() : false);
}
template <class E>
constexpr bool operator==(const Expected<void, E>& lhs, const Expected<void, E>& rhs)
{
return (lhs && rhs) ? true : ((!lhs && !rhs) ? lhs.error() == rhs.error() : false);
}
template <class T, class E>
constexpr bool operator!=(const Expected<T, E>& lhs, const Expected<T, E>& rhs)
{
return !(lhs == rhs);
}
template <class T, class E>
constexpr bool operator==(const Expected<T, E>& lhs, const T& rhs)
{
return lhs ? *lhs == rhs : false;
}
template <class T, class E>
constexpr bool operator==(const T& lhs, const Expected<T, E>& rhs)
{
return rhs == lhs;
}
template <class T, class E>
constexpr bool operator!=(const Expected<T, E>& lhs, const T& rhs)
{
return !(lhs == rhs);
}
template <class T, class E>
constexpr bool operator!=(const T& lhs, const Expected<T, E>& rhs)
{
return rhs != lhs;
}
template <class T, class E>
constexpr bool operator==(const Expected<T, E>& lhs, const Unexpected<E>& rhs)
{
return lhs ? false : lhs.error() == rhs.value();
}
template <class T, class E>
constexpr bool operator==(const Unexpected<E>& lhs, const Expected<T, E>& rhs)
{
return rhs == lhs;
}
template <class T, class E>
constexpr bool operator!=(const Expected<T, E>& lhs, const Unexpected<E>& rhs)
{
return !(lhs == rhs);
}
template <class T, class E>
constexpr bool operator!=(const Unexpected<E>& lhs, const Expected<T, E>& rhs)
{
return rhs != lhs;
}
}
// End File: Source/LuaBridge/detail/Expected.h
// Begin File: Source/LuaBridge/detail/Result.h
namespace luabridge {
struct Result
{
Result() = default;
Result(std::error_code ec) noexcept
: m_ec(ec)
{
}
Result(const Result&) = default;
Result(Result&&) = default;
Result& operator=(const Result&) = default;
Result& operator=(Result&&) = default;
explicit operator bool() const noexcept
{
return !m_ec;
}
std::error_code error() const noexcept
{
return m_ec;
}
operator std::error_code() const noexcept
{
return m_ec;
}
std::string message() const
{
return m_ec.message();
}
private:
std::error_code m_ec;
};
template <class T>
struct TypeResult
{
TypeResult() = default;
template <class U, class = std::enable_if_t<std::is_convertible_v<U, T> && !std::is_same_v<std::decay_t<U>, std::error_code>>>
TypeResult(U&& value) noexcept
: m_value(std::in_place, std::forward<U>(value))
{
}
TypeResult(std::error_code ec) noexcept
: m_value(makeUnexpected(ec))
{
}
TypeResult(const TypeResult&) = default;
TypeResult(TypeResult&&) = default;
TypeResult& operator=(const TypeResult&) = default;
TypeResult& operator=(TypeResult&&) = default;
explicit operator bool() const
{
return m_value.hasValue();
}
const T& value() const
{
return m_value.value();
}
T& operator*() &
{
return m_value.value();
}
T operator*() &&
{
return std::move(m_value.value());
}
const T& operator*() const&
{
return m_value.value();
}
std::error_code error() const
{
return m_value.error();
}
operator std::error_code() const
{
return m_value.error();
}
std::string message() const
{
return m_value.error().message();
}
private:
Expected<T, std::error_code> m_value;
};
template <class U>
inline bool operator==(const TypeResult<U>& lhs, const U& rhs) noexcept
{
return lhs ? *lhs == rhs : false;
}
template <class U>
inline bool operator==(const U& lhs, const TypeResult<U>& rhs) noexcept
{
return rhs == lhs;
}
template <class U>
inline bool operator!=(const TypeResult<U>& lhs, const U& rhs) noexcept
{
return !(lhs == rhs);
}
template <class U>
inline bool operator!=(const U& lhs, const TypeResult<U>& rhs) noexcept
{
return !(rhs == lhs);
}
}
// End File: Source/LuaBridge/detail/Result.h
// Begin File: Source/LuaBridge/detail/LuaException.h
namespace luabridge {
class LuaException : public std::exception
{
public:
LuaException(lua_State* L, std::error_code code)
: m_L(L)
, m_code(code)
{
}
~LuaException() noexcept override
{
}
const char* what() const noexcept override
{
return m_what.c_str();
}
static void raise(lua_State* L, std::error_code code)
{
assert(areExceptionsEnabled());
#if LUABRIDGE_HAS_EXCEPTIONS
throw LuaException(L, code, FromLua{});
#else
unused(L, code);
std::abort();
#endif
}
static bool areExceptionsEnabled() noexcept
{
return exceptionsEnabled();
}
static void enableExceptions(lua_State* L) noexcept
{
exceptionsEnabled() = true;
#if LUABRIDGE_HAS_EXCEPTIONS && LUABRIDGE_ON_LUAJIT
lua_pushlightuserdata(L, (void*)luajitWrapperCallback);
luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON);
lua_pop(L, 1);
#endif
#if LUABRIDGE_ON_LUAU
auto callbacks = lua_callbacks(L);
callbacks->panic = +[](lua_State* L, int) { panicHandlerCallback(L); };
#else
lua_atpanic(L, panicHandlerCallback);
#endif
}
lua_State* state() const { return m_L; }
private:
struct FromLua {};
LuaException(lua_State* L, std::error_code code, FromLua)
: m_L(L)
, m_code(code)
{
whatFromStack();
}
void whatFromStack()
{
std::stringstream ss;
const char* errorText = nullptr;
if (lua_gettop(m_L) > 0)
{
errorText = lua_tostring(m_L, -1);
lua_pop(m_L, 1);
}
ss << (errorText ? errorText : "Unknown error") << " (code=" << m_code.message() << ")";
m_what = std::move(ss).str();
}
static int panicHandlerCallback(lua_State* L)
{
#if LUABRIDGE_HAS_EXCEPTIONS
throw LuaException(L, makeErrorCode(ErrorCode::LuaFunctionCallFailed), FromLua{});
#else
unused(L);
std::abort();
#endif
}
#if LUABRIDGE_HAS_EXCEPTIONS && LUABRIDGE_ON_LUAJIT
static int luajitWrapperCallback(lua_State* L, lua_CFunction f)
{
try
{
return f(L);
}
catch (const std::exception& e)
{
lua_pushstring(L, e.what());
return lua_error_x(L);
}
}
#endif
static bool& exceptionsEnabled()
{
static bool areExceptionsEnabled = false;
return areExceptionsEnabled;
}
lua_State* m_L = nullptr;
std::error_code m_code;
std::string m_what;
};
inline void enableExceptions(lua_State* L) noexcept
{
#if LUABRIDGE_HAS_EXCEPTIONS
LuaException::enableExceptions(L);
#else
unused(L);
assert(false);
#endif
}
}
// End File: Source/LuaBridge/detail/LuaException.h
// Begin File: Source/LuaBridge/detail/ClassInfo.h
#if defined __clang__ || defined __GNUC__
#define LUABRIDGE_PRETTY_FUNCTION __PRETTY_FUNCTION__
#define LUABRIDGE_PRETTY_FUNCTION_PREFIX '='
#define LUABRIDGE_PRETTY_FUNCTION_SUFFIX ']'
#elif defined _MSC_VER
#define LUABRIDGE_PRETTY_FUNCTION __FUNCSIG__
#define LUABRIDGE_PRETTY_FUNCTION_PREFIX '<'
#define LUABRIDGE_PRETTY_FUNCTION_SUFFIX '>'
#endif
namespace luabridge {
namespace detail {
[[nodiscard]] constexpr auto fnv1a(const char* s, std::size_t count) noexcept
{
uint32_t seed = 2166136261u;
for (std::size_t i = 0; i < count; ++i)
seed ^= static_cast<uint32_t>(*s++) * 16777619u;
if constexpr (sizeof(void*) == 8)
return static_cast<uint64_t>(seed);
else
return seed;
}
template <class T>
[[nodiscard]] static constexpr auto typeName() noexcept
{
constexpr std::string_view prettyName{ LUABRIDGE_PRETTY_FUNCTION };
constexpr auto first = prettyName.find_first_not_of(' ', prettyName.find_first_of(LUABRIDGE_PRETTY_FUNCTION_PREFIX) + 1);
return prettyName.substr(first, prettyName.find_last_of(LUABRIDGE_PRETTY_FUNCTION_SUFFIX) - first);
}
template <class T, auto = typeName<T>().find_first_of('.')>
[[nodiscard]] static constexpr auto typeHash() noexcept
{
constexpr auto stripped = typeName<T>();
return fnv1a(stripped.data(), stripped.size());
}
[[nodiscard]] inline const void* getTypeKey() noexcept
{
return reinterpret_cast<void*>(0x71);
}
[[nodiscard]] inline const void* getConstKey() noexcept
{
return reinterpret_cast<void*>(0xc07);
}
[[nodiscard]] inline const void* getClassKey() noexcept
{
return reinterpret_cast<void*>(0xc1a);
}
[[nodiscard]] inline const void* getPropgetKey() noexcept
{
return reinterpret_cast<void*>(0x6e7);
}
[[nodiscard]] inline const void* getPropsetKey() noexcept
{
return reinterpret_cast<void*>(0x5e7);
}
[[nodiscard]] inline const void* getStaticKey() noexcept
{
return reinterpret_cast<void*>(0x57a);
}
[[nodiscard]] inline const void* getParentKey() noexcept
{
return reinterpret_cast<void*>(0xdad);
}
[[nodiscard]] inline const void* getIndexFallbackKey()
{
return reinterpret_cast<void*>(0x81ca);
}
[[nodiscard]] inline const void* getNewIndexFallbackKey()
{
return reinterpret_cast<void*>(0x8107);
}
template <class T>
[[nodiscard]] const void* getStaticRegistryKey() noexcept
{
static auto value = typeHash<T>();
return reinterpret_cast<void*>(value);
}
template <class T>
[[nodiscard]] const void* getClassRegistryKey() noexcept
{
static auto value = typeHash<T>() ^ 1;
return reinterpret_cast<void*>(value);
}
template <class T>
[[nodiscard]] const void* getConstRegistryKey() noexcept
{
static auto value = typeHash<T>() ^ 2;
return reinterpret_cast<void*>(value);
}
}
}
// End File: Source/LuaBridge/detail/ClassInfo.h
// Begin File: Source/LuaBridge/detail/TypeTraits.h
namespace luabridge {
template <class T>
struct ContainerTraits
{
using IsNotContainer = bool;
using Type = T;
};
template <class T>
struct ContainerTraits<std::shared_ptr<T>>
{
static_assert(std::is_base_of_v<std::enable_shared_from_this<T>, T>);
using Type = T;
static std::shared_ptr<T> construct(T* t)
{
return t->shared_from_this();
}
static T* get(const std::shared_ptr<T>& c)
{
return c.get();
}
};
namespace detail {
template <class T>
class IsContainer
{
private:
typedef char yes[1];
typedef char no[2];
template <class C>
static constexpr no& test(typename C::IsNotContainer*);
template <class>
static constexpr yes& test(...);
public:
static constexpr bool value = sizeof(test<ContainerTraits<T>>(0)) == sizeof(yes);
};
}
}
// End File: Source/LuaBridge/detail/TypeTraits.h
// Begin File: Source/LuaBridge/detail/Userdata.h
namespace luabridge {
namespace detail {
class Userdata
{
private:
static Userdata* getExactClass(lua_State* L, int index, const void* classKey)
{
return (void)classKey, static_cast<Userdata*>(lua_touserdata(L, lua_absindex(L, index)));
}
static Userdata* getClass(lua_State* L,
int index,
const void* registryConstKey,
const void* registryClassKey,
bool canBeConst)
{
index = lua_absindex(L, index);
lua_getmetatable(L, index);
if (!lua_istable(L, -1))
{
lua_rawgetp(L, LUA_REGISTRYINDEX, registryClassKey);
return throwBadArg(L, index);
}
lua_rawgetp(L, -1, getConstKey());
assert(lua_istable(L, -1) || lua_isnil(L, -1));
bool isConst = lua_isnil(L, -1);
if (isConst && canBeConst)
{
lua_rawgetp(L, LUA_REGISTRYINDEX, registryConstKey);
}
else
{
lua_rawgetp(L, LUA_REGISTRYINDEX, registryClassKey);
}
lua_insert(L, -3);
lua_pop(L, 1);
for (;;)
{
if (lua_rawequal(L, -1, -2))
{
lua_pop(L, 2);
return static_cast<Userdata*>(lua_touserdata(L, index));
}
lua_rawgetp(L, -1, getParentKey());
if (lua_isnil(L, -1))
{
lua_pop(L, 2);
return throwBadArg(L, index);
}
lua_remove(L, -2);
}
}
static bool isInstance(lua_State* L, int index, const void* registryClassKey)
{
index = lua_absindex(L, index);
int result = lua_getmetatable(L, index);
if (result == 0)
return false;
if (!lua_istable(L, -1))
{
lua_pop(L, 1);
return false;
}
lua_rawgetp(L, LUA_REGISTRYINDEX, registryClassKey);
lua_insert(L, -2);
for (;;)
{
if (lua_rawequal(L, -1, -2))
{
lua_pop(L, 2);
return true;
}
lua_rawgetp(L, -1, getParentKey());
if (lua_isnil(L, -1))
{
lua_pop(L, 3);
return false;
}
lua_remove(L, -2);
}
}
static Userdata* throwBadArg(lua_State* L, int index)
{
assert(lua_istable(L, -1) || lua_isnil(L, -1));
const char* expected = 0;
if (lua_isnil(L, -1))
{
expected = "unregistered class";
}
else
{
lua_rawgetp(L, -1, getTypeKey());
expected = lua_tostring(L, -1);
}
const char* got = 0;
if (lua_isuserdata(L, index))
{
lua_getmetatable(L, index);
if (lua_istable(L, -1))
{
lua_rawgetp(L, -1, getTypeKey());
if (lua_isstring(L, -1))
{
got = lua_tostring(L, -1);
}
}
}
if (!got)
{
got = lua_typename(L, lua_type(L, index));
}
luaL_argerror(L, index, lua_pushfstring(L, "%s expected, got %s", expected, got));
return nullptr;
}
public:
virtual ~Userdata() {}
template <class T>
static Userdata* getExact(lua_State* L, int index)
{
return getExactClass(L, index, detail::getClassRegistryKey<T>());
}
template <class T>
static T* get(lua_State* L, int index, bool canBeConst)
{
if (lua_isnil(L, index))
return nullptr;
return static_cast<T*>(getClass(L,
index,
detail::getConstRegistryKey<T>(),
detail::getClassRegistryKey<T>(),
canBeConst)
->getPointer());
}
template <class T>
static bool isInstance(lua_State* L, int index)
{
return isInstance(L, index, detail::getClassRegistryKey<T>());
}
protected:
Userdata() = default;
void* getPointer() const noexcept
{
return m_p;
}
void* m_p = nullptr;
};
template <class T>
class UserdataValue : public Userdata
{
public:
UserdataValue(const UserdataValue&) = delete;
UserdataValue operator=(const UserdataValue&) = delete;
~UserdataValue()
{
if (getPointer() != nullptr)
{
getObject()->~T();
}
}
static UserdataValue<T>* place(lua_State* L, std::error_code& ec)
{
auto* ud = new (lua_newuserdata_x<UserdataValue<T>>(L, sizeof(UserdataValue<T>))) UserdataValue<T>();
lua_rawgetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey<T>());
if (!lua_istable(L, -1))
{
lua_pop(L, 1);
ud->~UserdataValue<T>();
#if LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE
ec = throw_or_error_code<LuaException>(L, ErrorCode::ClassNotRegistered);
#else
ec = makeErrorCode(ErrorCode::ClassNotRegistered);
#endif
return nullptr;
}
lua_setmetatable(L, -2);
return ud;
}
template <class U>
static auto push(lua_State* L, const U& u) -> std::enable_if_t<std::is_copy_constructible_v<U>, Result>
{
std::error_code ec;
auto* ud = place(L, ec);
if (!ud)
return ec;
new (ud->getObject()) U(u);
ud->commit();
return {};
}
template <class U>
static auto push(lua_State* L, U&& u) -> std::enable_if_t<std::is_move_constructible_v<U>, Result>
{
std::error_code ec;
auto* ud = place(L, ec);
if (!ud)
return ec;
new (ud->getObject()) U(std::move(u));
ud->commit();
return {};
}
void commit() noexcept
{
m_p = getObject();
}
T* getObject() noexcept
{
return reinterpret_cast<T*>(&m_storage);
}
private:
UserdataValue() noexcept
: Userdata()
{
}
std::aligned_storage_t<sizeof(T), alignof(T)> m_storage;
};
class UserdataPtr : public Userdata
{
public:
UserdataPtr(const UserdataPtr&) = delete;
UserdataPtr operator=(const UserdataPtr&) = delete;
template <class T>
static Result push(lua_State* L, T* ptr)
{
if (ptr)
return push(L, ptr, getClassRegistryKey<T>());
lua_pushnil(L);
return {};
}
template <class T>
static Result push(lua_State* L, const T* ptr)
{
if (ptr)
return push(L, ptr, getConstRegistryKey<T>());
lua_pushnil(L);
return {};
}
private:
static Result push(lua_State* L, const void* ptr, const void* key)
{
auto* udptr = new (lua_newuserdata_x<UserdataPtr>(L, sizeof(UserdataPtr))) UserdataPtr(const_cast<void*>(ptr));
lua_rawgetp(L, LUA_REGISTRYINDEX, key);
if (!lua_istable(L, -1))
{
lua_pop(L, 1);
udptr->~UserdataPtr();
#if LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE
return throw_or_error_code<LuaException>(L, ErrorCode::ClassNotRegistered);
#else
return makeErrorCode(ErrorCode::ClassNotRegistered);
#endif
}
lua_setmetatable(L, -2);
return {};
}
explicit UserdataPtr(void* ptr)
{
assert(ptr != nullptr);
m_p = ptr;
}
};
template <class T>
class UserdataValueExternal : public Userdata
{
public:
UserdataValueExternal(const UserdataValueExternal&) = delete;
UserdataValueExternal operator=(const UserdataValueExternal&) = delete;
~UserdataValueExternal()
{
if (getObject() != nullptr)
m_dealloc(getObject());
}
template <class Dealloc>
static UserdataValueExternal<T>* place(lua_State* L, T* obj, Dealloc dealloc, std::error_code& ec)
{
auto* ud = new (lua_newuserdata_x<UserdataValueExternal<T>>(L, sizeof(UserdataValueExternal<T>))) UserdataValueExternal<T>(obj, dealloc);
lua_rawgetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey<T>());
if (!lua_istable(L, -1))
{
lua_pop(L, 1);
ud->~UserdataValueExternal<T>();
#if LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE
ec = throw_or_error_code<LuaException>(L, ErrorCode::ClassNotRegistered);
#else
ec = makeErrorCode(ErrorCode::ClassNotRegistered);
#endif
return nullptr;
}
lua_setmetatable(L, -2);
return ud;
}
T* getObject() noexcept
{
return static_cast<T*>(m_p);
}
private:
UserdataValueExternal(void* ptr, void (*dealloc)(T*)) noexcept
{
assert(ptr != nullptr);
m_p = ptr;
assert(dealloc != nullptr);
m_dealloc = dealloc;
}
void (*m_dealloc)(T*) = nullptr;
};
template <class C>
class UserdataShared : public Userdata
{
public:
UserdataShared(const UserdataShared&) = delete;
UserdataShared& operator=(const UserdataShared&) = delete;
~UserdataShared() = default;
template <class U>
explicit UserdataShared(const U& u) : m_c(u)
{
m_p = const_cast<void*>(reinterpret_cast<const void*>((ContainerTraits<C>::get(m_c))));
}
template <class U>
explicit UserdataShared(U* u) : m_c(u)
{
m_p = const_cast<void*>(reinterpret_cast<const void*>((ContainerTraits<C>::get(m_c))));
}
private:
C m_c;
};
template <class C, bool MakeObjectConst>
struct UserdataSharedHelper
{
using T = std::remove_const_t<typename ContainerTraits<C>::Type>;
static Result push(lua_State* L, const C& c)
{
if (ContainerTraits<C>::get(c) != nullptr)
{
auto* us = new (lua_newuserdata_x<UserdataShared<C>>(L, sizeof(UserdataShared<C>))) UserdataShared<C>(c);
lua_rawgetp(L, LUA_REGISTRYINDEX, getClassRegistryKey<T>());
if (!lua_istable(L, -1))
{
lua_pop(L, 1);
us->~UserdataShared<C>();
#if LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE
return throw_or_error_code<LuaException>(L, ErrorCode::ClassNotRegistered);
#else
return makeErrorCode(ErrorCode::ClassNotRegistered);
#endif
}
lua_setmetatable(L, -2);
}
else
{
lua_pushnil(L);
}
return {};
}
static Result push(lua_State* L, T* t)
{
if (t)
{
auto* us = new (lua_newuserdata_x<UserdataShared<C>>(L, sizeof(UserdataShared<C>))) UserdataShared<C>(t);
lua_rawgetp(L, LUA_REGISTRYINDEX, getClassRegistryKey<T>());
if (!lua_istable(L, -1))
{
lua_pop(L, 1);
us->~UserdataShared<C>();
#if LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE
return throw_or_error_code<LuaException>(L, ErrorCode::ClassNotRegistered);
#else
return makeErrorCode(ErrorCode::ClassNotRegistered);
#endif
}
lua_setmetatable(L, -2);
}
else
{
lua_pushnil(L);
}
return {};
}
};
template <class C>
struct UserdataSharedHelper<C, true>
{
using T = std::remove_const_t<typename ContainerTraits<C>::Type>;
static Result push(lua_State* L, const C& c)
{
if (ContainerTraits<C>::get(c) != nullptr)
{
auto* us = new (lua_newuserdata_x<UserdataShared<C>>(L, sizeof(UserdataShared<C>))) UserdataShared<C>(c);
lua_rawgetp(L, LUA_REGISTRYINDEX, getConstRegistryKey<T>());
if (!lua_istable(L, -1))
{
lua_pop(L, 1);
us->~UserdataShared<C>();
#if LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE
return throw_or_error_code<LuaException>(L, ErrorCode::ClassNotRegistered);
#else
return makeErrorCode(ErrorCode::ClassNotRegistered);
#endif
}
lua_setmetatable(L, -2);
}
else
{
lua_pushnil(L);
}
return {};
}
static Result push(lua_State* L, T* t)
{
if (t)
{
auto* us = new (lua_newuserdata_x<UserdataShared<C>>(L, sizeof(UserdataShared<C>))) UserdataShared<C>(t);
lua_rawgetp(L, LUA_REGISTRYINDEX, getConstRegistryKey<T>());
if (!lua_istable(L, -1))
{
lua_pop(L, 1);
us->~UserdataShared<C>();
#if LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE
return throw_or_error_code<LuaException>(L, ErrorCode::ClassNotRegistered);
#else
return makeErrorCode(ErrorCode::ClassNotRegistered);
#endif
}
lua_setmetatable(L, -2);
}
else
{
lua_pushnil(L);
}
return {};
}
};
template <class T, bool ByContainer>
struct StackHelper
{
using ReturnType = TypeResult<T>;
static Result push(lua_State* L, const T& t)
{
return UserdataSharedHelper<T, std::is_const_v<typename ContainerTraits<T>::Type>>::push(L, t);
}
static ReturnType get(lua_State* L, int index)
{
using CastType = std::remove_const_t<typename ContainerTraits<T>::Type>;
auto* result = Userdata::get<CastType>(L, index, true);
if (! result)
return makeErrorCode(ErrorCode::InvalidTypeCast);
return ContainerTraits<T>::construct(result);
}
};
template <class T>
struct StackHelper<T, false>
{
static Result push(lua_State* L, const T& t)
{
return UserdataValue<T>::push(L, t);
}
static Result push(lua_State* L, T&& t)
{
return UserdataValue<T>::push(L, std::move(t));
}
static TypeResult<std::reference_wrapper<const T>> get(lua_State* L, int index)
{
auto* result = Userdata::get<T>(L, index, true);
if (! result)
return makeErrorCode(ErrorCode::InvalidTypeCast);
return std::cref(*result);
}
};
template <class C, bool ByContainer>
struct RefStackHelper
{
using ReturnType = TypeResult<C>;
using T = std::remove_const_t<typename ContainerTraits<C>::Type>;
static Result push(lua_State* L, const C& t)
{
return UserdataSharedHelper<C, std::is_const_v<typename ContainerTraits<C>::Type>>::push(L, t);
}
static ReturnType get(lua_State* L, int index)
{
auto* result = Userdata::get<T>(L, index, true);
if (! result)
return makeErrorCode(ErrorCode::InvalidTypeCast);
return ContainerTraits<C>::construct(result);
}
};
template <class T>
struct RefStackHelper<T, false>
{
using ReturnType = TypeResult<std::reference_wrapper<T>>;
static Result push(lua_State* L, const T& t)
{
return UserdataPtr::push(L, std::addressof(t));
}
static ReturnType get(lua_State* L, int index)
{
auto* result = Userdata::get<T>(L, index, true);
if (! result)
return makeErrorCode(ErrorCode::InvalidTypeCast);
return std::ref(*result);
}
};
template <class T, class Enable = void>
struct UserdataGetter
{
using ReturnType = TypeResult<T*>;
static ReturnType get(lua_State* L, int index)
{
auto* result = Userdata::get<T>(L, index, true);
if (! result)
return makeErrorCode(ErrorCode::InvalidTypeCast);
return result;
}
};
template <class T>
struct UserdataGetter<T, std::void_t<T (*)()>>
{
using ReturnType = TypeResult<T>;
static ReturnType get(lua_State* L, int index)
{
auto result = StackHelper<T, IsContainer<T>::value>::get(L, index);
if (! result)
return result.error();
return *result;
}
};
}
template <class T, class = void>
struct Stack
{
using IsUserdata = void;
using Getter = detail::UserdataGetter<T>;
using ReturnType = typename Getter::ReturnType;
[[nodiscard]] static Result push(lua_State* L, const T& value)
{
return detail::StackHelper<T, detail::IsContainer<T>::value>::push(L, value);
}
[[nodiscard]] static Result push(lua_State* L, T&& value)
{
return detail::StackHelper<T, detail::IsContainer<T>::value>::push(L, std::move(value));
}
[[nodiscard]] static ReturnType get(lua_State* L, int index)
{
return Getter::get(L, index);
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return detail::Userdata::isInstance<T>(L, index);
}
};
namespace detail {
template <class T, class Enable = void>
struct IsUserdata : std::false_type
{
};
template <class T>
struct IsUserdata<T, std::void_t<typename Stack<T>::IsUserdata>> : std::true_type
{
};
template <class T, bool IsUserdata>
struct StackOpSelector;
template <class T>
struct StackOpSelector<T*, true>
{
using ReturnType = TypeResult<T*>;
static Result push(lua_State* L, T* value) { return UserdataPtr::push(L, value); }
static ReturnType get(lua_State* L, int index) { return Userdata::get<T>(L, index, false); }
static bool isInstance(lua_State* L, int index) { return Userdata::isInstance<T>(L, index); }
};
template <class T>
struct StackOpSelector<const T*, true>
{
using ReturnType = TypeResult<const T*>;
static Result push(lua_State* L, const T* value) { return UserdataPtr::push(L, value); }
static ReturnType get(lua_State* L, int index) { return Userdata::get<T>(L, index, true); }
static bool isInstance(lua_State* L, int index) { return Userdata::isInstance<T>(L, index); }
};
template <class T>
struct StackOpSelector<T&, true>
{
using Helper = RefStackHelper<T, IsContainer<T>::value>;
using ReturnType = typename Helper::ReturnType;
static Result push(lua_State* L, T& value) { return UserdataPtr::push(L, &value); }
static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); }
static bool isInstance(lua_State* L, int index) { return Userdata::isInstance<T>(L, index); }
};
template <class T>
struct StackOpSelector<const T&, true>
{
using Helper = RefStackHelper<T, IsContainer<T>::value>;
using ReturnType = typename Helper::ReturnType;
static Result push(lua_State* L, const T& value) { return Helper::push(L, value); }
static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); }
static bool isInstance(lua_State* L, int index) { return Userdata::isInstance<T>(L, index); }
};
}
}
// End File: Source/LuaBridge/detail/Userdata.h
// Begin File: Source/LuaBridge/detail/Stack.h
namespace luabridge {
class StackRestore final
{
public:
StackRestore(lua_State* L)
: m_L(L)
, m_stackTop(lua_gettop(L))
{
}
~StackRestore()
{
if (m_doRestoreStack)
lua_settop(m_L, m_stackTop);
}
void reset()
{
m_doRestoreStack = false;
}
private:
lua_State* const m_L = nullptr;
int m_stackTop = 0;
bool m_doRestoreStack = true;
};
template <class T, class>
struct Stack;
template <>
struct Stack<void>
{
[[nodiscard]] static Result push(lua_State*)
{
return {};
}
};
template <>
struct Stack<std::nullptr_t>
{
[[nodiscard]] static Result push(lua_State* L, std::nullptr_t)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushnil(L);
return {};
}
[[nodiscard]] static TypeResult<std::nullptr_t> get(lua_State* L, int index)
{
if (! lua_isnil(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
return nullptr;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_isnil(L, index);
}
};
template <>
struct Stack<lua_State*>
{
[[nodiscard]] static TypeResult<lua_State*> get(lua_State* L, int)
{
return L;
}
};
template <>
struct Stack<lua_CFunction>
{
[[nodiscard]] static Result push(lua_State* L, lua_CFunction f)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushcfunction_x(L, f);
return {};
}
[[nodiscard]] static TypeResult<lua_CFunction> get(lua_State* L, int index)
{
if (! lua_iscfunction(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
return lua_tocfunction(L, index);
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_iscfunction(L, index);
}
};
template <>
struct Stack<bool>
{
[[nodiscard]] static Result push(lua_State* L, bool value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushboolean(L, value ? 1 : 0);
return {};
}
[[nodiscard]] static TypeResult<bool> get(lua_State* L, int index)
{
return lua_toboolean(L, index) ? true : false;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_isboolean(L, index);
}
};
template <>
struct Stack<std::byte>
{
static_assert(sizeof(std::byte) < sizeof(lua_Integer));
[[nodiscard]] static Result push(lua_State* L, std::byte value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
pushunsigned(L, std::to_integer<std::make_unsigned_t<lua_Integer>>(value));
return {};
}
[[nodiscard]] static TypeResult<std::byte> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_integral_representable_by<unsigned char>(L, index))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
return static_cast<std::byte>(lua_tointeger(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_integral_representable_by<unsigned char>(L, index);
return false;
}
};
template <>
struct Stack<char>
{
[[nodiscard]] static Result push(lua_State* L, char value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushlstring(L, &value, 1);
return {};
}
[[nodiscard]] static TypeResult<char> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TSTRING)
return makeErrorCode(ErrorCode::InvalidTypeCast);
std::size_t length = 0;
const char* str = lua_tolstring(L, index, &length);
if (str == nullptr || length != 1)
return makeErrorCode(ErrorCode::InvalidTypeCast);
return str[0];
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TSTRING)
{
std::size_t len;
luaL_checklstring(L, index, &len);
return len == 1;
}
return false;
}
};
template <>
struct Stack<int8_t>
{
static_assert(sizeof(int8_t) < sizeof(lua_Integer));
[[nodiscard]] static Result push(lua_State* L, int8_t value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushinteger(L, static_cast<lua_Integer>(value));
return {};
}
[[nodiscard]] static TypeResult<int8_t> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_integral_representable_by<int8_t>(L, index))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
return static_cast<int8_t>(lua_tointeger(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_integral_representable_by<int8_t>(L, index);
return false;
}
};
template <>
struct Stack<unsigned char>
{
static_assert(sizeof(unsigned char) < sizeof(lua_Integer));
[[nodiscard]] static Result push(lua_State* L, unsigned char value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
pushunsigned(L, value);
return {};
}
[[nodiscard]] static TypeResult<unsigned char> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_integral_representable_by<unsigned char>(L, index))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
return static_cast<unsigned char>(lua_tointeger(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_integral_representable_by<unsigned char>(L, index);
return false;
}
};
template <>
struct Stack<short>
{
static_assert(sizeof(short) < sizeof(lua_Integer));
[[nodiscard]] static Result push(lua_State* L, short value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushinteger(L, static_cast<lua_Integer>(value));
return {};
}
[[nodiscard]] static TypeResult<short> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_integral_representable_by<short>(L, index))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
return static_cast<short>(lua_tointeger(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_integral_representable_by<short>(L, index);
return false;
}
};
template <>
struct Stack<unsigned short>
{
static_assert(sizeof(unsigned short) < sizeof(lua_Integer));
[[nodiscard]] static Result push(lua_State* L, unsigned short value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
pushunsigned(L, value);
return {};
}
[[nodiscard]] static TypeResult<unsigned short> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_integral_representable_by<unsigned short>(L, index))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
return static_cast<unsigned short>(lua_tointeger(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_integral_representable_by<unsigned short>(L, index);
return false;
}
};
template <>
struct Stack<int>
{
[[nodiscard]] static Result push(lua_State* L, int value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
if (! is_integral_representable_by(value))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
lua_pushinteger(L, static_cast<lua_Integer>(value));
return {};
}
[[nodiscard]] static TypeResult<int> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_integral_representable_by<int>(L, index))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
return static_cast<int>(lua_tointeger(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_integral_representable_by<int>(L, index);
return false;
}
};
template <>
struct Stack<unsigned int>
{
[[nodiscard]] static Result push(lua_State* L, unsigned int value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
if (! is_integral_representable_by(value))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
pushunsigned(L, value);
return {};
}
[[nodiscard]] static TypeResult<unsigned int> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_integral_representable_by<unsigned int>(L, index))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
return static_cast<unsigned int>(lua_tointeger(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_integral_representable_by<unsigned int>(L, index);
return false;
}
};
template <>
struct Stack<long>
{
[[nodiscard]] static Result push(lua_State* L, long value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
if (! is_integral_representable_by(value))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
lua_pushinteger(L, static_cast<lua_Integer>(value));
return {};
}
[[nodiscard]] static TypeResult<long> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_integral_representable_by<long>(L, index))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
return static_cast<long>(lua_tointeger(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_integral_representable_by<long>(L, index);
return false;
}
};
template <>
struct Stack<unsigned long>
{
[[nodiscard]] static Result push(lua_State* L, unsigned long value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
if (! is_integral_representable_by(value))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
pushunsigned(L, value);
return {};
}
[[nodiscard]] static TypeResult<unsigned long> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_integral_representable_by<unsigned long>(L, index))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
return static_cast<unsigned long>(lua_tointeger(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_integral_representable_by<unsigned long>(L, index);
return false;
}
};
template <>
struct Stack<long long>
{
[[nodiscard]] static Result push(lua_State* L, long long value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
if (! is_integral_representable_by(value))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
lua_pushinteger(L, static_cast<lua_Integer>(value));
return {};
}
[[nodiscard]] static TypeResult<long long> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_integral_representable_by<long long>(L, index))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
return static_cast<long long>(lua_tointeger(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_integral_representable_by<long long>(L, index);
return false;
}
};
template <>
struct Stack<unsigned long long>
{
[[nodiscard]] static Result push(lua_State* L, unsigned long long value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
if (! is_integral_representable_by(value))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
pushunsigned(L, value);
return {};
}
[[nodiscard]] static TypeResult<unsigned long long> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_integral_representable_by<unsigned long long>(L, index))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
return static_cast<unsigned long long>(lua_tointeger(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_integral_representable_by<unsigned long long>(L, index);
return false;
}
};
#if 0
template <>
struct Stack<__int128_t>
{
[[nodiscard]] static Result push(lua_State* L, __int128_t value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
if (! is_integral_representable_by(value))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
lua_pushinteger(L, static_cast<lua_Integer>(value));
return {};
}
[[nodiscard]] static TypeResult<__int128_t> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_integral_representable_by<__int128_t>(L, index))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
return static_cast<__int128_t>(lua_tointeger(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_integral_representable_by<__int128_t>(L, index);
return false;
}
};
template <>
struct Stack<__uint128_t>
{
[[nodiscard]] static Result push(lua_State* L, __uint128_t value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
if (! is_integral_representable_by(value))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
lua_pushinteger(L, static_cast<lua_Integer>(value));
return {};
}
[[nodiscard]] static TypeResult<__uint128_t> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_integral_representable_by<__uint128_t>(L, index))
return makeErrorCode(ErrorCode::IntegerDoesntFitIntoLuaInteger);
return static_cast<__uint128_t>(lua_tointeger(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_integral_representable_by<__uint128_t>(L, index);
return false;
}
};
#endif
template <>
struct Stack<float>
{
[[nodiscard]] static Result push(lua_State* L, float value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
if (! is_floating_point_representable_by(value))
return makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber);
lua_pushnumber(L, static_cast<lua_Number>(value));
return {};
}
[[nodiscard]] static TypeResult<float> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_floating_point_representable_by<float>(L, index))
return makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber);
return static_cast<float>(lua_tonumber(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_floating_point_representable_by<float>(L, index);
return false;
}
};
template <>
struct Stack<double>
{
[[nodiscard]] static Result push(lua_State* L, double value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
if (! is_floating_point_representable_by(value))
return makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber);
lua_pushnumber(L, static_cast<lua_Number>(value));
return {};
}
[[nodiscard]] static TypeResult<double> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_floating_point_representable_by<double>(L, index))
return makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber);
return static_cast<double>(lua_tonumber(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_floating_point_representable_by<double>(L, index);
return false;
}
};
template <>
struct Stack<long double>
{
[[nodiscard]] static Result push(lua_State* L, long double value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
if (! is_floating_point_representable_by(value))
return makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber);
lua_pushnumber(L, static_cast<lua_Number>(value));
return {};
}
[[nodiscard]] static TypeResult<long double> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TNUMBER)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! is_floating_point_representable_by<long double>(L, index))
return makeErrorCode(ErrorCode::FloatingPointDoesntFitIntoLuaNumber);
return static_cast<long double>(lua_tonumber(L, index));
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNUMBER)
return is_floating_point_representable_by<long double>(L, index);
return false;
}
};
template <>
struct Stack<const char*>
{
[[nodiscard]] static Result push(lua_State* L, const char* str)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
if (str != nullptr)
lua_pushstring(L, str);
else
lua_pushlstring(L, "", 0);
return {};
}
[[nodiscard]] static TypeResult<const char*> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TSTRING)
return makeErrorCode(ErrorCode::InvalidTypeCast);
std::size_t length = 0;
const char* str = lua_tolstring(L, index, &length);
if (str == nullptr)
return makeErrorCode(ErrorCode::InvalidTypeCast);
return str;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_type(L, index) == LUA_TSTRING;
}
};
template <>
struct Stack<std::string_view>
{
[[nodiscard]] static Result push(lua_State* L, std::string_view str)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushlstring(L, str.data(), str.size());
return {};
}
[[nodiscard]] static TypeResult<std::string_view> get(lua_State* L, int index)
{
if (lua_type(L, index) != LUA_TSTRING)
return makeErrorCode(ErrorCode::InvalidTypeCast);
std::size_t length = 0;
const char* str = lua_tolstring(L, index, &length);
if (str == nullptr)
return makeErrorCode(ErrorCode::InvalidTypeCast);
return std::string_view{ str, length };
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_type(L, index) == LUA_TSTRING;
}
};
template <>
struct Stack<std::string>
{
[[nodiscard]] static Result push(lua_State* L, const std::string& str)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushlstring(L, str.data(), str.size());
return {};
}
[[nodiscard]] static TypeResult<std::string> get(lua_State* L, int index)
{
std::size_t length = 0;
const char* str = nullptr;
if (lua_type(L, index) == LUA_TSTRING)
{
str = lua_tolstring(L, index, &length);
}
else
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushvalue(L, index);
str = lua_tolstring(L, -1, &length);
lua_pop(L, 1);
}
if (str == nullptr)
return makeErrorCode(ErrorCode::InvalidTypeCast);
return std::string{ str, length };
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_type(L, index) == LUA_TSTRING;
}
};
template <class T>
struct Stack<std::optional<T>>
{
using Type = std::optional<T>;
[[nodiscard]] static Result push(lua_State* L, const Type& value)
{
if (value)
{
StackRestore stackRestore(L);
auto result = Stack<T>::push(L, *value);
if (! result)
return result;
stackRestore.reset();
return {};
}
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushnil(L);
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TNIL)
return std::nullopt;
auto result = Stack<T>::get(L, index);
if (! result)
return result.error();
return *result;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_isnil(L, index) || Stack<T>::isInstance(L, index);
}
};
template <class T1, class T2>
struct Stack<std::pair<T1, T2>>
{
[[nodiscard]] static Result push(lua_State* L, const std::pair<T1, T2>& t)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, 2, 0);
auto result1 = push_element<0>(L, t);
if (! result1)
return result1;
auto result2 = push_element<1>(L, t);
if (! result2)
return result2;
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<std::pair<T1, T2>> get(lua_State* L, int index)
{
const StackRestore stackRestore(L);
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (get_length(L, index) != 2)
return makeErrorCode(ErrorCode::InvalidTableSizeInCast);
std::pair<T1, T2> value;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
auto result1 = pop_element<0>(L, absIndex, value);
if (! result1)
return result1.error();
auto result2 = pop_element<1>(L, absIndex, value);
if (! result2)
return result2.error();
return value;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_type(L, index) == LUA_TTABLE && get_length(L, index) == 2;
}
private:
template <std::size_t Index>
static Result push_element(lua_State* L, const std::pair<T1, T2>& p)
{
static_assert(Index < 2);
using T = std::tuple_element_t<Index, std::pair<T1, T2>>;
lua_pushinteger(L, static_cast<lua_Integer>(Index + 1));
auto result = Stack<T>::push(L, std::get<Index>(p));
if (! result)
{
lua_pushnil(L);
lua_settable(L, -3);
return result;
}
lua_settable(L, -3);
return {};
}
template <std::size_t Index>
static Result pop_element(lua_State* L, int absIndex, std::pair<T1, T2>& p)
{
static_assert(Index < 2);
using T = std::tuple_element_t<Index, std::pair<T1, T2>>;
if (lua_next(L, absIndex) == 0)
return makeErrorCode(ErrorCode::InvalidTypeCast);
auto result = Stack<T>::get(L, -1);
if (! result)
return makeErrorCode(ErrorCode::InvalidTypeCast);
std::get<Index>(p) = std::move(*result);
lua_pop(L, 1);
return {};
}
};
template <class... Types>
struct Stack<std::tuple<Types...>>
{
[[nodiscard]] static Result push(lua_State* L, const std::tuple<Types...>& t)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, static_cast<int>(Size), 0);
auto result = push_element(L, t);
if (! result)
return result;
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<std::tuple<Types...>> get(lua_State* L, int index)
{
const StackRestore stackRestore(L);
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (get_length(L, index) != static_cast<int>(Size))
return makeErrorCode(ErrorCode::InvalidTableSizeInCast);
std::tuple<Types...> value;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
auto result = pop_element(L, absIndex, value);
if (! result)
return result.error();
return value;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_type(L, index) == LUA_TTABLE && get_length(L, index) == static_cast<int>(Size);
}
private:
static constexpr std::size_t Size = std::tuple_size_v<std::tuple<Types...>>;
template <std::size_t Index = 0>
static auto push_element(lua_State*, const std::tuple<Types...>&)
-> std::enable_if_t<Index == sizeof...(Types), Result>
{
return {};
}
template <std::size_t Index = 0>
static auto push_element(lua_State* L, const std::tuple<Types...>& t)
-> std::enable_if_t<Index < sizeof...(Types), Result>
{
using T = std::tuple_element_t<Index, std::tuple<Types...>>;
lua_pushinteger(L, static_cast<lua_Integer>(Index + 1));
auto result = Stack<T>::push(L, std::get<Index>(t));
if (! result)
{
lua_pushnil(L);
lua_settable(L, -3);
return result;
}
lua_settable(L, -3);
return push_element<Index + 1>(L, t);
}
template <std::size_t Index = 0>
static auto pop_element(lua_State*, int, std::tuple<Types...>&)
-> std::enable_if_t<Index == sizeof...(Types), Result>
{
return {};
}
template <std::size_t Index = 0>
static auto pop_element(lua_State* L, int absIndex, std::tuple<Types...>& t)
-> std::enable_if_t<Index < sizeof...(Types), Result>
{
using T = std::tuple_element_t<Index, std::tuple<Types...>>;
if (lua_next(L, absIndex) == 0)
return makeErrorCode(ErrorCode::InvalidTypeCast);
auto result = Stack<T>::get(L, -1);
if (! result)
return makeErrorCode(ErrorCode::InvalidTypeCast);
std::get<Index>(t) = std::move(*result);
lua_pop(L, 1);
return pop_element<Index + 1>(L, absIndex, t);
}
};
template <class T, std::size_t N>
struct Stack<T[N]>
{
static_assert(N > 0, "Unsupported zero sized array");
[[nodiscard]] static Result push(lua_State* L, const T (&value)[N])
{
if constexpr (std::is_same_v<T, char>)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushlstring(L, value, N - 1);
return {};
}
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 2))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, static_cast<int>(N), 0);
for (std::size_t i = 0; i < N; ++i)
{
lua_pushinteger(L, static_cast<lua_Integer>(i + 1));
auto result = Stack<T>::push(L, value[i]);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_type(L, index) == LUA_TTABLE && get_length(L, index) == static_cast<int>(N);
}
};
namespace detail {
template <class T>
struct StackOpSelector<T&, false>
{
using ReturnType = TypeResult<T>;
static Result push(lua_State* L, T& value) { return Stack<T>::push(L, value); }
static ReturnType get(lua_State* L, int index) { return Stack<T>::get(L, index); }
static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
};
template <class T>
struct StackOpSelector<const T&, false>
{
using ReturnType = TypeResult<T>;
static Result push(lua_State* L, const T& value) { return Stack<T>::push(L, value); }
static ReturnType get(lua_State* L, int index) { return Stack<T>::get(L, index); }
static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
};
template <class T>
struct StackOpSelector<T*, false>
{
using ReturnType = TypeResult<T>;
static Result push(lua_State* L, T* value) { return Stack<T>::push(L, *value); }
static ReturnType get(lua_State* L, int index) { return Stack<T>::get(L, index); }
static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
};
template <class T>
struct StackOpSelector<const T*, false>
{
using ReturnType = TypeResult<T>;
static Result push(lua_State* L, const T* value) { return Stack<T>::push(L, *value); }
static ReturnType get(lua_State* L, int index) { return Stack<T>::get(L, index); }
static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
};
}
template <class T>
struct Stack<T&, std::enable_if_t<!std::is_array_v<T&>>>
{
using Helper = detail::StackOpSelector<T&, detail::IsUserdata<T>::value>;
using ReturnType = typename Helper::ReturnType;
[[nodiscard]] static Result push(lua_State* L, T& value) { return Helper::push(L, value); }
[[nodiscard]] static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); }
[[nodiscard]] static bool isInstance(lua_State* L, int index) { return Helper::template isInstance<T>(L, index); }
};
template <class T>
struct Stack<const T&, std::enable_if_t<!std::is_array_v<const T&>>>
{
using Helper = detail::StackOpSelector<const T&, detail::IsUserdata<T>::value>;
using ReturnType = typename Helper::ReturnType;
[[nodiscard]] static Result push(lua_State* L, const T& value) { return Helper::push(L, value); }
[[nodiscard]] static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); }
[[nodiscard]] static bool isInstance(lua_State* L, int index) { return Helper::template isInstance<T>(L, index); }
};
template <class T>
struct Stack<T*>
{
using Helper = detail::StackOpSelector<T*, detail::IsUserdata<T>::value>;
using ReturnType = typename Helper::ReturnType;
[[nodiscard]] static Result push(lua_State* L, T* value) { return Helper::push(L, value); }
[[nodiscard]] static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); }
[[nodiscard]] static bool isInstance(lua_State* L, int index) { return Helper::template isInstance<T>(L, index); }
};
template<class T>
struct Stack<const T*>
{
using Helper = detail::StackOpSelector<const T*, detail::IsUserdata<T>::value>;
using ReturnType = typename Helper::ReturnType;
[[nodiscard]] static Result push(lua_State* L, const T* value) { return Helper::push(L, value); }
[[nodiscard]] static ReturnType get(lua_State* L, int index) { return Helper::get(L, index); }
[[nodiscard]] static bool isInstance(lua_State* L, int index) { return Helper::template isInstance<T>(L, index); }
};
template <class T>
[[nodiscard]] Result push(lua_State* L, const T& t)
{
return Stack<T>::push(L, t);
}
template <class T>
[[nodiscard]] TypeResult<T> get(lua_State* L, int index)
{
return Stack<T>::get(L, index);
}
template <class T>
[[nodiscard]] bool isInstance(lua_State* L, int index)
{
return Stack<T>::isInstance(L, index);
}
}
// End File: Source/LuaBridge/detail/Stack.h
// Begin File: Source/LuaBridge/Array.h
namespace luabridge {
template <class T, std::size_t Size>
struct Stack<std::array<T, Size>>
{
using Type = std::array<T, Size>;
[[nodiscard]] static Result push(lua_State* L, const Type& array)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, static_cast<int>(Size), 0);
for (std::size_t i = 0; i < Size; ++i)
{
lua_pushinteger(L, static_cast<lua_Integer>(i + 1));
auto result = Stack<T>::push(L, array[i]);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (get_length(L, index) != Size)
return makeErrorCode(ErrorCode::InvalidTableSizeInCast);
const StackRestore stackRestore(L);
Type array;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
int arrayIndex = 0;
while (lua_next(L, absIndex) != 0)
{
auto item = Stack<T>::get(L, -1);
if (!item)
return makeErrorCode(ErrorCode::InvalidTypeCast);
array[arrayIndex++] = *item;
lua_pop(L, 1);
}
return array;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index) && get_length(L, index) == Size;
}
};
}
// End File: Source/LuaBridge/Array.h
// Begin File: Source/LuaBridge/List.h
namespace luabridge {
template <class T>
struct Stack<std::list<T>>
{
using Type = std::list<T>;
[[nodiscard]] static Result push(lua_State* L, const Type& list)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, static_cast<int>(list.size()), 0);
auto it = list.cbegin();
for (lua_Integer tableIndex = 1; it != list.cend(); ++tableIndex, ++it)
{
lua_pushinteger(L, tableIndex);
auto result = Stack<T>::push(L, *it);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type list;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto item = Stack<T>::get(L, -1);
if (! item)
return makeErrorCode(ErrorCode::InvalidTypeCast);
list.emplace_back(*item);
lua_pop(L, 1);
}
return list;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
}
// End File: Source/LuaBridge/List.h
// Begin File: Source/LuaBridge/detail/FuncTraits.h
namespace luabridge {
namespace detail {
[[noreturn]] inline void unreachable()
{
#if __GNUC__
__builtin_unreachable();
#elif _MSC_VER
__assume(false);
#endif
}
template< class T >
struct remove_cvref
{
typedef std::remove_cv_t<std::remove_reference_t<T>> type;
};
template <class T>
using remove_cvref_t = typename remove_cvref<T>::type;
template <bool IsMember, bool IsConst, class R, class... Args>
struct function_traits_base
{
using result_type = R;
using argument_types = std::tuple<Args...>;
static constexpr auto arity = sizeof...(Args);
static constexpr auto is_member = IsMember;
static constexpr auto is_const = IsConst;
};
template <class...>
struct function_traits_impl;
template <class R, class... Args>
struct function_traits_impl<R(Args...)> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (*)(Args...)> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (C::*)(Args...)> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (C::*)(Args...) const> : function_traits_base<true, true, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R(Args...) noexcept> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (*)(Args...) noexcept> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (C::*)(Args...) noexcept> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (C::*)(Args...) const noexcept> : function_traits_base<true, true, R, Args...>
{
};
#if _MSC_VER && _M_IX86
template <class R, class... Args>
struct function_traits_impl<R __stdcall(Args...)> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (__stdcall *)(Args...)> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__stdcall C::*)(Args...)> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__stdcall C::*)(Args...) const> : function_traits_base<true, true, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R __stdcall(Args...) noexcept> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (__stdcall *)(Args...) noexcept> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__stdcall C::*)(Args...) noexcept> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__stdcall C::*)(Args...) const noexcept> : function_traits_base<true, true, R, Args...>
{
};
#endif
template <class F>
struct functor_traits_impl : function_traits_impl<decltype(&F::operator())>
{
};
template <class F>
struct function_traits : std::conditional_t<std::is_class_v<F>,
detail::functor_traits_impl<F>,
detail::function_traits_impl<F>>
{
};
template <std::size_t I, class F, class = void>
struct function_argument_or_void
{
using type = void;
};
template <std::size_t I, class F>
struct function_argument_or_void<I, F, std::enable_if_t<I < std::tuple_size_v<typename function_traits<F>::argument_types>>>
{
using type = std::tuple_element_t<I, typename function_traits<F>::argument_types>;
};
template <std::size_t I, class F>
using function_argument_or_void_t = typename function_argument_or_void<I, F>::type;
template <class F>
using function_result_t = typename function_traits<F>::result_type;
template <std::size_t I, class F>
using function_argument_t = std::tuple_element_t<I, typename function_traits<F>::argument_types>;
template <class F>
using function_arguments_t = typename function_traits<F>::argument_types;
template <class F>
static constexpr std::size_t function_arity_v = function_traits<F>::arity;
template <class F>
static constexpr bool function_is_member_v = function_traits<F>::is_member;
template <class F>
static constexpr bool function_is_const_v = function_traits<F>::is_const;
template <class T, class = void>
struct is_callable
{
static constexpr bool value = false;
};
template <class T>
struct is_callable<T, std::void_t<decltype(&T::operator())>>
{
static constexpr bool value = true;
};
template <class T>
struct is_callable<T, std::enable_if_t<std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>>>
{
static constexpr bool value = true;
};
template <class T>
struct is_callable<T, std::enable_if_t<std::is_member_function_pointer_v<T>>>
{
static constexpr bool value = true;
};
template <class T>
inline static constexpr bool is_callable_v = is_callable<T>::value;
template <class T>
struct is_const_member_function_pointer
{
static constexpr bool value = false;
};
template <class T, class R, class... Args>
struct is_const_member_function_pointer<R (T::*)(Args...)>
{
static constexpr bool value = false;
};
template <class T, class R, class... Args>
struct is_const_member_function_pointer<R (T::*)(Args...) const>
{
static constexpr bool value = true;
};
template <class T, class R, class... Args>
struct is_const_member_function_pointer<R (T::*)(Args...) noexcept>
{
static constexpr bool value = false;
};
template <class T, class R, class... Args>
struct is_const_member_function_pointer<R (T::*)(Args...) const noexcept>
{
static constexpr bool value = true;
};
template <class T>
inline static constexpr bool is_const_member_function_pointer_v = is_const_member_function_pointer<T>::value;
template <class T>
struct is_cfunction_pointer
{
static constexpr bool value = false;
};
template <>
struct is_cfunction_pointer<int (*)(lua_State*)>
{
static constexpr bool value = true;
};
template <class T>
inline static constexpr bool is_cfunction_pointer_v = is_cfunction_pointer<T>::value;
template <class T>
struct is_member_cfunction_pointer
{
static constexpr bool value = false;
};
template <class T>
struct is_member_cfunction_pointer<int (T::*)(lua_State*)>
{
static constexpr bool value = true;
};
template <class T>
struct is_member_cfunction_pointer<int (T::*)(lua_State*) const>
{
static constexpr bool value = true;
};
template <class T>
inline static constexpr bool is_member_cfunction_pointer_v = is_member_cfunction_pointer<T>::value;
template <class T>
struct is_const_member_cfunction_pointer
{
static constexpr bool value = false;
};
template <class T>
struct is_const_member_cfunction_pointer<int (T::*)(lua_State*)>
{
static constexpr bool value = false;
};
template <class T>
struct is_const_member_cfunction_pointer<int (T::*)(lua_State*) const>
{
static constexpr bool value = true;
};
template <class T>
inline static constexpr bool is_const_member_cfunction_pointer_v = is_const_member_cfunction_pointer<T>::value;
template <class T>
inline static constexpr bool is_any_cfunction_pointer_v = is_cfunction_pointer_v<T> || is_member_cfunction_pointer_v<T>;
template <class T, class F>
inline static constexpr bool is_proxy_member_function_v =
!std::is_member_function_pointer_v<F> &&
std::is_same_v<T, remove_cvref_t<std::remove_pointer_t<function_argument_or_void_t<0, F>>>>;
template <class T, class F>
inline static constexpr bool is_const_proxy_function_v =
is_proxy_member_function_v<T, F> &&
std::is_const_v<std::remove_pointer_t<function_argument_or_void_t<0, F>>>;
template <class, class>
struct function_arity_excluding
{
};
template < class... Ts, class ExclusionType>
struct function_arity_excluding<std::tuple<Ts...>, ExclusionType>
: std::integral_constant<std::size_t, (0 + ... + (std::is_same_v<std::decay_t<Ts>, ExclusionType> ? 0 : 1))>
{
};
template <class F, class ExclusionType>
inline static constexpr std::size_t function_arity_excluding_v = function_arity_excluding<function_arguments_t<F>, ExclusionType>::value;
template <class, class, class, class, class = void>
struct member_function_arity_excluding
{
};
template <class T, class F, class... Ts, class ExclusionType>
struct member_function_arity_excluding<T, F, std::tuple<Ts...>, ExclusionType, std::enable_if_t<!is_proxy_member_function_v<T, F>>>
: std::integral_constant<std::size_t, (0 + ... + (std::is_same_v<std::decay_t<Ts>, ExclusionType> ? 0 : 1))>
{
};
template <class T, class F, class... Ts, class ExclusionType>
struct member_function_arity_excluding<T, F, std::tuple<Ts...>, ExclusionType, std::enable_if_t<is_proxy_member_function_v<T, F>>>
: std::integral_constant<std::size_t, (0 + ... + (std::is_same_v<std::decay_t<Ts>, ExclusionType> ? 0 : 1)) - 1>
{
};
template <class T, class F, class ExclusionType>
inline static constexpr std::size_t member_function_arity_excluding_v = member_function_arity_excluding<T, F, function_arguments_t<F>, ExclusionType>::value;
template <class T, class F>
static constexpr bool is_const_function =
detail::is_const_member_function_pointer_v<F> ||
(detail::function_arity_v<F> > 0 && detail::is_const_proxy_function_v<T, F>);
template <class T, class... Fs>
inline static constexpr std::size_t const_functions_count = (0 + ... + (is_const_function<T, Fs> ? 1 : 0));
template <class T, class... Fs>
inline static constexpr std::size_t non_const_functions_count = (0 + ... + (is_const_function<T, Fs> ? 0 : 1));
template <class... Types>
constexpr auto tupleize(Types&&... types)
{
return std::tuple<Types...>(std::forward<Types>(types)...);
}
template <class T>
struct remove_first_type
{
};
template <class T, class... Ts>
struct remove_first_type<std::tuple<T, Ts...>>
{
using type = std::tuple<Ts...>;
};
template <class T>
using remove_first_type_t = typename remove_first_type<T>::type;
}
}
// End File: Source/LuaBridge/detail/FuncTraits.h
// Begin File: Source/LuaBridge/detail/CFunctions.h
namespace luabridge {
namespace detail {
template <class T>
auto unwrap_argument_or_error(lua_State* L, std::size_t index)
{
auto result = Stack<T>::get(L, static_cast<int>(index));
if (! result)
luaL_error(L, "Error decoding argument #%d: %s", static_cast<int>(index), result.message().c_str());
return std::move(*result);
}
template <class ArgsPack, std::size_t Start, std::size_t... Indices>
auto make_arguments_list_impl(lua_State* L, std::index_sequence<Indices...>)
{
return tupleize(unwrap_argument_or_error<std::tuple_element_t<Indices, ArgsPack>>(L, Start + Indices)...);
}
template <class ArgsPack, std::size_t Start>
auto make_arguments_list(lua_State* L)
{
return make_arguments_list_impl<ArgsPack, Start>(L, std::make_index_sequence<std::tuple_size_v<ArgsPack>>());
}
template <std::size_t Index = 0, class... Types>
auto push_arguments(lua_State*, std::tuple<Types...>)
-> std::enable_if_t<Index == sizeof...(Types), std::tuple<Result, std::size_t>>
{
return std::make_tuple(Result(), Index + 1);
}
template <std::size_t Index = 0, class... Types>
auto push_arguments(lua_State* L, std::tuple<Types...> t)
-> std::enable_if_t<Index < sizeof...(Types), std::tuple<Result, std::size_t>>
{
using T = std::tuple_element_t<Index, std::tuple<Types...>>;
auto result = Stack<T>::push(L, std::get<Index>(t));
if (! result)
return std::make_tuple(result, Index + 1);
return push_arguments<Index + 1, Types...>(L, std::move(t));
}
template <std::ptrdiff_t Start, std::ptrdiff_t Index = 0, class... Types>
auto pop_arguments(lua_State*, std::tuple<Types...>&)
-> std::enable_if_t<Index == sizeof...(Types), std::size_t>
{
return sizeof...(Types);
}
template <std::ptrdiff_t Start, std::ptrdiff_t Index = 0, class... Types>
auto pop_arguments(lua_State* L, std::tuple<Types...>& t)
-> std::enable_if_t<Index < sizeof...(Types), std::size_t>
{
using T = std::tuple_element_t<Index, std::tuple<Types...>>;
std::get<Index>(t) = Stack<T>::get(L, Start - Index);
return pop_arguments<Start, Index + 1, Types...>(L, t);
}
inline int index_metamethod(lua_State* L)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
luaL_checkstack(L, 3, detail::error_lua_stack_overflow);
#endif
assert(lua_istable(L, 1) || lua_isuserdata(L, 1));
lua_getmetatable(L, 1);
assert(lua_istable(L, -1));
for (;;)
{
lua_pushvalue(L, 2);
lua_rawget(L, -2);
if (lua_iscfunction(L, -1))
{
lua_remove(L, -2);
return 1;
}
assert(lua_isnil(L, -1));
lua_pop(L, 1);
lua_rawgetp(L, -1, getPropgetKey());
assert(lua_istable(L, -1));
lua_pushvalue(L, 2);
lua_rawget(L, -2);
lua_remove(L, -2);
if (lua_iscfunction(L, -1))
{
lua_remove(L, -2);
lua_pushvalue(L, 1);
lua_call(L, 1, 1);
return 1;
}
assert(lua_isnil(L, -1));
lua_pop(L, 1);
lua_rawgetp(L, -1, getParentKey());
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
lua_rawgetp(L, -1, getIndexFallbackKey());
lua_remove(L, -2);
if (lua_iscfunction(L, -1))
{
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_call(L, 2, 1);
}
else
{
lua_pop(L, 1);
lua_pushnil(L);
}
return 1;
}
assert(lua_istable(L, -1));
lua_remove(L, -2);
}
}
inline int newindex_metamethod(lua_State* L, bool pushSelf)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
luaL_checkstack(L, 3, detail::error_lua_stack_overflow);
#endif
assert(lua_istable(L, 1) || lua_isuserdata(L, 1));
lua_getmetatable(L, 1);
assert(lua_istable(L, -1));
for (;;)
{
lua_rawgetp(L, -1, getPropsetKey());
if (lua_isnil(L, -1))
{
lua_pop(L, 2);
luaL_error(L, "No member named '%s'", lua_tostring(L, 2));
}
assert(lua_istable(L, -1));
lua_pushvalue(L, 2);
lua_rawget(L, -2);
lua_remove(L, -2);
if (lua_iscfunction(L, -1))
{
lua_remove(L, -2);
if (pushSelf)
lua_pushvalue(L, 1);
lua_pushvalue(L, 3);
lua_call(L, pushSelf ? 2 : 1, 0);
return 0;
}
assert(lua_isnil(L, -1));
lua_pop(L, 1);
lua_rawgetp(L, -1, getParentKey());
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
lua_rawgetp(L, -1, getNewIndexFallbackKey());
if (lua_iscfunction(L, -1))
{
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_pushvalue(L, 3);
lua_call(L, 3, 1);
return 0;
}
lua_pop(L, 1);
lua_pop(L, 1);
luaL_error(L, "No writable member '%s'", lua_tostring(L, 2));
return 0;
}
assert(lua_istable(L, -1));
lua_remove(L, -2);
}
return 0;
}
inline int newindex_object_metamethod(lua_State* L)
{
return newindex_metamethod(L, true);
}
inline int newindex_static_metamethod(lua_State* L)
{
return newindex_metamethod(L, false);
}
inline int read_only_error(lua_State* L)
{
std::string s;
s = s + "'" + lua_tostring(L, lua_upvalueindex(1)) + "' is read-only";
luaL_error(L, "%s", s.c_str());
return 0;
}
template <class C>
static int gc_metamethod(lua_State* L)
{
Userdata* ud = Userdata::getExact<C>(L, 1);
assert(ud);
ud->~Userdata();
return 0;
}
template <class T, class C = void>
struct property_getter;
template <class T>
struct property_getter<T, void>
{
static int call(lua_State* L)
{
assert(lua_islightuserdata(L, lua_upvalueindex(1)));
T* ptr = static_cast<T*>(lua_touserdata(L, lua_upvalueindex(1)));
assert(ptr != nullptr);
auto result = Stack<T&>::push(L, *ptr);
if (! result)
raise_lua_error(L, "%s", result.message().c_str());
return 1;
}
};
#if 0
template <class T>
struct property_getter<std::reference_wrapper<T>, void>
{
static int call(lua_State* L)
{
assert(lua_islightuserdata(L, lua_upvalueindex(1)));
std::reference_wrapper<T>* ptr = static_cast<std::reference_wrapper<T>*>(lua_touserdata(L, lua_upvalueindex(1)));
assert(ptr != nullptr);
auto result = Stack<T&>::push(L, ptr->get());
if (! result)
luaL_error(L, "%s", result.message().c_str());
return 1;
}
};
#endif
template <class T, class C>
struct property_getter
{
static int call(lua_State* L)
{
C* c = Userdata::get<C>(L, 1, true);
T C::** mp = static_cast<T C::**>(lua_touserdata(L, lua_upvalueindex(1)));
Result result;
#if LUABRIDGE_HAS_EXCEPTIONS
try
{
#endif
result = Stack<T&>::push(L, c->**mp);
#if LUABRIDGE_HAS_EXCEPTIONS
}
catch (const std::exception& e)
{
raise_lua_error(L, "%s", e.what());
}
#endif
if (! result)
raise_lua_error(L, "%s", result.message().c_str());
return 1;
}
};
inline void add_property_getter(lua_State* L, const char* name, int tableIndex)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
luaL_checkstack(L, 2, detail::error_lua_stack_overflow);
#endif
assert(name != nullptr);
assert(lua_istable(L, tableIndex));
assert(lua_iscfunction(L, -1));
lua_rawgetp(L, tableIndex, getPropgetKey());
lua_pushvalue(L, -2);
rawsetfield(L, -2, name);
lua_pop(L, 2);
}
template <class T, class C = void>
struct property_setter;
template <class T>
struct property_setter<T, void>
{
static int call(lua_State* L)
{
assert(lua_islightuserdata(L, lua_upvalueindex(1)));
T* ptr = static_cast<T*>(lua_touserdata(L, lua_upvalueindex(1)));
assert(ptr != nullptr);
auto result = Stack<T>::get(L, 1);
if (! result)
raise_lua_error(L, "%s", result.error().message().c_str());
*ptr = std::move(*result);
return 0;
}
};
#if 0
template <class T>
struct property_setter<std::reference_wrapper<T>, void>
{
static int call(lua_State* L)
{
assert(lua_islightuserdata(L, lua_upvalueindex(1)));
std::reference_wrapper<T>* ptr = static_cast<std::reference_wrapper<T>*>(lua_touserdata(L, lua_upvalueindex(1)));
assert(ptr != nullptr);
ptr->get() = Stack<T>::get(L, 1);
return 0;
}
};
#endif
template <class T, class C>
struct property_setter
{
static int call(lua_State* L)
{
C* c = Userdata::get<C>(L, 1, false);
T C::** mp = static_cast<T C::**>(lua_touserdata(L, lua_upvalueindex(1)));
#if LUABRIDGE_HAS_EXCEPTIONS
try
{
#endif
auto result = Stack<T>::get(L, 2);
if (! result)
raise_lua_error(L, "%s", result.error().message().c_str());
c->** mp = std::move(*result);
#if LUABRIDGE_HAS_EXCEPTIONS
}
catch (const std::exception& e)
{
raise_lua_error(L, "%s", e.what());
}
#endif
return 0;
}
};
inline void add_property_setter(lua_State* L, const char* name, int tableIndex)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
luaL_checkstack(L, 2, detail::error_lua_stack_overflow);
#endif
assert(name != nullptr);
assert(lua_istable(L, tableIndex));
assert(lua_iscfunction(L, -1));
lua_rawgetp(L, tableIndex, getPropsetKey());
lua_pushvalue(L, -2);
rawsetfield(L, -2, name);
lua_pop(L, 2);
}
template <class ReturnType, class ArgsPack, std::size_t Start = 1u>
struct function
{
template <class F>
static int call(lua_State* L, F func)
{
Result result;
#if LUABRIDGE_HAS_EXCEPTIONS
try
{
#endif
result = Stack<ReturnType>::push(L, std::apply(func, make_arguments_list<ArgsPack, Start>(L)));
#if LUABRIDGE_HAS_EXCEPTIONS
}
catch (const std::exception& e)
{
raise_lua_error(L, "%s", e.what());
}
#endif
if (! result)
raise_lua_error(L, "%s", result.message().c_str());
return 1;
}
template <class T, class F>
static int call(lua_State* L, T* ptr, F func)
{
Result result;
#if LUABRIDGE_HAS_EXCEPTIONS
try
{
#endif
auto f = [ptr, func](auto&&... args) -> ReturnType { return (ptr->*func)(std::forward<decltype(args)>(args)...); };
result = Stack<ReturnType>::push(L, std::apply(f, make_arguments_list<ArgsPack, Start>(L)));
#if LUABRIDGE_HAS_EXCEPTIONS
}
catch (const std::exception& e)
{
raise_lua_error(L, "%s", e.what());
}
#endif
if (! result)
raise_lua_error(L, "%s", result.message().c_str());
return 1;
}
};
template <class ArgsPack, std::size_t Start>
struct function<void, ArgsPack, Start>
{
template <class F>
static int call(lua_State* L, F func)
{
#if LUABRIDGE_HAS_EXCEPTIONS
try
{
#endif
std::apply(func, make_arguments_list<ArgsPack, Start>(L));
#if LUABRIDGE_HAS_EXCEPTIONS
}
catch (const std::exception& e)
{
raise_lua_error(L, "%s", e.what());
}
#endif
return 0;
}
template <class T, class F>
static int call(lua_State* L, T* ptr, F func)
{
#if LUABRIDGE_HAS_EXCEPTIONS
try
{
#endif
auto f = [ptr, func](auto&&... args) { (ptr->*func)(std::forward<decltype(args)>(args)...); };
std::apply(f, make_arguments_list<ArgsPack, Start>(L));
#if LUABRIDGE_HAS_EXCEPTIONS
}
catch (const std::exception& e)
{
raise_lua_error(L, "%s", e.what());
}
#endif
return 0;
}
};
template <class F, class T>
int invoke_member_function(lua_State* L)
{
using FnTraits = detail::function_traits<F>;
assert(isfulluserdata(L, lua_upvalueindex(1)));
T* ptr = Userdata::get<T>(L, 1, false);
const F& func = *static_cast<const F*>(lua_touserdata(L, lua_upvalueindex(1)));
assert(func != nullptr);
return function<typename FnTraits::result_type, typename FnTraits::argument_types, 2>::call(L, ptr, func);
}
template <class F, class T>
int invoke_const_member_function(lua_State* L)
{
using FnTraits = detail::function_traits<F>;
assert(isfulluserdata(L, lua_upvalueindex(1)));
const T* ptr = Userdata::get<T>(L, 1, true);
const F& func = *static_cast<const F*>(lua_touserdata(L, lua_upvalueindex(1)));
assert(func != nullptr);
return function<typename FnTraits::result_type, typename FnTraits::argument_types, 2>::call(L, ptr, func);
}
template <class T>
int invoke_member_cfunction(lua_State* L)
{
using F = int (T::*)(lua_State * L);
assert(isfulluserdata(L, lua_upvalueindex(1)));
T* t = Userdata::get<T>(L, 1, false);
const F& func = *static_cast<const F*>(lua_touserdata(L, lua_upvalueindex(1)));
assert(func != nullptr);
return (t->*func)(L);
}
template <class T>
int invoke_const_member_cfunction(lua_State* L)
{
using F = int (T::*)(lua_State * L) const;
assert(isfulluserdata(L, lua_upvalueindex(1)));
const T* t = Userdata::get<T>(L, 1, true);
const F& func = *static_cast<const F*>(lua_touserdata(L, lua_upvalueindex(1)));
assert(func != nullptr);
return (t->*func)(L);
}
template <class F>
int invoke_proxy_function(lua_State* L)
{
using FnTraits = detail::function_traits<F>;
assert(lua_islightuserdata(L, lua_upvalueindex(1)));
auto func = reinterpret_cast<F>(lua_touserdata(L, lua_upvalueindex(1)));
assert(func != nullptr);
return function<typename FnTraits::result_type, typename FnTraits::argument_types, 1>::call(L, func);
}
template <class F>
int invoke_proxy_functor(lua_State* L)
{
using FnTraits = detail::function_traits<F>;
assert(isfulluserdata(L, lua_upvalueindex(1)));
auto& func = *align<F>(lua_touserdata(L, lua_upvalueindex(1)));
return function<typename FnTraits::result_type, typename FnTraits::argument_types, 1>::call(L, func);
}
template <bool Member>
inline int try_overload_functions(lua_State* L)
{
const int nargs = lua_gettop(L);
const int effective_args = nargs - (Member ? 1 : 0);
lua_pushvalue(L, lua_upvalueindex(1));
assert(lua_istable(L, -1));
const int idx_overloads = nargs + 1;
const int num_overloads = get_length(L, idx_overloads);
lua_createtable(L, num_overloads, 0);
const int idx_errors = nargs + 2;
int nerrors = 0;
lua_pushnil(L);
while (lua_next(L, idx_overloads) != 0)
{
assert(lua_istable(L, -1));
lua_rawgeti(L, -1, 1);
assert(lua_isnumber(L, -1));
const int overload_arity = static_cast<int>(lua_tointeger(L, -1));
if (overload_arity >= 0 && overload_arity != effective_args)
{
lua_pushfstring(L, "Skipped overload #%d with unmatched arity of %d instead of %d", nerrors, overload_arity, effective_args);
lua_rawseti(L, idx_errors, ++nerrors);
lua_pop(L, 2);
continue;
}
lua_pop(L, 1);
lua_pushnumber(L, 2);
lua_gettable(L, -2);
assert(lua_isfunction(L, -1));
for (int i = 1; i <= nargs; ++i)
lua_pushvalue(L, i);
const int err = lua_pcall(L, nargs, LUA_MULTRET, 0);
if (err == LUABRIDGE_LUA_OK)
{
const int nresults = lua_gettop(L) - nargs - 4;
return nresults;
}
else if (err == LUA_ERRRUN)
{
lua_rawseti(L, idx_errors, ++nerrors);
}
else
{
return lua_error_x(L);
}
lua_pop(L, 1);
}
lua_Debug debug;
lua_getstack_info_x(L, 0, "n", &debug);
lua_pushfstring(L, "All %d overloads of %s returned an error:", nerrors, debug.name);
for (int i = 1; i <= nerrors; ++i)
{
lua_pushfstring(L, "\n%d: ", i);
lua_rawgeti(L, idx_errors, i);
}
lua_concat(L, nerrors * 2 + 1);
return lua_error_x(L);
}
inline void push_function(lua_State* L, lua_CFunction fp)
{
lua_pushcfunction_x(L, fp);
}
template <class ReturnType, class... Params>
inline void push_function(lua_State* L, ReturnType (*fp)(Params...))
{
using FnType = decltype(fp);
lua_pushlightuserdata(L, reinterpret_cast<void*>(fp));
lua_pushcclosure_x(L, &invoke_proxy_function<FnType>, 1);
}
template <class ReturnType, class... Params>
inline void push_function(lua_State* L, ReturnType (*fp)(Params...) noexcept)
{
using FnType = decltype(fp);
lua_pushlightuserdata(L, reinterpret_cast<void*>(fp));
lua_pushcclosure_x(L, &invoke_proxy_function<FnType>, 1);
}
template <class F, class = std::enable_if<is_callable_v<F> && !std::is_pointer_v<F> && !std::is_member_function_pointer_v<F>>>
inline void push_function(lua_State* L, F&& f)
{
lua_newuserdata_aligned<F>(L, std::forward<F>(f));
lua_pushcclosure_x(L, &invoke_proxy_functor<F>, 1);
}
template <class T>
void push_member_function(lua_State* L, lua_CFunction fp)
{
lua_pushcfunction_x(L, fp);
}
template <class T, class ReturnType, class... Params>
void push_member_function(lua_State* L, ReturnType (*fp)(T*, Params...))
{
using FnType = decltype(fp);
lua_pushlightuserdata(L, reinterpret_cast<void*>(fp));
lua_pushcclosure_x(L, &invoke_proxy_function<FnType>, 1);
}
template <class T, class ReturnType, class... Params>
void push_member_function(lua_State* L, ReturnType (*fp)(T*, Params...) noexcept)
{
using FnType = decltype(fp);
lua_pushlightuserdata(L, reinterpret_cast<void*>(fp));
lua_pushcclosure_x(L, &invoke_proxy_function<FnType>, 1);
}
template <class T, class ReturnType, class... Params>
void push_member_function(lua_State* L, ReturnType (*fp)(const T*, Params...))
{
using FnType = decltype(fp);
lua_pushlightuserdata(L, reinterpret_cast<void*>(fp));
lua_pushcclosure_x(L, &invoke_proxy_function<FnType>, 1);
}
template <class T, class ReturnType, class... Params>
void push_member_function(lua_State* L, ReturnType (*fp)(const T*, Params...) noexcept)
{
using FnType = decltype(fp);
lua_pushlightuserdata(L, reinterpret_cast<void*>(fp));
lua_pushcclosure_x(L, &invoke_proxy_function<FnType>, 1);
}
template <class T, class F, class = std::enable_if<
is_callable_v<F> &&
std::is_object_v<F> &&
!std::is_pointer_v<F> &&
!std::is_member_function_pointer_v<F>>>
void push_member_function(lua_State* L, F&& f)
{
static_assert(std::is_same_v<T, remove_cvref_t<std::remove_pointer_t<function_argument_or_void_t<0, F>>>>);
lua_newuserdata_aligned<F>(L, std::forward<F>(f));
lua_pushcclosure_x(L, &invoke_proxy_functor<F>, 1);
}
template <class T, class U, class ReturnType, class... Params>
void push_member_function(lua_State* L, ReturnType (U::*mfp)(Params...))
{
static_assert(std::is_same_v<T, U> || std::is_base_of_v<U, T>);
using F = decltype(mfp);
new (lua_newuserdata_x<F>(L, sizeof(F))) F(mfp);
lua_pushcclosure_x(L, &invoke_member_function<F, T>, 1);
}
template <class T, class U, class ReturnType, class... Params>
void push_member_function(lua_State* L, ReturnType (U::*mfp)(Params...) noexcept)
{
static_assert(std::is_same_v<T, U> || std::is_base_of_v<U, T>);
using F = decltype(mfp);
new (lua_newuserdata_x<F>(L, sizeof(F))) F(mfp);
lua_pushcclosure_x(L, &invoke_member_function<F, T>, 1);
}
template <class T, class U, class ReturnType, class... Params>
void push_member_function(lua_State* L, ReturnType (U::*mfp)(Params...) const)
{
static_assert(std::is_same_v<T, U> || std::is_base_of_v<U, T>);
using F = decltype(mfp);
new (lua_newuserdata_x<F>(L, sizeof(F))) F(mfp);
lua_pushcclosure_x(L, &detail::invoke_const_member_function<F, T>, 1);
}
template <class T, class U, class ReturnType, class... Params>
void push_member_function(lua_State* L, ReturnType (U::*mfp)(Params...) const noexcept)
{
static_assert(std::is_same_v<T, U> || std::is_base_of_v<U, T>);
using F = decltype(mfp);
new (lua_newuserdata_x<F>(L, sizeof(F))) F(mfp);
lua_pushcclosure_x(L, &detail::invoke_const_member_function<F, T>, 1);
}
template <class T, class U = T>
void push_member_function(lua_State* L, int (U::*mfp)(lua_State*))
{
static_assert(std::is_same_v<T, U> || std::is_base_of_v<U, T>);
using F = decltype(mfp);
new (lua_newuserdata_x<F>(L, sizeof(F))) F(mfp);
lua_pushcclosure_x(L, &invoke_member_cfunction<T>, 1);
}
template <class T, class U = T>
void push_member_function(lua_State* L, int (U::*mfp)(lua_State*) const)
{
static_assert(std::is_same_v<T, U> || std::is_base_of_v<U, T>);
using F = decltype(mfp);
new (lua_newuserdata_x<F>(L, sizeof(F))) F(mfp);
lua_pushcclosure_x(L, &invoke_const_member_cfunction<T>, 1);
}
template <class T, class Args>
struct constructor;
template <class T>
struct constructor<T, void>
{
using empty = std::tuple<>;
static T* call(const empty&)
{
return new T;
}
static T* call(void* ptr, const empty&)
{
return new (ptr) T;
}
};
template <class T, class Args>
struct constructor
{
static T* call(const Args& args)
{
auto alloc = [](auto&&... args) { return new T(std::forward<decltype(args)>(args)...); };
return std::apply(alloc, args);
}
static T* call(void* ptr, const Args& args)
{
auto alloc = [ptr](auto&&... args) { return new (ptr) T(std::forward<decltype(args)>(args)...); };
return std::apply(alloc, args);
}
};
template <class T>
struct placement_constructor
{
template <class F, class Args>
static T* construct(void* ptr, const F& func, const Args& args)
{
auto alloc = [ptr, &func](auto&&... args) { return func(ptr, std::forward<decltype(args)>(args)...); };
return std::apply(alloc, args);
}
template <class F>
static T* construct(void* ptr, const F& func)
{
return func(ptr);
}
};
template <class T>
struct external_constructor
{
template <class F, class Args>
static T* construct(const F& func, const Args& args)
{
auto alloc = [&func](auto&&... args) { return func(std::forward<decltype(args)>(args)...); };
return std::apply(alloc, args);
}
template <class F>
static T* construct(const F& func)
{
return func();
}
};
template <class C, class Args>
int constructor_container_proxy(lua_State* L)
{
using T = typename ContainerTraits<C>::Type;
T* object = detail::constructor<T, Args>::call(detail::make_arguments_list<Args, 2>(L));
auto result = detail::UserdataSharedHelper<C, false>::push(L, object);
if (! result)
luaL_error(L, "%s", result.message().c_str());
return 1;
}
template <class T, class Args>
int constructor_placement_proxy(lua_State* L)
{
std::error_code ec;
auto* value = detail::UserdataValue<T>::place(L, ec);
if (! value)
luaL_error(L, "%s", ec.message().c_str());
detail::constructor<T, Args>::call(value->getObject(), detail::make_arguments_list<Args, 2>(L));
value->commit();
return 1;
}
template <class T, class F>
struct constructor_forwarder
{
explicit constructor_forwarder(F f)
: m_func(std::move(f))
{
}
T* operator()(lua_State* L)
{
std::error_code ec;
auto* value = UserdataValue<T>::place(L, ec);
if (! value)
luaL_error(L, "%s", ec.message().c_str());
using FnTraits = function_traits<F>;
using FnArgs = remove_first_type_t<typename FnTraits::argument_types>;
T* obj = placement_constructor<T>::construct(
value->getObject(), m_func, make_arguments_list<FnArgs, 2>(L));
value->commit();
return obj;
}
private:
F m_func;
};
template <class T, class Alloc, class Dealloc>
struct factory_forwarder
{
explicit factory_forwarder(Alloc alloc, Dealloc dealloc)
: m_alloc(std::move(alloc))
, m_dealloc(std::move(dealloc))
{
}
T* operator()(lua_State* L)
{
using FnTraits = function_traits<Alloc>;
using FnArgs = typename FnTraits::argument_types;
T* obj = external_constructor<T>::construct(m_alloc, make_arguments_list<FnArgs, 0>(L));
std::error_code ec;
auto* value = UserdataValueExternal<T>::place(L, obj, m_dealloc, ec);
if (! value)
luaL_error(L, "%s", ec.message().c_str());
return obj;
}
private:
Alloc m_alloc;
Dealloc m_dealloc;
};
}
}
// End File: Source/LuaBridge/detail/CFunctions.h
// Begin File: Source/LuaBridge/detail/LuaRef.h
namespace luabridge {
class LuaResult;
struct LuaNil
{
};
template <>
struct Stack<LuaNil>
{
[[nodiscard]] static Result push(lua_State* L, const LuaNil&)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushnil(L);
return {};
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_type(L, index) == LUA_TNIL;
}
};
template <class Impl, class LuaRef>
class LuaRefBase
{
protected:
friend struct Stack<LuaRef>;
struct FromStack
{
};
LuaRefBase(lua_State* L)
: m_L(L)
{
}
int createRef() const
{
impl().push();
return luaL_ref(m_L, LUA_REGISTRYINDEX);
}
public:
std::string tostring() const
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(m_L, 2))
return {};
#endif
const StackRestore stackRestore(m_L);
lua_getglobal(m_L, "tostring");
impl().push();
lua_call(m_L, 1, 1);
const char* str = lua_tostring(m_L, -1);
return str != nullptr ? str : "";
}
void print(std::ostream& os) const
{
switch (type())
{
case LUA_TNONE:
case LUA_TNIL:
os << "nil";
break;
case LUA_TNUMBER:
os << unsafe_cast<lua_Number>();
break;
case LUA_TBOOLEAN:
os << (unsafe_cast<bool>() ? "true" : "false");
break;
case LUA_TSTRING:
os << '"' << unsafe_cast<const char*>() << '"';
break;
case LUA_TTABLE:
case LUA_TFUNCTION:
case LUA_TTHREAD:
case LUA_TUSERDATA:
case LUA_TLIGHTUSERDATA:
os << tostring();
break;
default:
os << "unknown";
break;
}
}
friend std::ostream& operator<<(std::ostream& os, const LuaRefBase& ref)
{
ref.print(os);
return os;
}
lua_State* state() const
{
return m_L;
}
void push(lua_State* L) const
{
assert(equalstates(L, m_L));
(void) L;
impl().push();
}
void pop(lua_State* L)
{
assert(equalstates(L, m_L));
(void) L;
impl().pop();
}
int type() const
{
const StackRestore stackRestore(m_L);
impl().push();
const int refType = lua_type(m_L, -1);
return refType;
}
bool isNil() const { return type() == LUA_TNIL; }
bool isBool() const { return type() == LUA_TBOOLEAN; }
bool isNumber() const { return type() == LUA_TNUMBER; }
bool isString() const { return type() == LUA_TSTRING; }
bool isTable() const { return type() == LUA_TTABLE; }
bool isFunction() const { return type() == LUA_TFUNCTION; }
bool isUserdata() const { return type() == LUA_TUSERDATA; }
bool isThread() const { return type() == LUA_TTHREAD; }
bool isLightUserdata() const { return type() == LUA_TLIGHTUSERDATA; }
bool isCallable() const
{
if (isFunction())
return true;
auto metatable = getMetatable();
return metatable.isTable() && metatable["__call"].isFunction();
}
template <class T>
TypeResult<T> cast() const
{
const StackRestore stackRestore(m_L);
impl().push();
if constexpr (std::is_enum_v<T>)
{
using U = std::underlying_type_t<T>;
auto result = Stack<U>::get(m_L, -1);
if (! result)
return result.error();
return static_cast<T>(*result);
}
else
{
return Stack<T>::get(m_L, -1);
}
}
template <class T>
T unsafe_cast() const
{
const StackRestore stackRestore(m_L);
impl().push();
if constexpr (std::is_enum_v<T>)
{
using U = std::underlying_type_t<T>;
return static_cast<T>(*Stack<U>::get(m_L, -1));
}
else
{
return *Stack<T>::get(m_L, -1);
}
}
template <class T>
bool isInstance() const
{
const StackRestore stackRestore(m_L);
impl().push();
if constexpr (std::is_enum_v<T>)
{
using U = std::underlying_type_t<T>;
return Stack<U>::isInstance(m_L, -1);
}
else
{
return Stack<T>::isInstance(m_L, -1);
}
}
template <class T>
operator T() const
{
return cast<T>().value();
}
LuaRef getMetatable() const
{
if (isNil())
return LuaRef(m_L);
const StackRestore stackRestore(m_L);
impl().push();
if (! lua_getmetatable(m_L, -1))
return LuaRef(m_L);
return LuaRef::fromStack(m_L);
}
template <class T>
bool operator==(const T& rhs) const
{
const StackRestore stackRestore(m_L);
impl().push();
if (! Stack<T>::push(m_L, rhs))
return false;
return lua_compare(m_L, -2, -1, LUA_OPEQ) == 1;
}
template <class T>
bool operator!=(const T& rhs) const
{
return !(*this == rhs);
}
template <class T>
bool operator<(const T& rhs) const
{
const StackRestore stackRestore(m_L);
impl().push();
if (! Stack<T>::push(m_L, rhs))
return false;
const int lhsType = lua_type(m_L, -2);
const int rhsType = lua_type(m_L, -1);
if (lhsType != rhsType)
return lhsType < rhsType;
return lua_compare(m_L, -2, -1, LUA_OPLT) == 1;
}
template <class T>
bool operator<=(const T& rhs) const
{
const StackRestore stackRestore(m_L);
impl().push();
if (! Stack<T>::push(m_L, rhs))
return false;
const int lhsType = lua_type(m_L, -2);
const int rhsType = lua_type(m_L, -1);
if (lhsType != rhsType)
return lhsType <= rhsType;
return lua_compare(m_L, -2, -1, LUA_OPLE) == 1;
}
template <class T>
bool operator>(const T& rhs) const
{
const StackRestore stackRestore(m_L);
impl().push();
if (! Stack<T>::push(m_L, rhs))
return false;
const int lhsType = lua_type(m_L, -2);
const int rhsType = lua_type(m_L, -1);
if (lhsType != rhsType)
return lhsType > rhsType;
return lua_compare(m_L, -1, -2, LUA_OPLT) == 1;
}
template <class T>
bool operator>=(const T& rhs) const
{
const StackRestore stackRestore(m_L);
impl().push();
if (! Stack<T>::push(m_L, rhs))
return false;
const int lhsType = lua_type(m_L, -2);
const int rhsType = lua_type(m_L, -1);
if (lhsType != rhsType)
return lhsType >= rhsType;
return lua_compare(m_L, -1, -2, LUA_OPLE) == 1;
}
template <class T>
bool rawequal(const T& v) const
{
const StackRestore stackRestore(m_L);
impl().push();
if (! Stack<T>::push(m_L, v))
return false;
return lua_rawequal(m_L, -1, -2) == 1;
}
int length() const
{
const StackRestore stackRestore(m_L);
impl().push();
return get_length(m_L, -1);
}
template <class... Args>
LuaResult operator()(Args&&... args) const;
protected:
lua_State* m_L = nullptr;
private:
const Impl& impl() const { return static_cast<const Impl&>(*this); }
Impl& impl() { return static_cast<Impl&>(*this); }
};
class LuaRef : public LuaRefBase<LuaRef, LuaRef>
{
class TableItem : public LuaRefBase<TableItem, LuaRef>
{
friend class LuaRef;
public:
TableItem(lua_State* L, int tableRef)
: LuaRefBase(L)
, m_keyRef(luaL_ref(L, LUA_REGISTRYINDEX))
{
#if LUABRIDGE_SAFE_STACK_CHECKS
luaL_checkstack(m_L, 1, detail::error_lua_stack_overflow);
#endif
lua_rawgeti(m_L, LUA_REGISTRYINDEX, tableRef);
m_tableRef = luaL_ref(L, LUA_REGISTRYINDEX);
}
TableItem(const TableItem& other)
: LuaRefBase(other.m_L)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(m_L, 1))
return;
#endif
lua_rawgeti(m_L, LUA_REGISTRYINDEX, other.m_tableRef);
m_tableRef = luaL_ref(m_L, LUA_REGISTRYINDEX);
lua_rawgeti(m_L, LUA_REGISTRYINDEX, other.m_keyRef);
m_keyRef = luaL_ref(m_L, LUA_REGISTRYINDEX);
}
~TableItem()
{
if (m_keyRef != LUA_NOREF)
luaL_unref(m_L, LUA_REGISTRYINDEX, m_keyRef);
if (m_tableRef != LUA_NOREF)
luaL_unref(m_L, LUA_REGISTRYINDEX, m_tableRef);
}
template <class T>
TableItem& operator=(const T& v)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(m_L, 2))
return *this;
#endif
const StackRestore stackRestore(m_L);
lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_tableRef);
lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_keyRef);
if (! Stack<T>::push(m_L, v))
return *this;
lua_settable(m_L, -3);
return *this;
}
template <class T>
TableItem& rawset(const T& v)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(m_L, 2))
return *this;
#endif
const StackRestore stackRestore(m_L);
lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_tableRef);
lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_keyRef);
if (! Stack<T>::push(m_L, v))
return *this;
lua_rawset(m_L, -3);
return *this;
}
using LuaRefBase::push;
void push() const
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(m_L, 3))
return;
#endif
lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_tableRef);
lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_keyRef);
lua_gettable(m_L, -2);
lua_remove(m_L, -2);
}
template <class T>
TableItem operator[](const T& key) const
{
return LuaRef(*this)[key];
}
template <class T>
LuaRef rawget(const T& key) const
{
return LuaRef(*this).rawget(key);
}
private:
int m_tableRef = LUA_NOREF;
int m_keyRef = LUA_NOREF;
};
friend struct Stack<TableItem>;
friend struct Stack<TableItem&>;
LuaRef(lua_State* L, FromStack)
: LuaRefBase(L)
, m_ref(luaL_ref(m_L, LUA_REGISTRYINDEX))
{
}
LuaRef(lua_State* L, int index, FromStack)
: LuaRefBase(L)
, m_ref(LUA_NOREF)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(m_L, 1))
return;
#endif
lua_pushvalue(m_L, index);
m_ref = luaL_ref(m_L, LUA_REGISTRYINDEX);
}
public:
LuaRef(lua_State* L)
: LuaRefBase(L)
, m_ref(LUA_NOREF)
{
}
template <class T>
LuaRef(lua_State* L, const T& v)
: LuaRefBase(L)
, m_ref(LUA_NOREF)
{
if (! Stack<T>::push(m_L, v))
return;
m_ref = luaL_ref(m_L, LUA_REGISTRYINDEX);
}
LuaRef(const TableItem& v)
: LuaRefBase(v.state())
, m_ref(v.createRef())
{
}
LuaRef(const LuaRef& other)
: LuaRefBase(other.m_L)
, m_ref(other.createRef())
{
}
LuaRef(LuaRef&& other)
: LuaRefBase(other.m_L)
, m_ref(std::exchange(other.m_ref, LUA_NOREF))
{
}
~LuaRef()
{
if (m_ref != LUA_NOREF)
luaL_unref(m_L, LUA_REGISTRYINDEX, m_ref);
}
static LuaRef fromStack(lua_State* L)
{
return LuaRef(L, FromStack());
}
static LuaRef fromStack(lua_State* L, int index)
{
return LuaRef(L, index, FromStack());
}
static LuaRef newTable(lua_State* L)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return { L };
#endif
lua_newtable(L);
return LuaRef(L, FromStack());
}
static LuaRef getGlobal(lua_State* L, const char* name)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return { L };
#endif
lua_getglobal(L, name);
return LuaRef(L, FromStack());
}
bool isValid() const { return m_ref != LUA_NOREF; }
LuaRef& operator=(const LuaRef& rhs)
{
LuaRef ref(rhs);
swap(ref);
return *this;
}
LuaRef& operator=(LuaRef&& rhs)
{
if (m_ref != LUA_NOREF)
luaL_unref(m_L, LUA_REGISTRYINDEX, m_ref);
m_L = rhs.m_L;
m_ref = std::exchange(rhs.m_ref, LUA_NOREF);
return *this;
}
LuaRef& operator=(const LuaRef::TableItem& rhs)
{
LuaRef ref(rhs);
swap(ref);
return *this;
}
LuaRef& operator=(const LuaNil&)
{
LuaRef ref(m_L);
swap(ref);
return *this;
}
template <class T>
LuaRef& operator=(const T& rhs)
{
LuaRef ref(m_L, rhs);
swap(ref);
return *this;
}
using LuaRefBase::push;
void push() const
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(m_L, 1))
return;
#endif
lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_ref);
}
void pop()
{
if (m_ref != LUA_NOREF)
luaL_unref(m_L, LUA_REGISTRYINDEX, m_ref);
m_ref = luaL_ref(m_L, LUA_REGISTRYINDEX);
}
template <class T>
TableItem operator[](const T& key) const
{
if (! Stack<T>::push(m_L, key))
return TableItem(m_L, m_ref);
return TableItem(m_L, m_ref);
}
template <class T>
LuaRef rawget(const T& key) const
{
const StackRestore stackRestore(m_L);
push(m_L);
if (! Stack<T>::push(m_L, key))
return LuaRef(m_L);
lua_rawget(m_L, -2);
return LuaRef(m_L, FromStack());
}
std::size_t hash() const
{
std::size_t value;
switch (type())
{
case LUA_TNONE:
value = std::hash<std::nullptr_t>{}(nullptr);
break;
case LUA_TBOOLEAN:
value = std::hash<bool>{}(unsafe_cast<bool>());
break;
case LUA_TNUMBER:
value = std::hash<lua_Number>{}(unsafe_cast<lua_Number>());
break;
case LUA_TSTRING:
value = std::hash<const char*>{}(unsafe_cast<const char*>());
break;
case LUA_TNIL:
case LUA_TTABLE:
case LUA_TFUNCTION:
case LUA_TTHREAD:
case LUA_TUSERDATA:
case LUA_TLIGHTUSERDATA:
default:
value = static_cast<std::size_t>(m_ref);
break;
}
const std::size_t seed = std::hash<int>{}(type());
return value + 0x9e3779b9u + (seed << 6) + (seed >> 2);
}
private:
void swap(LuaRef& other)
{
using std::swap;
swap(m_L, other.m_L);
swap(m_ref, other.m_ref);
}
int m_ref = LUA_NOREF;
};
template <class T>
auto operator==(const T& lhs, const LuaRef& rhs)
-> std::enable_if_t<!std::is_same_v<T, LuaRef> && !std::is_same_v<T, LuaRefBase<LuaRef, LuaRef>>, bool>
{
return rhs == lhs;
}
template <class T>
auto operator!=(const T& lhs, const LuaRef& rhs)
-> std::enable_if_t<!std::is_same_v<T, LuaRef> && !std::is_same_v<T, LuaRefBase<LuaRef, LuaRef>>, bool>
{
return !(rhs == lhs);
}
template <class T>
auto operator<(const T& lhs, const LuaRef& rhs)
-> std::enable_if_t<!std::is_same_v<T, LuaRef> && !std::is_same_v<T, LuaRefBase<LuaRef, LuaRef>>, bool>
{
return !(rhs >= lhs);
}
template <class T>
auto operator<=(const T& lhs, const LuaRef& rhs)
-> std::enable_if_t<!std::is_same_v<T, LuaRef> && !std::is_same_v<T, LuaRefBase<LuaRef, LuaRef>>, bool>
{
return !(rhs > lhs);
}
template <class T>
auto operator>(const T& lhs, const LuaRef& rhs)
-> std::enable_if_t<!std::is_same_v<T, LuaRef> && !std::is_same_v<T, LuaRefBase<LuaRef, LuaRef>>, bool>
{
return rhs <= lhs;
}
template <class T>
auto operator>=(const T& lhs, const LuaRef& rhs)
-> std::enable_if_t<!std::is_same_v<T, LuaRef> && !std::is_same_v<T, LuaRefBase<LuaRef, LuaRef>>, bool>
{
return !(rhs > lhs);
}
template <>
struct Stack<LuaRef>
{
[[nodiscard]] static Result push(lua_State* L, const LuaRef& v)
{
v.push(L);
return {};
}
[[nodiscard]] static TypeResult<LuaRef> get(lua_State* L, int index)
{
return LuaRef::fromStack(L, index);
}
};
template <>
struct Stack<LuaRef::TableItem>
{
[[nodiscard]] static Result push(lua_State* L, const LuaRef::TableItem& v)
{
v.push(L);
return {};
}
};
[[nodiscard]] inline LuaRef newTable(lua_State* L)
{
return LuaRef::newTable(L);
}
[[nodiscard]] inline LuaRef getGlobal(lua_State* L, const char* name)
{
return LuaRef::getGlobal(L, name);
}
template <class T>
[[nodiscard]] TypeResult<T> cast(const LuaRef& ref)
{
return ref.cast<T>();
}
template <class T>
[[nodiscard]] T unsafe_cast(const LuaRef& ref)
{
return ref.unsafe_cast<T>();
}
}
namespace std {
template <>
struct hash<luabridge::LuaRef>
{
std::size_t operator()(const luabridge::LuaRef& x) const
{
return x.hash();
}
};
}
// End File: Source/LuaBridge/detail/LuaRef.h
// Begin File: Source/LuaBridge/detail/Invoke.h
namespace luabridge {
class LuaResult
{
public:
explicit operator bool() const noexcept
{
return !m_ec;
}
bool wasOk() const noexcept
{
return !m_ec;
}
bool hasFailed() const noexcept
{
return !!m_ec;
}
std::error_code errorCode() const noexcept
{
return m_ec;
}
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 {};
}
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;
}
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;
};
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);
}
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)...);
}
}
// End File: Source/LuaBridge/detail/Invoke.h
// Begin File: Source/LuaBridge/detail/Iterator.h
namespace luabridge {
class Iterator
{
public:
explicit Iterator(const LuaRef& table, bool isEnd = false)
: m_L(table.state())
, m_table(table)
, m_key(table.state())
, m_value(table.state())
{
if (! isEnd)
{
next();
}
}
lua_State* state() const noexcept
{
return m_L;
}
std::pair<LuaRef, LuaRef> operator*() const
{
return std::make_pair(m_key, m_value);
}
LuaRef operator->() const
{
return m_value;
}
bool operator!=(const Iterator& rhs) const
{
assert(m_L == rhs.m_L);
return ! m_table.rawequal(rhs.m_table) || ! m_key.rawequal(rhs.m_key);
}
Iterator& operator++()
{
if (isNil())
{
return *this;
}
else
{
next();
return *this;
}
}
bool isNil() const noexcept
{
return m_key.isNil();
}
LuaRef key() const
{
return m_key;
}
LuaRef value() const
{
return m_value;
}
private:
Iterator operator++(int);
void next()
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(m_L, 2))
{
m_key = LuaNil();
m_value = LuaNil();
return;
}
#endif
m_table.push();
m_key.push();
if (lua_next(m_L, -2))
{
m_value.pop();
m_key.pop();
}
else
{
m_key = LuaNil();
m_value = LuaNil();
}
lua_pop(m_L, 1);
}
lua_State* m_L = nullptr;
LuaRef m_table;
LuaRef m_key;
LuaRef m_value;
};
class Range
{
public:
Range(const Iterator& begin, const Iterator& end)
: m_begin(begin)
, m_end(end)
{
}
const Iterator& begin() const noexcept
{
return m_begin;
}
const Iterator& end() const noexcept
{
return m_end;
}
private:
Iterator m_begin;
Iterator m_end;
};
inline Range pairs(const LuaRef& table)
{
return Range{ Iterator(table, false), Iterator(table, true) };
}
}
// End File: Source/LuaBridge/detail/Iterator.h
// Begin File: Source/LuaBridge/detail/Security.h
namespace luabridge {
class Security
{
public:
static bool hideMetatables() noexcept
{
return getSettings().hideMetatables;
}
static void setHideMetatables(bool shouldHide) noexcept
{
getSettings().hideMetatables = shouldHide;
}
private:
struct Settings
{
Settings() noexcept
: hideMetatables(true)
{
}
bool hideMetatables;
};
static Settings& getSettings() noexcept
{
static Settings settings;
return settings;
}
};
template <class T>
TypeResult<T> getGlobal(lua_State* L, const char* name)
{
lua_getglobal(L, name);
auto result = luabridge::Stack<T>::get(L, -1);
lua_pop(L, 1);
return result;
}
template <class T>
bool setGlobal(lua_State* L, T&& t, const char* name)
{
if (auto result = push(L, std::forward<T>(t)))
{
lua_setglobal(L, name);
return true;
}
return false;
}
inline void setHideMetatables(bool shouldHide) noexcept
{
Security::setHideMetatables(shouldHide);
}
}
// End File: Source/LuaBridge/detail/Security.h
// Begin File: Source/LuaBridge/detail/Namespace.h
namespace luabridge {
namespace detail {
class Registrar
{
protected:
Registrar(lua_State* L)
: L(L)
, m_stackSize(0)
{
}
Registrar(lua_State* L, int skipStackPops)
: L(L)
, m_stackSize(0)
, m_skipStackPops(skipStackPops)
{
}
Registrar(const Registrar& rhs)
: L(rhs.L)
, m_stackSize(std::exchange(rhs.m_stackSize, 0))
, m_skipStackPops(std::exchange(rhs.m_skipStackPops, 0))
{
}
Registrar& operator=(const Registrar& rhs)
{
m_stackSize = rhs.m_stackSize;
m_skipStackPops = rhs.m_skipStackPops;
return *this;
}
~Registrar()
{
const int popsCount = m_stackSize - m_skipStackPops;
if (popsCount > 0)
{
assert(popsCount <= lua_gettop(L));
lua_pop(L, popsCount);
}
}
void assertIsActive() const
{
if (m_stackSize == 0)
{
throw_or_assert<std::logic_error>("Unable to continue registration");
}
}
lua_State* const L = nullptr;
int mutable m_stackSize = 0;
int mutable m_skipStackPops = 0;
};
}
class Namespace : public detail::Registrar
{
#if 0
static int luaError(lua_State* L, std::string message)
{
assert(lua_isstring(L, lua_upvalueindex(1)));
std::string s;
lua_Debug ar;
int result = lua_getstack(L, 2, &ar);
if (result != 0)
{
lua_getinfo(L, "Sl", &ar);
s = ar.short_src;
if (ar.currentline != -1)
{
lua_pushnumber(L, ar.currentline);
s = s + ":" + lua_tostring(L, -1) + ": ";
lua_pop(L, 1);
}
}
s = s + message;
luaL_error(L, "%s", s.c_str());
return 0;
}
#endif
class ClassBase : public detail::Registrar
{
public:
explicit ClassBase(Namespace& parent)
: Registrar(parent)
{
}
using Registrar::operator=;
protected:
void createConstTable(const char* name, bool trueConst = true)
{
assert(name != nullptr);
std::string type_name = std::string(trueConst ? "const " : "") + name;
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setmetatable(L, -2);
lua_pushstring(L, type_name.c_str());
lua_rawsetp(L, -2, detail::getTypeKey());
lua_pushcfunction_x(L, &detail::index_metamethod);
rawsetfield(L, -2, "__index");
lua_pushcfunction_x(L, &detail::newindex_object_metamethod);
rawsetfield(L, -2, "__newindex");
lua_newtable(L);
lua_rawsetp(L, -2, detail::getPropgetKey());
if (Security::hideMetatables())
{
lua_pushnil(L);
rawsetfield(L, -2, "__metatable");
}
}
void createClassTable(const char* name)
{
assert(name != nullptr);
createConstTable(name, false);
lua_newtable(L);
lua_rawsetp(L, -2, detail::getPropsetKey());
lua_pushvalue(L, -2);
lua_rawsetp(L, -2, detail::getConstKey());
lua_pushvalue(L, -1);
lua_rawsetp(L, -3, detail::getClassKey());
}
void createStaticTable(const char* name)
{
assert(name != nullptr);
lua_newtable(L);
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setmetatable(L, -3);
lua_insert(L, -2);
rawsetfield(L, -5, name);
#if 0
lua_pushlightuserdata(L, this);
lua_pushcclosure_x(L, &tostringMetaMethod, 1);
rawsetfield(L, -2, "__tostring");
#endif
lua_pushcfunction_x(L, &detail::index_metamethod);
rawsetfield(L, -2, "__index");
lua_pushcfunction_x(L, &detail::newindex_static_metamethod);
rawsetfield(L, -2, "__newindex");
lua_newtable(L);
lua_rawsetp(L, -2, detail::getPropgetKey());
lua_newtable(L);
lua_rawsetp(L, -2, detail::getPropsetKey());
lua_pushvalue(L, -2);
lua_rawsetp(L, -2, detail::getClassKey());
if (Security::hideMetatables())
{
lua_pushnil(L);
rawsetfield(L, -2, "__metatable");
}
}
void assertStackState() const
{
assert(lua_istable(L, -3));
assert(lua_istable(L, -2));
assert(lua_istable(L, -1));
}
};
template <class T>
class Class : public ClassBase
{
public:
Class(const char* name, Namespace& parent)
: ClassBase(parent)
{
assert(name != nullptr);
assert(lua_istable(L, -1));
rawgetfield(L, -1, name);
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
createConstTable(name);
#if !defined(LUABRIDGE_ON_LUAU)
lua_pushcfunction_x(L, &detail::gc_metamethod<T>);
rawsetfield(L, -2, "__gc");
#endif
++m_stackSize;
createClassTable(name);
#if !defined(LUABRIDGE_ON_LUAU)
lua_pushcfunction_x(L, &detail::gc_metamethod<T>);
rawsetfield(L, -2, "__gc");
#endif
++m_stackSize;
createStaticTable(name);
++m_stackSize;
lua_pushvalue(L, -1);
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getStaticRegistryKey<T>());
lua_pushvalue(L, -2);
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey<T>());
lua_pushvalue(L, -3);
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getConstRegistryKey<T>());
}
else
{
assert(lua_istable(L, -1));
++m_stackSize;
lua_rawgetp(L, LUA_REGISTRYINDEX, detail::getConstRegistryKey<T>());
lua_insert(L, -2);
++m_stackSize;
lua_rawgetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey<T>());
lua_insert(L, -2);
++m_stackSize;
}
}
Class(const char* name, Namespace& parent, void const* const staticKey)
: ClassBase(parent)
{
assert(name != nullptr);
assert(lua_istable(L, -1));
createConstTable(name);
#if !defined(LUABRIDGE_ON_LUAU)
lua_pushcfunction_x(L, &detail::gc_metamethod<T>);
rawsetfield(L, -2, "__gc");
#endif
++m_stackSize;
createClassTable(name);
#if !defined(LUABRIDGE_ON_LUAU)
lua_pushcfunction_x(L, &detail::gc_metamethod<T>);
rawsetfield(L, -2, "__gc");
#endif
++m_stackSize;
createStaticTable(name);
++m_stackSize;
lua_rawgetp(L, LUA_REGISTRYINDEX, staticKey);
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
throw_or_assert<std::logic_error>("Base class is not registered");
return;
}
assert(lua_istable(L, -1));
lua_rawgetp(L, -1, detail::getClassKey());
assert(lua_istable(L, -1));
lua_rawgetp(L, -1, detail::getConstKey());
assert(lua_istable(L, -1));
lua_rawsetp(L, -6, detail::getParentKey());
lua_rawsetp(L, -4, detail::getParentKey());
lua_rawsetp(L, -2, detail::getParentKey());
lua_pushvalue(L, -1);
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getStaticRegistryKey<T>());
lua_pushvalue(L, -2);
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey<T>());
lua_pushvalue(L, -3);
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getConstRegistryKey<T>());
}
Namespace endClass()
{
assert(m_stackSize > 3);
m_stackSize -= 3;
lua_pop(L, 3);
return Namespace(*this);
}
template <class U, class = std::enable_if_t<!std::is_invocable_v<U>>>
Class<T>& addStaticProperty(const char* name, const U* value)
{
assert(name != nullptr);
assertStackState();
lua_pushlightuserdata(L, const_cast<U*>(value));
lua_pushcclosure_x(L, &detail::property_getter<U>::call, 1);
detail::add_property_getter(L, name, -2);
lua_pushstring(L, name);
lua_pushcclosure_x(L, &detail::read_only_error, 1);
detail::add_property_setter(L, name, -2);
return *this;
}
template <class U, class = std::enable_if_t<!std::is_invocable_v<U>>>
Class<T>& addStaticProperty(const char* name, U* value, bool isWritable = true)
{
assert(name != nullptr);
assertStackState();
lua_pushlightuserdata(L, value);
lua_pushcclosure_x(L, &detail::property_getter<U>::call, 1);
detail::add_property_getter(L, name, -2);
if (isWritable)
{
lua_pushlightuserdata(L, value);
lua_pushcclosure_x(L, &detail::property_setter<U>::call, 1);
}
else
{
lua_pushstring(L, name);
lua_pushcclosure_x(L, &detail::read_only_error, 1);
}
detail::add_property_setter(L, name, -2);
return *this;
}
template <class U>
Class<T>& addStaticProperty(const char* name, U (*get)(), void (*set)(U) = nullptr)
{
assert(name != nullptr);
assertStackState();
lua_pushlightuserdata(L, reinterpret_cast<void*>(get));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<U (*)()>, 1);
detail::add_property_getter(L, name, -2);
if (set != nullptr)
{
lua_pushlightuserdata(L, reinterpret_cast<void*>(set));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<void (*)(U)>, 1);
}
else
{
lua_pushstring(L, name);
lua_pushcclosure_x(L, &detail::read_only_error, 1);
}
detail::add_property_setter(L, name, -2);
return *this;
}
template <class U>
Class<T>& addStaticProperty(const char* name, U (*get)() noexcept, void (*set)(U) noexcept = nullptr)
{
assert(name != nullptr);
assertStackState();
lua_pushlightuserdata(L, reinterpret_cast<void*>(get));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<U (*)() noexcept>, 1);
detail::add_property_getter(L, name, -2);
if (set != nullptr)
{
lua_pushlightuserdata(L, reinterpret_cast<void*>(set));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<void (*)(U) noexcept>, 1);
}
else
{
lua_pushstring(L, name);
lua_pushcclosure_x(L, &detail::read_only_error, 1);
}
detail::add_property_setter(L, name, -2);
return *this;
}
template <class Getter, class = std::enable_if_t<!std::is_pointer_v<Getter>>>
Class<T>& addStaticProperty(const char* name, Getter get)
{
assert(name != nullptr);
assertStackState();
using GetType = decltype(get);
lua_newuserdata_aligned<GetType>(L, std::move(get));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<GetType>, 1);
detail::add_property_getter(L, name, -2);
return *this;
}
template <class Getter, class Setter, class = std::enable_if_t<!std::is_pointer_v<Getter> && !std::is_pointer_v<Setter>>>
Class<T>& addStaticProperty(const char* name, Getter get, Setter set)
{
assert(name != nullptr);
assertStackState();
using GetType = decltype(get);
using SetType = decltype(set);
lua_newuserdata_aligned<GetType>(L, std::move(get));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<GetType>, 1);
detail::add_property_getter(L, name, -2);
lua_newuserdata_aligned<SetType>(L, std::move(set));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<SetType>, 1);
detail::add_property_setter(L, name, -2);
return *this;
}
template <class... Functions>
auto addStaticFunction(const char* name, Functions... functions)
-> std::enable_if_t<(detail::is_callable_v<Functions> && ...) && (sizeof...(Functions) > 0), Class<T>&>
{
assert(name != nullptr);
assertStackState();
if constexpr (sizeof...(Functions) == 1)
{
([&]
{
detail::push_function(L, std::move(functions));
} (), ...);
}
else
{
lua_createtable(L, static_cast<int>(sizeof...(Functions)), 0);
int idx = 1;
([&]
{
lua_createtable(L, 2, 0);
lua_pushinteger(L, 1);
if constexpr (detail::is_any_cfunction_pointer_v<Functions>)
lua_pushinteger(L, -1);
else
lua_pushinteger(L, static_cast<int>(detail::function_arity_excluding_v<Functions, lua_State*>));
lua_settable(L, -3);
lua_pushinteger(L, 2);
detail::push_function(L, std::move(functions));
lua_settable(L, -3);
lua_rawseti(L, -2, idx);
++idx;
} (), ...);
lua_pushcclosure_x(L, &detail::try_overload_functions<false>, 1);
}
rawsetfield(L, -2, name);
return *this;
}
template <class U, class V>
Class<T>& addProperty(const char* name, U V::*mp, bool isWritable = true)
{
static_assert(std::is_base_of_v<V, T>);
using MemberPtrType = decltype(mp);
assert(name != nullptr);
assertStackState();
new (lua_newuserdata_x<MemberPtrType>(L, sizeof(MemberPtrType))) MemberPtrType(mp);
lua_pushcclosure_x(L, &detail::property_getter<U, T>::call, 1);
lua_pushvalue(L, -1);
detail::add_property_getter(L, name, -5);
detail::add_property_getter(L, name, -3);
if (isWritable)
{
new (lua_newuserdata_x<MemberPtrType>(L, sizeof(MemberPtrType))) MemberPtrType(mp);
lua_pushcclosure_x(L, &detail::property_setter<U, T>::call, 1);
detail::add_property_setter(L, name, -3);
}
return *this;
}
template <class TG, class TS = TG>
Class<T>& addProperty(const char* name, TG (T::*get)() const, void (T::*set)(TS) = nullptr)
{
using GetType = TG (T::*)() const;
using SetType = void (T::*)(TS);
assert(name != nullptr);
assertStackState();
new (lua_newuserdata_x<GetType>(L, sizeof(GetType))) GetType(get);
lua_pushcclosure_x(L, &detail::invoke_const_member_function<GetType, T>, 1);
lua_pushvalue(L, -1);
detail::add_property_getter(L, name, -5);
detail::add_property_getter(L, name, -3);
if (set != nullptr)
{
new (lua_newuserdata_x<SetType>(L, sizeof(SetType))) SetType(set);
lua_pushcclosure_x(L, &detail::invoke_member_function<SetType, T>, 1);
detail::add_property_setter(L, name, -3);
}
return *this;
}
template <class TG, class TS = TG>
Class<T>& addProperty(const char* name, TG (T::*get)() const noexcept, void (T::*set)(TS) noexcept = nullptr)
{
using GetType = TG (T::*)() const noexcept;
using SetType = void (T::*)(TS) noexcept;
assert(name != nullptr);
assertStackState();
new (lua_newuserdata_x<GetType>(L, sizeof(GetType))) GetType(get);
lua_pushcclosure_x(L, &detail::invoke_const_member_function<GetType, T>, 1);
lua_pushvalue(L, -1);
detail::add_property_getter(L, name, -5);
detail::add_property_getter(L, name, -3);
if (set != nullptr)
{
new (lua_newuserdata_x<SetType>(L, sizeof(SetType))) SetType(set);
lua_pushcclosure_x(L, &detail::invoke_member_function<SetType, T>, 1);
detail::add_property_setter(L, name, -3);
}
return *this;
}
template <class TG, class TS = TG>
Class<T>& addProperty(const char* name, TG (T::*get)(lua_State*) const, void (T::*set)(TS, lua_State*) = nullptr)
{
using GetType = TG (T::*)(lua_State*) const;
using SetType = void (T::*)(TS, lua_State*);
assert(name != nullptr);
assertStackState();
new (lua_newuserdata_x<GetType>(L, sizeof(GetType))) GetType(get);
lua_pushcclosure_x(L, &detail::invoke_const_member_function<GetType, T>, 1);
lua_pushvalue(L, -1);
detail::add_property_getter(L, name, -5);
detail::add_property_getter(L, name, -3);
if (set != nullptr)
{
new (lua_newuserdata_x<SetType>(L, sizeof(SetType))) SetType(set);
lua_pushcclosure_x(L, &detail::invoke_member_function<SetType, T>, 1);
detail::add_property_setter(L, name, -3);
}
return *this;
}
template <class TG, class TS = TG>
Class<T>& addProperty(const char* name, TG (T::*get)(lua_State*) const noexcept, void (T::*set)(TS, lua_State*) noexcept = nullptr)
{
using GetType = TG (T::*)(lua_State*) const noexcept;
using SetType = void (T::*)(TS, lua_State*) noexcept;
assert(name != nullptr);
assertStackState();
new (lua_newuserdata_x<GetType>(L, sizeof(GetType))) GetType(get);
lua_pushcclosure_x(L, &detail::invoke_const_member_function<GetType, T>, 1);
lua_pushvalue(L, -1);
detail::add_property_getter(L, name, -5);
detail::add_property_getter(L, name, -3);
if (set != nullptr)
{
new (lua_newuserdata_x<SetType>(L, sizeof(SetType))) SetType(set);
lua_pushcclosure_x(L, &detail::invoke_member_function<SetType, T>, 1);
detail::add_property_setter(L, name, -3);
}
return *this;
}
template <class TG, class TS = TG>
Class<T>& addProperty(const char* name, TG (*get)(const T*), void (*set)(T*, TS) = nullptr)
{
assert(name != nullptr);
assertStackState();
lua_pushlightuserdata(L, reinterpret_cast<void*>(get));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<TG (*)(const T*)>, 1);
lua_pushvalue(L, -1);
detail::add_property_getter(L, name, -5);
detail::add_property_getter(L, name, -3);
if (set != nullptr)
{
lua_pushlightuserdata( L, reinterpret_cast<void*>(set));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<void (*)(T*, TS)>, 1);
detail::add_property_setter(L, name, -3);
}
return *this;
}
template <class TG, class TS = TG>
Class<T>& addProperty(const char* name, TG (*get)(const T*) noexcept, void (*set)(T*, TS) noexcept = nullptr)
{
assert(name != nullptr);
assertStackState();
lua_pushlightuserdata(L, reinterpret_cast<void*>(get));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<TG (*)(const T*) noexcept>, 1);
lua_pushvalue(L, -1);
detail::add_property_getter(L, name, -5);
detail::add_property_getter(L, name, -3);
if (set != nullptr)
{
lua_pushlightuserdata( L, reinterpret_cast<void*>(set));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<void (*)(T*, TS) noexcept>, 1);
detail::add_property_setter(L, name, -3);
}
return *this;
}
Class<T>& addProperty(const char* name, lua_CFunction get, lua_CFunction set = nullptr)
{
assert(name != nullptr);
assertStackState();
lua_pushcfunction_x(L, get);
lua_pushvalue(L, -1);
detail::add_property_getter(L, name, -5);
detail::add_property_getter(L, name, -3);
if (set != nullptr)
{
lua_pushcfunction_x(L, set);
detail::add_property_setter(L, name, -3);
}
return *this;
}
template <class Getter, class = std::enable_if_t<!std::is_pointer_v<Getter>>>
Class<T>& addProperty(const char* name, Getter get)
{
using FirstArg = detail::function_argument_t<0, Getter>;
static_assert(std::is_same_v<std::decay_t<std::remove_pointer_t<FirstArg>>, T>);
assert(name != nullptr);
assertStackState();
using GetType = decltype(get);
lua_newuserdata_aligned<GetType>(L, std::move(get));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<GetType>, 1);
lua_pushvalue(L, -1);
detail::add_property_getter(L, name, -4);
detail::add_property_getter(L, name, -4);
return *this;
}
template <class Getter, class Setter, class = std::enable_if_t<!std::is_pointer_v<Getter> && !std::is_pointer_v<Setter>>>
Class<T>& addProperty(const char* name, Getter get, Setter set)
{
addProperty<Getter>(name, std::move(get));
using FirstArg = detail::function_argument_t<0, Setter>;
static_assert(std::is_same_v<std::decay_t<std::remove_pointer_t<FirstArg>>, T>);
assert(name != nullptr);
assertStackState();
using SetType = decltype(set);
lua_newuserdata_aligned<SetType>(L, std::move(set));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<SetType>, 1);
detail::add_property_setter(L, name, -3);
return *this;
}
template <class... Functions>
auto addFunction(const char* name, Functions... functions)
-> std::enable_if_t<(detail::is_callable_v<Functions> && ...) && (sizeof...(Functions) > 0), Class<T>&>
{
assert(name != nullptr);
assertStackState();
if (name == std::string_view("__gc"))
{
throw_or_assert<std::logic_error>("__gc metamethod registration is forbidden");
return *this;
}
if constexpr (sizeof...(Functions) == 1)
{
([&]
{
detail::push_member_function<T>(L, std::move(functions));
} (), ...);
if constexpr (detail::const_functions_count<T, Functions...> == 1)
{
lua_pushvalue(L, -1);
rawsetfield(L, -4, name);
rawsetfield(L, -4, name);
}
else
{
rawsetfield(L, -3, name);
}
}
else
{
if constexpr (detail::const_functions_count<T, Functions...> > 0)
{
lua_createtable(L, static_cast<int>(detail::const_functions_count<T, Functions...>), 0);
int idx = 1;
([&]
{
if (!detail::is_const_function<T, Functions>)
return;
lua_createtable(L, 2, 0);
lua_pushinteger(L, 1);
if constexpr (detail::is_any_cfunction_pointer_v<Functions>)
lua_pushinteger(L, -1);
else
lua_pushinteger(L, static_cast<int>(detail::member_function_arity_excluding_v<T, Functions, lua_State*>));
lua_settable(L, -3);
lua_pushinteger(L, 2);
detail::push_member_function<T>(L, std::move(functions));
lua_settable(L, -3);
lua_rawseti(L, -2, idx);
++idx;
} (), ...);
assert(idx > 1);
lua_pushcclosure_x(L, &detail::try_overload_functions<true>, 1);
lua_pushvalue(L, -1);
rawsetfield(L, -4, name);
rawsetfield(L, -4, name);
}
if constexpr (detail::non_const_functions_count<T, Functions...> > 0)
{
lua_createtable(L, static_cast<int>(detail::non_const_functions_count<T, Functions...>), 0);
int idx = 1;
([&]
{
if (detail::is_const_function<T, Functions>)
return;
lua_createtable(L, 2, 0);
lua_pushinteger(L, 1);
if constexpr (detail::is_any_cfunction_pointer_v<Functions>)
lua_pushinteger(L, -1);
else
lua_pushinteger(L, static_cast<int>(detail::member_function_arity_excluding_v<T, Functions, lua_State*>));
lua_settable(L, -3);
lua_pushinteger(L, 2);
detail::push_member_function<T>(L, std::move(functions));
lua_settable(L, -3);
lua_rawseti(L, -2, idx);
++idx;
} (), ...);
assert(idx > 1);
lua_pushcclosure_x(L, &detail::try_overload_functions<true>, 1);
rawsetfield(L, -3, name);
}
}
return *this;
}
template <class... Functions>
auto addConstructor()
-> std::enable_if_t<(sizeof...(Functions) > 0), Class<T>&>
{
assertStackState();
if constexpr (sizeof...(Functions) == 1)
{
([&]
{
lua_pushcclosure_x(L, &detail::constructor_placement_proxy<T, detail::function_arguments_t<Functions>>, 0);
} (), ...);
}
else
{
lua_createtable(L, static_cast<int>(sizeof...(Functions)), 0);
int idx = 1;
([&]
{
lua_createtable(L, 2, 0);
lua_pushinteger(L, 1);
lua_pushinteger(L, static_cast<int>(detail::function_arity_excluding_v<Functions, lua_State*>));
lua_settable(L, -3);
lua_pushinteger(L, 2);
lua_pushcclosure_x(L, &detail::constructor_placement_proxy<T, detail::function_arguments_t<Functions>>, 0);
lua_settable(L, -3);
lua_rawseti(L, -2, idx);
++idx;
} (), ...);
lua_pushcclosure_x(L, &detail::try_overload_functions<true>, 1);
}
rawsetfield(L, -2, "__call");
return *this;
}
template <class C, class... Functions>
auto addConstructorFrom()
-> std::enable_if_t<(sizeof...(Functions) > 0), Class<T>&>
{
assertStackState();
if constexpr (sizeof...(Functions) == 1)
{
([&]
{
lua_pushcclosure_x(L, &detail::constructor_container_proxy<C, detail::function_arguments_t<Functions>>, 0);
} (), ...);
}
else
{
lua_createtable(L, static_cast<int>(sizeof...(Functions)), 0);
int idx = 1;
([&]
{
lua_createtable(L, 2, 0);
lua_pushinteger(L, 1);
lua_pushinteger(L, static_cast<int>(detail::function_arity_excluding_v<Functions, lua_State*>));
lua_settable(L, -3);
lua_pushinteger(L, 2);
lua_pushcclosure_x(L, &detail::constructor_container_proxy<C, detail::function_arguments_t<Functions>>, 0);
lua_settable(L, -3);
lua_rawseti(L, -2, idx);
++idx;
} (), ...);
lua_pushcclosure_x(L, &detail::try_overload_functions<true>, 1);
}
rawsetfield(L, -2, "__call");
return *this;
}
template <class... Functions>
auto addConstructor(Functions... functions)
-> std::enable_if_t<(detail::is_callable_v<Functions> && ...) && (sizeof...(Functions) > 0), Class<T>&>
{
static_assert(((detail::function_arity_excluding_v<Functions, lua_State*> >= 1) && ...));
static_assert(((std::is_same_v<detail::function_argument_t<0, Functions>, void*>) && ...));
assertStackState();
if constexpr (sizeof...(Functions) == 1)
{
([&]
{
detail::push_function(L, detail::constructor_forwarder<T, Functions>(std::move(functions)));
} (), ...);
}
else
{
lua_createtable(L, static_cast<int>(sizeof...(Functions)), 0);
int idx = 1;
([&]
{
lua_createtable(L, 2, 0);
lua_pushinteger(L, 1);
if constexpr (detail::is_any_cfunction_pointer_v<Functions>)
lua_pushinteger(L, -1);
else
lua_pushinteger(L, static_cast<int>(detail::function_arity_excluding_v<Functions, lua_State*>) - 1);
lua_settable(L, -3);
lua_pushinteger(L, 2);
detail::push_function(L, detail::constructor_forwarder<T, Functions>(std::move(functions)));
lua_settable(L, -3);
lua_rawseti(L, -2, idx);
++idx;
} (), ...);
lua_pushcclosure_x(L, &detail::try_overload_functions<true>, 1);
}
rawsetfield(L, -2, "__call");
return *this;
}
template <class Allocator, class Deallocator>
Class<T>& addFactory(Allocator allocator, Deallocator deallocator)
{
assertStackState();
detail::push_function(L, detail::factory_forwarder<T, Allocator, Deallocator>(std::move(allocator), std::move(deallocator)));
rawsetfield(L, -2, "__call");
return *this;
}
template <class Function>
auto addIndexMetaMethod(Function function)
-> std::enable_if_t<!std::is_pointer_v<Function>
&& std::is_invocable_v<Function, T&, const LuaRef&, lua_State*>, Class<T>&>
{
using FnType = decltype(function);
assertStackState();
lua_newuserdata_aligned<FnType>(L, std::move(function));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<FnType>, 1);
lua_rawsetp(L, -3, detail::getIndexFallbackKey());
return *this;
}
Class<T>& addIndexMetaMethod(LuaRef (*idxf)(T&, const LuaRef&, lua_State*))
{
using FnType = decltype(idxf);
assertStackState();
lua_pushlightuserdata(L, reinterpret_cast<void*>(idxf));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<FnType>, 1);
lua_rawsetp(L, -3, detail::getIndexFallbackKey());
return *this;
}
Class<T>& addIndexMetaMethod(LuaRef (T::* idxf)(const LuaRef&, lua_State*))
{
using MemFnPtr = decltype(idxf);
assertStackState();
new (lua_newuserdata_x<MemFnPtr>(L, sizeof(MemFnPtr))) MemFnPtr(idxf);
lua_pushcclosure_x(L, &detail::invoke_member_function<MemFnPtr, T>, 1);
lua_rawsetp(L, -3, detail::getIndexFallbackKey());
return *this;
}
template <class Function>
auto addNewIndexMetaMethod(Function function)
-> std::enable_if_t<!std::is_pointer_v<Function>
&& std::is_invocable_v<Function, T&, const LuaRef&, const LuaRef&, lua_State*>, Class<T>&>
{
using FnType = decltype(function);
assertStackState();
lua_newuserdata_aligned<FnType>(L, std::move(function));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<FnType>, 1);
lua_rawsetp(L, -3, detail::getNewIndexFallbackKey());
return *this;
}
Class<T>& addNewIndexMetaMethod(LuaRef (*idxf)(T&, const LuaRef&, const LuaRef&, lua_State*))
{
using FnType = decltype(idxf);
assertStackState();
lua_pushlightuserdata(L, reinterpret_cast<void*>(idxf));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<FnType>, 1);
lua_rawsetp(L, -3, detail::getNewIndexFallbackKey());
return *this;
}
Class<T>& addNewIndexMetaMethod(LuaRef (T::* idxf)(const LuaRef&, const LuaRef&, lua_State*))
{
using MemFnPtr = decltype(idxf);
assertStackState();
new (lua_newuserdata_x<MemFnPtr>(L, sizeof(MemFnPtr))) MemFnPtr(idxf);
lua_pushcclosure_x(L, &detail::invoke_member_function<MemFnPtr, T>, 1);
lua_rawsetp(L, -3, detail::getNewIndexFallbackKey());
return *this;
}
};
class Table : public detail::Registrar
{
public:
explicit Table(const char* name, Namespace& parent)
: Registrar(parent)
{
lua_newtable(L);
lua_pushvalue(L, -1);
rawsetfield(L, -3, name);
++m_stackSize;
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setmetatable(L, -3);
++m_stackSize;
}
using Registrar::operator=;
template <class Function>
Table& addFunction(const char* name, Function function)
{
using FnType = decltype(function);
assert(name != nullptr);
assert(lua_istable(L, -1));
lua_newuserdata_aligned<FnType>(L, std::move(function));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<FnType>, 1);
rawsetfield(L, -3, name);
return *this;
}
template <class Function>
Table& addMetaFunction(const char* name, Function function)
{
using FnType = decltype(function);
assert(name != nullptr);
assert(lua_istable(L, -1));
lua_newuserdata_aligned<FnType>(L, std::move(function));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<FnType>, 1);
rawsetfield(L, -2, name);
return *this;
}
Namespace endTable()
{
assert(m_stackSize > 2);
m_stackSize -= 2;
lua_pop(L, 2);
return Namespace(*this);
}
};
private:
struct FromStack {};
explicit Namespace(lua_State* L)
: Registrar(L)
{
lua_getglobal(L, "_G");
++m_stackSize;
}
Namespace(lua_State* L, FromStack)
: Registrar(L, 1)
{
assert(lua_istable(L, -1));
{
lua_pushvalue(L, -1);
lua_setmetatable(L, -2);
lua_pushcfunction_x(L, &detail::index_metamethod);
rawsetfield(L, -2, "__index");
lua_newtable(L);
lua_rawsetp(L, -2, detail::getPropgetKey());
lua_newtable(L);
lua_rawsetp(L, -2, detail::getPropsetKey());
}
++m_stackSize;
}
Namespace(const char* name, Namespace& parent)
: Registrar(parent)
{
assert(name != nullptr);
assert(lua_istable(L, -1));
rawgetfield(L, -1, name);
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setmetatable(L, -2);
lua_pushcfunction_x(L, &detail::index_metamethod);
rawsetfield(L, -2, "__index");
lua_pushcfunction_x(L, &detail::newindex_static_metamethod);
rawsetfield(L, -2, "__newindex");
lua_newtable(L);
lua_rawsetp(L, -2, detail::getPropgetKey());
lua_newtable(L);
lua_rawsetp(L, -2, detail::getPropsetKey());
lua_pushvalue(L, -1);
rawsetfield(L, -3, name);
}
++m_stackSize;
}
explicit Namespace(ClassBase& child)
: Registrar(child)
{
}
explicit Namespace(Table& child)
: Registrar(child)
{
}
using Registrar::operator=;
public:
static Namespace getGlobalNamespace(lua_State* L)
{
return Namespace(L);
}
static Namespace getNamespaceFromStack(lua_State* L)
{
return Namespace(L, FromStack{});
}
Namespace beginNamespace(const char* name)
{
assertIsActive();
return Namespace(name, *this);
}
Namespace endNamespace()
{
if (m_stackSize == 1)
{
throw_or_assert<std::logic_error>("endNamespace() called on global namespace");
return Namespace(*this);
}
assert(m_stackSize > 1);
--m_stackSize;
lua_pop(L, 1);
return Namespace(*this);
}
template <class T>
Namespace& addVariable(const char* name, const T& value)
{
if (m_stackSize == 1)
{
throw_or_assert<std::logic_error>("addVariable() called on global namespace");
return *this;
}
assert(name != nullptr);
assert(lua_istable(L, -1));
if constexpr (std::is_enum_v<T>)
{
using U = std::underlying_type_t<T>;
auto result = Stack<U>::push(L, static_cast<U>(value));
if (! result)
luaL_error(L, "%s", result.message().c_str());
}
else
{
auto result = Stack<T>::push(L, value);
if (! result)
luaL_error(L, "%s", result.message().c_str());
}
rawsetfield(L, -2, name);
return *this;
}
template <class T>
Namespace& addProperty(const char* name, T* value, bool isWritable = true)
{
if (m_stackSize == 1)
{
throw_or_assert<std::logic_error>("addProperty() called on global namespace");
return *this;
}
assert(name != nullptr);
assert(lua_istable(L, -1));
lua_pushlightuserdata(L, value);
lua_pushcclosure_x(L, &detail::property_getter<T>::call, 1);
detail::add_property_getter(L, name, -2);
if (isWritable)
{
lua_pushlightuserdata(L, value);
lua_pushcclosure_x(L, &detail::property_setter<T>::call, 1);
}
else
{
lua_pushstring(L, name);
lua_pushcclosure_x(L, &detail::read_only_error, 1);
}
detail::add_property_setter(L, name, -2);
return *this;
}
template <class TG, class TS = TG>
Namespace& addProperty(const char* name, TG (*get)(), void (*set)(TS) = nullptr)
{
if (m_stackSize == 1)
{
throw_or_assert<std::logic_error>("addProperty() called on global namespace");
return *this;
}
assert(name != nullptr);
assert(lua_istable(L, -1));
lua_pushlightuserdata(L, reinterpret_cast<void*>(get));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<TG (*)()>, 1);
detail::add_property_getter(L, name, -2);
if (set != nullptr)
{
lua_pushlightuserdata(L, reinterpret_cast<void*>(set));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<void (*)(TS)>, 1);
}
else
{
lua_pushstring(L, name);
lua_pushcclosure_x(L, &detail::read_only_error, 1);
}
detail::add_property_setter(L, name, -2);
return *this;
}
template <class TG, class TS = TG>
Namespace& addProperty(const char* name, TG (*get)() noexcept, void (*set)(TS) noexcept = nullptr)
{
if (m_stackSize == 1)
{
throw_or_assert<std::logic_error>("addProperty() called on global namespace");
return *this;
}
assert(name != nullptr);
assert(lua_istable(L, -1));
lua_pushlightuserdata(L, reinterpret_cast<void*>(get));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<TG (*)() noexcept>, 1);
detail::add_property_getter(L, name, -2);
if (set != nullptr)
{
lua_pushlightuserdata(L, reinterpret_cast<void*>(set));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<void (*)(TS) noexcept>, 1);
}
else
{
lua_pushstring(L, name);
lua_pushcclosure_x(L, &detail::read_only_error, 1);
}
detail::add_property_setter(L, name, -2);
return *this;
}
template <class Getter, class = std::enable_if_t<!std::is_pointer_v<Getter>>>
Namespace& addProperty(const char* name, Getter get)
{
assert(name != nullptr);
assert(lua_istable(L, -1));
if constexpr (std::is_enum_v<Getter>)
{
using U = std::underlying_type_t<Getter>;
auto enumGet = [get = std::move(get)] { return static_cast<U>(get); };
using GetType = decltype(enumGet);
lua_newuserdata_aligned<GetType>(L, std::move(enumGet));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<GetType>, 1);
}
else
{
using GetType = decltype(get);
lua_newuserdata_aligned<GetType>(L, std::move(get));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<GetType>, 1);
}
detail::add_property_getter(L, name, -2);
lua_pushstring(L, name);
lua_pushcclosure_x(L, &detail::read_only_error, 1);
detail::add_property_setter(L, name, -2);
return *this;
}
template <class Getter, class Setter, class = std::enable_if_t<!std::is_pointer_v<Getter> && !std::is_pointer_v<Setter>>>
Namespace& addProperty(const char* name, Getter get, Setter set)
{
assert(name != nullptr);
assert(lua_istable(L, -1));
addProperty<Getter>(name, std::move(get));
using SetType = decltype(set);
lua_newuserdata_aligned<SetType>(L, std::move(set));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<SetType>, 1);
detail::add_property_setter(L, name, -2);
return *this;
}
Namespace& addProperty(const char* name, lua_CFunction get, lua_CFunction set = nullptr)
{
assert(name != nullptr);
assert(lua_istable(L, -1));
lua_pushcfunction_x(L, get);
detail::add_property_getter(L, name, -2);
if (set != nullptr)
{
lua_pushcfunction_x(L, set);
detail::add_property_setter(L, name, -2);
}
else
{
lua_pushstring(L, name);
lua_pushcclosure_x(L, &detail::read_only_error, 1);
detail::add_property_setter(L, name, -2);
}
return *this;
}
template <class... Functions>
auto addFunction(const char* name, Functions... functions)
-> std::enable_if_t<(detail::is_callable_v<Functions> && ...) && (sizeof...(Functions) > 0), Namespace&>
{
assert(name != nullptr);
assert(lua_istable(L, -1));
if constexpr (sizeof...(Functions) == 1)
{
([&]
{
detail::push_function(L, std::move(functions));
} (), ...);
}
else
{
lua_createtable(L, static_cast<int>(sizeof...(Functions)), 0);
int idx = 1;
([&]
{
lua_createtable(L, 2, 0);
lua_pushinteger(L, 1);
if constexpr (detail::is_any_cfunction_pointer_v<Functions>)
lua_pushinteger(L, -1);
else
lua_pushinteger(L, static_cast<int>(detail::function_arity_excluding_v<Functions, lua_State*>));
lua_settable(L, -3);
lua_pushinteger(L, 2);
detail::push_function(L, std::move(functions));
lua_settable(L, -3);
lua_rawseti(L, -2, idx);
++idx;
} (), ...);
lua_pushcclosure_x(L, &detail::try_overload_functions<false>, 1);
}
rawsetfield(L, -2, name);
return *this;
}
Table beginTable(const char* name)
{
assertIsActive();
return Table(name, *this);
}
template <class T>
Class<T> beginClass(const char* name)
{
assertIsActive();
return Class<T>(name, *this);
}
template <class Derived, class Base>
Class<Derived> deriveClass(const char* name)
{
assertIsActive();
return Class<Derived>(name, *this, detail::getStaticRegistryKey<Base>());
}
};
inline Namespace getGlobalNamespace(lua_State* L)
{
return Namespace::getGlobalNamespace(L);
}
inline Namespace getNamespaceFromStack(lua_State* L)
{
return Namespace::getNamespaceFromStack(L);
}
inline void registerMainThread(lua_State* L)
{
register_main_thread(L);
}
}
// End File: Source/LuaBridge/detail/Namespace.h
// Begin File: Source/LuaBridge/detail/Overload.h
namespace luabridge {
namespace detail {
template <class... Args>
struct non_const_overload
{
template <class R, class T>
constexpr auto operator()(R (T::*ptr)(Args...)) const noexcept -> decltype(ptr)
{
return ptr;
}
};
template <class... Args>
struct const_overload
{
template <class R, class T>
constexpr auto operator()(R (T::*ptr)(Args...) const) const noexcept -> decltype(ptr)
{
return ptr;
}
};
template <class... Args>
struct overload : const_overload<Args...>, non_const_overload<Args...>
{
using const_overload<Args...>::operator();
using non_const_overload<Args...>::operator();
template <class R>
constexpr auto operator()(R (*ptr)(Args...)) const noexcept -> decltype(ptr)
{
return ptr;
}
};
}
template <class... Args> constexpr detail::overload<Args...> overload = {};
template <class... Args> constexpr detail::const_overload<Args...> constOverload = {};
template <class... Args> constexpr detail::non_const_overload<Args...> nonConstOverload = {};
}
// End File: Source/LuaBridge/detail/Overload.h
// Begin File: Source/LuaBridge/detail/ScopeGuard.h
namespace luabridge::detail {
template <class F>
class ScopeGuard
{
public:
template <class V>
ScopeGuard(V&& v)
: m_func(std::forward<V>(v))
, m_shouldRun(true)
{
}
~ScopeGuard()
{
if (m_shouldRun)
m_func();
}
void reset()
{
m_shouldRun = false;
}
private:
F m_func;
bool m_shouldRun;
};
template <class F>
ScopeGuard(F&&) -> ScopeGuard<F>;
}
// End File: Source/LuaBridge/detail/ScopeGuard.h
// Begin File: Source/LuaBridge/LuaBridge.h
#define LUABRIDGE_MAJOR_VERSION 3
#define LUABRIDGE_MINOR_VERSION 1
#define LUABRIDGE_VERSION 301
// End File: Source/LuaBridge/LuaBridge.h
// Begin File: Source/LuaBridge/Map.h
namespace luabridge {
template <class K, class V>
struct Stack<std::map<K, V>>
{
using Type = std::map<K, V>;
[[nodiscard]] static Result push(lua_State* L, const Type& map)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, 0, static_cast<int>(map.size()));
for (auto it = map.begin(); it != map.end(); ++it)
{
auto result = Stack<K>::push(L, it->first);
if (! result)
return result;
result = Stack<V>::push(L, it->second);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type map;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto value = Stack<V>::get(L, -1);
if (! value)
return makeErrorCode(ErrorCode::InvalidTypeCast);
auto key = Stack<K>::get(L, -2);
if (! key)
return makeErrorCode(ErrorCode::InvalidTypeCast);
map.emplace(*key, *value);
lua_pop(L, 1);
}
return map;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
}
// End File: Source/LuaBridge/Map.h
// Begin File: Source/LuaBridge/RefCountedObject.h
namespace luabridge {
template<class CounterType>
class RefCountedObjectType
{
public:
inline void incReferenceCount() const { ++refCount; }
inline void decReferenceCount() const
{
assert(getReferenceCount() > 0);
if (--refCount == 0)
delete this;
}
inline int getReferenceCount() const { return static_cast<int>(refCount); }
protected:
RefCountedObjectType() : refCount() {}
virtual ~RefCountedObjectType()
{
assert(getReferenceCount() == 0);
}
private:
CounterType mutable refCount;
};
typedef RefCountedObjectType<int> RefCountedObject;
template<class ReferenceCountedObjectClass>
class RefCountedObjectPtr
{
public:
typedef ReferenceCountedObjectClass ReferencedType;
inline RefCountedObjectPtr() : referencedObject(0) {}
inline RefCountedObjectPtr(ReferenceCountedObjectClass* const refCountedObject)
: referencedObject(refCountedObject)
{
if (refCountedObject != 0)
refCountedObject->incReferenceCount();
}
inline RefCountedObjectPtr(const RefCountedObjectPtr& other)
: referencedObject(other.referencedObject)
{
if (referencedObject != 0)
referencedObject->incReferenceCount();
}
inline RefCountedObjectPtr(RefCountedObjectPtr&& other)
: referencedObject(other.referencedObject)
{
other.referencedObject = 0;
}
template<class DerivedClass>
inline RefCountedObjectPtr(const RefCountedObjectPtr<DerivedClass>& other)
: referencedObject(static_cast<ReferenceCountedObjectClass*>(other.getObject()))
{
if (referencedObject != 0)
referencedObject->incReferenceCount();
}
RefCountedObjectPtr& operator=(const RefCountedObjectPtr& other)
{
return operator=(other.referencedObject);
}
template<class DerivedClass>
RefCountedObjectPtr& operator=(const RefCountedObjectPtr<DerivedClass>& other)
{
return operator=(static_cast<ReferenceCountedObjectClass*>(other.getObject()));
}
RefCountedObjectPtr& operator=(RefCountedObjectPtr&& other)
{
using std::swap;
swap(referencedObject, other.referencedObject);
return *this;
}
RefCountedObjectPtr& operator=(ReferenceCountedObjectClass* const newObject)
{
if (referencedObject != newObject)
{
if (newObject != 0)
newObject->incReferenceCount();
ReferenceCountedObjectClass* const oldObject = referencedObject;
referencedObject = newObject;
if (oldObject != 0)
oldObject->decReferenceCount();
}
return *this;
}
~RefCountedObjectPtr()
{
if (referencedObject != 0)
referencedObject->decReferenceCount();
}
operator ReferenceCountedObjectClass*() const { return referencedObject; }
ReferenceCountedObjectClass* operator->() const { return referencedObject; }
ReferenceCountedObjectClass* getObject() const { return referencedObject; }
private:
ReferenceCountedObjectClass* referencedObject;
};
template<class ReferenceCountedObjectClass>
bool operator==(const RefCountedObjectPtr<ReferenceCountedObjectClass>& object1,
ReferenceCountedObjectClass* const object2)
{
return object1.getObject() == object2;
}
template<class ReferenceCountedObjectClass>
bool operator==(const RefCountedObjectPtr<ReferenceCountedObjectClass>& object1,
const RefCountedObjectPtr<ReferenceCountedObjectClass>& object2)
{
return object1.getObject() == object2.getObject();
}
template<class ReferenceCountedObjectClass>
bool operator==(ReferenceCountedObjectClass* object1,
RefCountedObjectPtr<ReferenceCountedObjectClass>& object2)
{
return object1 == object2.getObject();
}
template<class ReferenceCountedObjectClass>
bool operator!=(const RefCountedObjectPtr<ReferenceCountedObjectClass>& object1,
const ReferenceCountedObjectClass* object2)
{
return object1.getObject() != object2;
}
template<class ReferenceCountedObjectClass>
bool operator!=(const RefCountedObjectPtr<ReferenceCountedObjectClass>& object1,
RefCountedObjectPtr<ReferenceCountedObjectClass>& object2)
{
return object1.getObject() != object2.getObject();
}
template<class ReferenceCountedObjectClass>
bool operator!=(ReferenceCountedObjectClass* object1,
RefCountedObjectPtr<ReferenceCountedObjectClass>& object2)
{
return object1 != object2.getObject();
}
template<class T>
struct ContainerTraits<RefCountedObjectPtr<T>>
{
using Type = T;
static RefCountedObjectPtr<T> construct(T* c) { return c; }
static T* get(RefCountedObjectPtr<T> const& c) { return c.getObject(); }
};
}
// End File: Source/LuaBridge/RefCountedObject.h
// Begin File: Source/LuaBridge/Set.h
namespace luabridge {
template <class K>
struct Stack<std::set<K>>
{
using Type = std::set<K>;
[[nodiscard]] static Result push(lua_State* L, const Type& set)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, 0, static_cast<int>(set.size()));
auto it = set.cbegin();
for (lua_Integer tableIndex = 1; it != set.cend(); ++tableIndex, ++it)
{
lua_pushinteger(L, tableIndex);
auto result = Stack<K>::push(L, *it);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeUnexpected(makeErrorCode(ErrorCode::InvalidTypeCast));
const StackRestore stackRestore(L);
Type set;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto item = Stack<K>::get(L, -1);
if (! item)
return makeUnexpected(makeErrorCode(ErrorCode::InvalidTypeCast));
set.emplace(*item);
lua_pop(L, 1);
}
return set;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
}
// End File: Source/LuaBridge/Set.h
// Begin File: Source/LuaBridge/UnorderedMap.h
namespace luabridge {
template <class K, class V>
struct Stack<std::unordered_map<K, V>>
{
using Type = std::unordered_map<K, V>;
[[nodiscard]] static Result push(lua_State* L, const Type& map)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, 0, static_cast<int>(map.size()));
for (auto it = map.begin(); it != map.end(); ++it)
{
auto result = Stack<K>::push(L, it->first);
if (! result)
return result;
result = Stack<V>::push(L, it->second);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type map;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto value = Stack<V>::get(L, -1);
if (! value)
return makeErrorCode(ErrorCode::InvalidTypeCast);
auto key = Stack<K>::get(L, -2);
if (! key)
return makeErrorCode(ErrorCode::InvalidTypeCast);
map.emplace(*key, *value);
lua_pop(L, 1);
}
return map;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
}
// End File: Source/LuaBridge/UnorderedMap.h
// Begin File: Source/LuaBridge/Vector.h
namespace luabridge {
template <class T>
struct Stack<std::vector<T>>
{
using Type = std::vector<T>;
[[nodiscard]] static Result push(lua_State* L, const Type& vector)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, static_cast<int>(vector.size()), 0);
for (std::size_t i = 0; i < vector.size(); ++i)
{
lua_pushinteger(L, static_cast<lua_Integer>(i + 1));
auto result = Stack<T>::push(L, vector[i]);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type vector;
vector.reserve(static_cast<std::size_t>(get_length(L, index)));
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto item = Stack<T>::get(L, -1);
if (! item)
return makeErrorCode(ErrorCode::InvalidTypeCast);
vector.emplace_back(*item);
lua_pop(L, 1);
}
return vector;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
}
// End File: Source/LuaBridge/Vector.h
// Begin File: Source/LuaBridge/detail/Dump.h
namespace luabridge {
namespace debug {
inline void putIndent(std::ostream& stream, unsigned level)
{
for (unsigned i = 0; i < level; ++i)
{
stream << " ";
}
}
inline void dumpTable(lua_State* L, int index, std::ostream& stream, unsigned level);
inline void dumpValue(lua_State* L, int index, std::ostream& stream, unsigned level = 0)
{
const int type = lua_type(L, index);
switch (type)
{
case LUA_TNIL:
stream << "nil";
break;
case LUA_TBOOLEAN:
stream << (lua_toboolean(L, index) ? "true" : "false");
break;
case LUA_TNUMBER:
stream << lua_tonumber(L, index);
break;
case LUA_TSTRING:
stream << '"' << lua_tostring(L, index) << '"';
break;
case LUA_TFUNCTION:
if (lua_iscfunction(L, index))
{
stream << "cfunction@" << lua_topointer(L, index);
}
else
{
stream << "function@" << lua_topointer(L, index);
}
break;
case LUA_TTHREAD:
stream << "thread@" << lua_tothread(L, index);
break;
case LUA_TLIGHTUSERDATA:
stream << "lightuserdata@" << lua_touserdata(L, index);
break;
case LUA_TTABLE:
dumpTable(L, index, stream, level);
break;
case LUA_TUSERDATA:
stream << "userdata@" << lua_touserdata(L, index);
break;
default:
stream << lua_typename(L, type);
;
break;
}
}
inline void dumpTable(lua_State* L, int index, std::ostream& stream, unsigned level = 0)
{
stream << "table@" << lua_topointer(L, index);
if (level > 0)
{
return;
}
index = lua_absindex(L, index);
stream << " {";
lua_pushnil(L);
while (lua_next(L, index))
{
stream << "\n";
putIndent(stream, level + 1);
dumpValue(L, -2, stream, level + 1);
stream << ": ";
dumpValue(L, -1, stream, level + 1);
lua_pop(L, 1);
}
putIndent(stream, level);
stream << "\n}";
}
inline void dumpState(lua_State* L, std::ostream& stream = std::cerr)
{
int top = lua_gettop(L);
for (int i = 1; i <= top; ++i)
{
stream << "stack #" << i << ": ";
dumpValue(L, i, stream, 0);
stream << "\n";
}
}
}
}
// End File: Source/LuaBridge/detail/Dump.h
// clang-format on