初始化内容

This commit is contained in:
2026-09-16 14:07:40 +08:00
parent 6934b390bf
commit 4f643c1e7c
18350 changed files with 6088489 additions and 305 deletions
File diff suppressed because it is too large Load Diff
+259
View File
@@ -0,0 +1,259 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2020, Dmitry Tarakanov
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include <cstdint>
#include <memory>
#include <string_view>
#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>(static_cast<uint32_t>(seed ^ static_cast<uint8_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(T* = nullptr) 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(T* = nullptr) noexcept
{
constexpr auto stripped = typeName<T>();
return fnv1a(stripped.data(), stripped.size());
}
//=================================================================================================
/**
* @brief A unique key for the exceptions in the registry.
*/
[[nodiscard]] inline void* getExceptionsKey() noexcept
{
return reinterpret_cast<void*>(0xc7);
}
//=================================================================================================
/**
* @brief A unique key for a type name in a metatable.
*/
[[nodiscard]] inline const void* getTypeKey() noexcept
{
return reinterpret_cast<void*>(0x71);
}
//=================================================================================================
/**
* @brief The key of a const table in another metatable.
*/
[[nodiscard]] inline const void* getConstKey() noexcept
{
return reinterpret_cast<void*>(0xc07);
}
//=================================================================================================
/**
* @brief The key of a class table in another metatable.
*/
[[nodiscard]] inline const void* getClassKey() noexcept
{
return reinterpret_cast<void*>(0xc1a);
}
//=================================================================================================
/**
* @brief The key of a class options table in another metatable.
*/
[[nodiscard]] inline const void* getClassOptionsKey() noexcept
{
return reinterpret_cast<void*>(0xc2b);
}
//=================================================================================================
/**
* @brief The key of a type identity tag in class/const metatables.
*/
[[nodiscard]] inline const void* getTypeIdentityKey() noexcept
{
return reinterpret_cast<void*>(0xc2c);
}
//=================================================================================================
/**
* @brief The key of a propget table in another metatable.
*/
[[nodiscard]] inline const void* getPropgetKey() noexcept
{
return reinterpret_cast<void*>(0x6e7);
}
//=================================================================================================
/**
* @brief The key of a propset table in another metatable.
*/
[[nodiscard]] inline const void* getPropsetKey() noexcept
{
return reinterpret_cast<void*>(0x5e7);
}
//=================================================================================================
/**
* @brief The key of a static table in another metatable.
*/
[[nodiscard]] inline const void* getStaticKey() noexcept
{
return reinterpret_cast<void*>(0x57a);
}
//=================================================================================================
/**
* @brief The key of a parent table in another metatable.
*/
[[nodiscard]] inline const void* getParentKey() noexcept
{
return reinterpret_cast<void*>(0xdad);
}
//=================================================================================================
/**
* @brief The key of a cast offset table in a derived class metatable.
*
* Maps base class registry keys to byte offsets for pointer adjustment when converting
* a derived class pointer to a base class pointer in multiple inheritance scenarios.
*/
[[nodiscard]] inline const void* getCastTableKey() noexcept
{
return reinterpret_cast<void*>(0xca57);
}
//=================================================================================================
/**
* The key of the index fall back in another metatable.
*/
[[nodiscard]] inline const void* getIndexFallbackKey()
{
return reinterpret_cast<void*>(0x81ca);
}
[[nodiscard]] inline const void* getIndexExtensibleKey()
{
return reinterpret_cast<void*>(0x81cb);
}
//=================================================================================================
/**
* The key of the new index fall back in another metatable.
*/
[[nodiscard]] inline const void* getNewIndexFallbackKey()
{
return reinterpret_cast<void*>(0x8107);
}
[[nodiscard]] inline const void* getNewIndexExtensibleKey()
{
return reinterpret_cast<void*>(0x8108);
}
//=================================================================================================
/**
* @brief The key of a ConverterRegistry userdata in a class metatable.
*/
[[nodiscard]] inline const void* getConvertersKey() noexcept
{
return reinterpret_cast<void*>(0xc0de);
}
//=================================================================================================
/**
* The key of the static index fall back in another metatable.
*/
[[nodiscard]] inline const void* getStaticIndexFallbackKey()
{
return reinterpret_cast<void*>(0x81cc);
}
//=================================================================================================
/**
* The key of the static new index fall back in another metatable.
*/
[[nodiscard]] inline const void* getStaticNewIndexFallbackKey()
{
return reinterpret_cast<void*>(0x8109);
}
//=================================================================================================
/**
* @brief Get the key for the static table in the Lua registry.
*
* The static table holds the static data members, static properties, and static member functions for a class.
*/
template <class T>
[[nodiscard]] const void* getStaticRegistryKey() noexcept
{
static auto value = typeHash<T>();
return reinterpret_cast<void*>(value);
}
//=================================================================================================
/**
* @brief Get the key for the class table in the Lua registry.
*
* The class table holds the data members, properties, and member functions of a class. Read-only data and properties, and const
* member functions are also placed here (to save a lookup in the const table).
*/
template <class T>
[[nodiscard]] const void* getClassRegistryKey() noexcept
{
static auto value = typeHash<T>() ^ 1;
return reinterpret_cast<void*>(value);
}
//=================================================================================================
/**
* @brief Get the key for the const table in the Lua registry.
*
* The const table holds read-only data members and properties, and const member functions of a class.
*/
template <class T>
[[nodiscard]] const void* getConstRegistryKey() noexcept
{
static auto value = typeHash<T>() ^ 2;
return reinterpret_cast<void*>(value);
}
} // namespace detail
} // namespace luabridge
+258
View File
@@ -0,0 +1,258 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2020, Dmitry Tarakanov
// Copyright 2019, George Tokmaji
// SPDX-License-Identifier: MIT
#pragma once
#include <cassert>
#if __has_include(<version>)
#include <version>
#endif
#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 __cplusplus >= 202302L || (defined(_MSC_VER) && _HAS_CXX23)
#define LUABRIDGE_CXX23_OR_GREATER 1
#elif __cplusplus >= 202002L || (defined(_MSC_VER) && _HAS_CXX20)
#define LUABRIDGE_CXX20_OR_GREATER 1
#endif
#if defined(LUAU_FASTMATH_BEGIN)
#define LUABRIDGE_ON_LUAU 1
#elif defined(LUAJIT_VERSION)
#define LUABRIDGE_ON_LUAJIT 1
#elif defined(RAVI_OPTION_STRING2)
#define LUABRIDGE_ON_RAVI 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(LUABRIDGE_HAS_EXCEPTIONS)
#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
#endif
#if LUABRIDGE_HAS_EXCEPTIONS
#define LUABRIDGE_IF_EXCEPTIONS(...) __VA_ARGS__
#define LUABRIDGE_IF_NO_EXCEPTIONS(...)
#else
#define LUABRIDGE_IF_EXCEPTIONS(...)
#define LUABRIDGE_IF_NO_EXCEPTIONS(...) __VA_ARGS__
#endif
#if defined(__clang__) || defined(__GNUC__)
#define LUABRIDGE_NO_SANITIZE(x) __attribute__((no_sanitize(x)))
#else
#define LUABRIDGE_NO_SANITIZE(x)
#endif
#if defined(__OBJC__)
#define LUABRIDGE_ON_OBJECTIVE_C 1
#endif
/**
* @brief Enable safe stack checks to avoid lua stack overflow when pushing values on the stack.
*
* @note Default is enabled.
*/
#if !defined(LUABRIDGE_SAFE_STACK_CHECKS)
#define LUABRIDGE_SAFE_STACK_CHECKS 1
#endif
/**
* @brief Enable strict stack conversions to enforce exact type matching when getting values from the stack.
*
* When enabled:
* - `Stack<bool>::get` only accepts `LUA_TBOOLEAN` (nil is not convertible to bool).
* - Integer `Stack` specializations only accept Lua integer values (not floats with integer representation, on Lua 5.3+).
* - `Stack<std::string>::get` only accepts `LUA_TSTRING` (numbers are not coerced to strings).
*
* When disabled (default), a more permissive conversion is used:
* - `Stack<bool>::get` accepts `LUA_TBOOLEAN` and `LUA_TNIL` (nil converts to false).
* - Integer `Stack` specializations accept any `LUA_TNUMBER` that can be represented as the target integer type.
* - `Stack<std::string>::get` accepts `LUA_TSTRING` and `LUA_TNUMBER` (numbers are coerced to strings).
*
* @note Default is disabled.
*/
#if !defined(LUABRIDGE_STRICT_STACK_CONVERSIONS)
#define LUABRIDGE_STRICT_STACK_CONVERSIONS 0
#endif
/**
* @brief Enable safe exception handling when lua is compiled as `C` and exceptions raise during execution of registered `lua_CFunction`.
*
* This is a problem that manifests when exceptions are leaking a CFunction when lua is compiled as `C` because the library will then longjmp
* instead of correctly unwinding the exception into C++ land. If you have exceptions enabled and are compiling lua as `C` and you are getting random
* crashes when invoking CFunctions that throw, you have two options: or you catch exceptions in your CFunction and raise a `lua_error` instead
* or you enable this macro, which will add a safe indirection doing exceptions catching and raising when invoking your registered CFunction.
*
* @warning When enabled, some performance degradation is to be expected when invoking registered `lua_CFunction` through the library.
*
* @note Default is disabled, can only be enabled when `LUABRIDGE_HAS_EXCEPTIONS` is 1.
*/
#if !defined(LUABRIDGE_SAFE_LUA_C_EXCEPTION_HANDLING)
#define LUABRIDGE_SAFE_LUA_C_EXCEPTION_HANDLING 0
#endif
/**
* @brief Control raising when an unregistered class is used.
*
* @note Default is enabled when exceptions are enabled, disabled otherwise.
*/
#if !defined(LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE)
#if LUABRIDGE_HAS_EXCEPTIONS
#define LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE 1
#else
#define LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE 0
#endif
#endif
/**
* @brief Control the assertion mechanism used by the library.
*
* @note By default, assertions are enabled in debug builds and disabled in release builds. Define LUABRIDGE_FORCE_ASSERT_RELEASE to enable assertions even in release builds.
*/
#if !defined(LUABRIDGE_ASSERT)
#if defined(NDEBUG) && !defined(LUABRIDGE_FORCE_ASSERT_RELEASE)
#define LUABRIDGE_ASSERT(expr) ((void)(expr))
#else
#define LUABRIDGE_ASSERT(expr) assert(expr)
#endif
#endif
/**
* @brief Enable C++17 filesystem library support.
*
* Requires C++17 and the filesystem header to be available.
* Define LUABRIDGE_DISABLE_CXX17_FILESYSTEM to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX17_FILESYSTEM)
#if !defined(LUABRIDGE_DISABLE_CXX17_FILESYSTEM) && __has_include(<filesystem>) && defined(__cpp_lib_filesystem)
#define LUABRIDGE_HAS_CXX17_FILESYSTEM 1
#else
#define LUABRIDGE_HAS_CXX17_FILESYSTEM 0
#endif
#endif
/**
* @brief Enable C++17 any library support.
*
* Requires C++17 and the any header to be available.
* Define LUABRIDGE_DISABLE_CXX17_ANY to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX17_ANY)
#if !defined(LUABRIDGE_DISABLE_CXX17_ANY) && __has_include(<any>) && defined(__cpp_lib_any)
#define LUABRIDGE_HAS_CXX17_ANY 1
#else
#define LUABRIDGE_HAS_CXX17_ANY 0
#endif
#endif
/**
* @brief Enable C++20 span library support.
*
* Requires C++20 and the span header to be available.
* Define LUABRIDGE_DISABLE_CXX20_SPAN to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX20_SPAN)
#if !defined(LUABRIDGE_DISABLE_CXX20_SPAN) && LUABRIDGE_CXX20_OR_GREATER && __has_include(<span>) && defined(__cpp_lib_span)
#define LUABRIDGE_HAS_CXX20_SPAN 1
#else
#define LUABRIDGE_HAS_CXX20_SPAN 0
#endif
#endif
/**
* @brief Enable C++20 ranges library support.
*
* Requires C++20 and the ranges header to be available.
* Define LUABRIDGE_DISABLE_CXX20_RANGES to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX20_RANGES)
#if !defined(LUABRIDGE_DISABLE_CXX20_RANGES) && LUABRIDGE_CXX20_OR_GREATER && defined(__cpp_lib_ranges)
#define LUABRIDGE_HAS_CXX20_RANGES 1
#else
#define LUABRIDGE_HAS_CXX20_RANGES 0
#endif
#endif
/**
* @brief Enable C++20 coroutine integration with Lua coroutines.
*
* Requires C++20 and Lua 5.2+ (lua_yieldk). Not supported on Lua 5.1, LuaJIT, or Luau.
* Define LUABRIDGE_DISABLE_CXX20_COROUTINES to force-disable even when C++20 is available.
*/
#if !defined(LUABRIDGE_HAS_CXX20_COROUTINES)
#if !defined(LUABRIDGE_DISABLE_CXX20_COROUTINES) && LUABRIDGE_CXX20_OR_GREATER && !(LUABRIDGE_ON_LUAU || LUABRIDGE_ON_LUAJIT || LUABRIDGE_ON_RAVI || LUA_VERSION_NUM < 502)
#define LUABRIDGE_HAS_CXX20_COROUTINES 1
#else
#define LUABRIDGE_HAS_CXX20_COROUTINES 0
#endif
#endif
/**
* @brief Enable C++23 expected library support.
*
* Requires C++23 and the expected header to be available.
* Define LUABRIDGE_DISABLE_CXX23_EXPECTED to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX23_EXPECTED)
#if !defined(LUABRIDGE_DISABLE_CXX23_EXPECTED) && LUABRIDGE_CXX23_OR_GREATER && __has_include(<expected>) && defined(__cpp_lib_expected)
#define LUABRIDGE_HAS_CXX23_EXPECTED 1
#else
#define LUABRIDGE_HAS_CXX23_EXPECTED 0
#endif
#endif
/**
* @brief Enable C++23 flat containers library support.
*
* Requires C++23 and the flat_map header to be available.
* Define LUABRIDGE_DISABLE_CXX23_FLAT_CONTAINERS to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX23_FLAT_CONTAINERS)
#if !defined(LUABRIDGE_DISABLE_CXX23_FLAT_CONTAINERS) && LUABRIDGE_CXX23_OR_GREATER && __has_include(<flat_map>) && __has_include(<flat_set>) && defined(__cpp_lib_flat_map)
#define LUABRIDGE_HAS_CXX23_FLAT_CONTAINERS 1
#else
#define LUABRIDGE_HAS_CXX23_FLAT_CONTAINERS 0
#endif
#endif
/**
* @brief Enable C++23 move_only_function library support.
*
* Requires C++23 and move_only_function to be available.
* Define LUABRIDGE_DISABLE_CXX23_MOVE_ONLY_FUNCTION to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION)
#if !defined(LUABRIDGE_DISABLE_CXX23_MOVE_ONLY_FUNCTION) && LUABRIDGE_CXX23_OR_GREATER && defined(__cpp_lib_move_only_function)
#define LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION 1
#else
#define LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION 0
#endif
#endif
+183
View File
@@ -0,0 +1,183 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "ClassInfo.h"
#include "Errors.h"
#include "LuaHelpers.h"
#include "Result.h"
#include <memory>
#include <optional>
#include <type_traits>
#include <unordered_map>
#include <utility>
namespace luabridge {
// Forward declaration.
template <class T, class>
struct Stack;
//=================================================================================================
/**
* @brief Opt-in trait for enabling custom type converters for type T.
*
* Specialize with enabled = true so that Stack<T>::get consults the metatable
* converter registry as a Phase 3 fallback after Phase 1 (exact match) and
* Phase 2 (inheritance) both fail.
*
* Example:
* template <> struct luabridge::StackConversion<MyType> { static constexpr bool enabled = true; };
*/
template <class T>
struct StackConversion
{
static constexpr bool enabled = false;
};
//=================================================================================================
/**
* @brief User-defined conversion hook from source type From to target type To.
*
* Specialize this template for each (To, From) pair and provide:
* static To convert(const From& from);
*
* Example:
* template <>
* struct luabridge::StackConverter<Vec3, glm::vec3> {
* static Vec3 convert(const glm::vec3& v) { return {v.x, v.y, v.z}; }
* };
*/
template <class To, class From>
struct StackConverter;
//=================================================================================================
namespace detail {
struct ConverterRegistry
{
std::unordered_map<const void*, const void*> converters;
};
inline ConverterRegistry* getOrCreateConverterRegistry(lua_State* L, int metatableIdx)
{
const int absIdx = lua_absindex(L, metatableIdx);
lua_rawgetp_x(L, absIdx, getConvertersKey());
if (lua_isuserdata(L, -1) && !lua_islightuserdata(L, -1))
{
auto* reg = align<ConverterRegistry>(lua_touserdata(L, -1));
lua_pop(L, 1);
return reg;
}
lua_pop(L, 1); // pop nil or unexpected value
// Create ConverterRegistry as an aligned Lua full userdata with automatic __gc
lua_newuserdata_aligned<ConverterRegistry>(L);
auto* reg = align<ConverterRegistry>(lua_touserdata(L, -1));
// Store the userdata in the class metatable
lua_pushvalue(L, -1); // dup userdata
lua_rawsetp_x(L, absIdx, getConvertersKey()); // store, pops dup
lua_pop(L, 1); // pop the original userdata
return reg;
}
template <class T>
class ConverterConstRef
{
public:
explicit ConverterConstRef(const T& value) noexcept
: m_ref(std::addressof(value))
{
}
explicit ConverterConstRef(T&& value) noexcept(std::is_nothrow_move_constructible_v<T>)
: m_value(std::move(value))
, m_ref(std::addressof(*m_value))
{
}
ConverterConstRef(ConverterConstRef&& other) noexcept(std::is_nothrow_move_constructible_v<T>)
: m_value(std::move(other.m_value))
, m_ref(m_value.has_value() ? std::addressof(*m_value) : other.m_ref)
{
}
ConverterConstRef(const ConverterConstRef& other)
: m_value(other.m_value)
, m_ref(m_value.has_value() ? std::addressof(*m_value) : other.m_ref)
{
}
ConverterConstRef& operator=(ConverterConstRef&& other) noexcept(std::is_nothrow_move_assignable_v<T>)
{
m_value = std::move(other.m_value);
m_ref = m_value.has_value() ? std::addressof(*m_value) : other.m_ref;
return *this;
}
ConverterConstRef& operator=(const ConverterConstRef& other)
{
m_value = other.m_value;
m_ref = m_value.has_value() ? std::addressof(*m_value) : other.m_ref;
return *this;
}
operator const T&() const noexcept
{
return *m_ref;
}
private:
std::optional<T> m_value;
const T* m_ref = nullptr;
};
template <class To>
TypeResult<To> tryConvertFromRegisteredConverter(lua_State* L, int index)
{
using FnType = TypeResult<To>(*)(lua_State*, int);
if (! lua_getmetatable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
lua_rawgetp_x(L, -1, detail::getConvertersKey());
if (lua_isuserdata(L, -1) && !lua_islightuserdata(L, -1))
{
auto* reg = align<detail::ConverterRegistry>(lua_touserdata(L, -1));
lua_pop(L, 2); // registry userdata + metatable
auto it = reg->converters.find(detail::getClassRegistryKey<To>());
if (it != reg->converters.end() && it->second)
{
const auto* fn = static_cast<const FnType*>(it->second);
return (*fn)(L, index);
}
}
else
{
lua_pop(L, 2); // nil/other + metatable
}
return makeErrorCode(ErrorCode::InvalidTypeCast);
}
template <class To, class From>
TypeResult<To> convertFromStack(lua_State* L, int index)
{
auto result = detail::Userdata::get<From>(L, index, true);
if (!result || !*result)
return makeErrorCode(ErrorCode::InvalidTypeCast);
return StackConverter<To, From>::convert(**result);
}
} // namespace detail
} // namespace luabridge
+492
View File
@@ -0,0 +1,492 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "CFunctions.h"
#include "Errors.h"
#include "LuaHelpers.h"
#include "Stack.h"
#if LUABRIDGE_HAS_CXX20_COROUTINES
#if LUABRIDGE_ON_LUAJIT || LUA_VERSION_NUM == 501 || LUABRIDGE_ON_LUAU
#ifndef LUABRIDGE_DISABLE_COROUTINE_INTEGRATION
#error "C++20 coroutine integration requires Lua 5.2+ with lua_yieldk support. Define LUABRIDGE_DISABLE_COROUTINE_INTEGRATION to suppress this error."
#endif
#else
#include <coroutine>
#include <exception>
#include <type_traits>
#include <utility>
namespace luabridge {
//=================================================================================================
/**
* @brief A C++20 coroutine type callable from Lua.
*
* Register instances via Namespace::addCoroutine(). When called from Lua, the coroutine body
* runs until the first co_yield (which yields a value back to Lua) or co_return (which
* returns a final value). Subsequent Lua resumes continue the body from the last suspension point.
*
* @tparam R The type yielded/returned by the coroutine. May be void.
*
* Example:
* @code
* luabridge::getGlobalNamespace(L)
* .addCoroutine("range", [](int start, int stop) -> luabridge::CppCoroutine<int> {
* for (int i = start; i < stop; ++i)
* co_yield i;
* co_return -1;
* });
* @endcode
*
* @note Requires Lua 5.2+ (lua_yieldk). Not supported on Lua 5.1, LuaJIT, or Luau.
* @note Not thread-safe. Must be driven from a single OS thread.
*/
template <class R>
struct CppCoroutine
{
struct promise_type
{
lua_State* L = nullptr;
int nresults = 0;
bool is_done = false;
std::exception_ptr exception;
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() noexcept
{
exception = std::current_exception();
}
std::suspend_always yield_value(const R& value)
{
nresults = 0;
if (L)
{
auto result = Stack<R>::push(L, value);
if (result)
nresults = 1;
else
exception = std::make_exception_ptr(std::system_error(result.error()));
}
return {};
}
std::suspend_always yield_value(R&& value)
{
nresults = 0;
if (L)
{
auto result = Stack<R>::push(L, std::move(value));
if (result)
nresults = 1;
else
exception = std::make_exception_ptr(std::system_error(result.error()));
}
return {};
}
void return_value(const R& value)
{
nresults = 0;
if (L)
{
auto result = Stack<R>::push(L, value);
if (result)
nresults = 1;
else
exception = std::make_exception_ptr(std::system_error(result.error()));
}
is_done = true;
}
void return_value(R&& value)
{
nresults = 0;
if (L)
{
auto result = Stack<R>::push(L, std::move(value));
if (result)
nresults = 1;
else
exception = std::make_exception_ptr(std::system_error(result.error()));
}
is_done = true;
}
CppCoroutine get_return_object()
{
return CppCoroutine{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
};
std::coroutine_handle<promise_type> handle;
explicit CppCoroutine(std::coroutine_handle<promise_type> h) noexcept
: handle(h)
{
}
CppCoroutine(CppCoroutine&& other) noexcept
: handle(std::exchange(other.handle, {}))
{
}
CppCoroutine(const CppCoroutine&) = delete;
CppCoroutine& operator=(const CppCoroutine&) = delete;
~CppCoroutine() = default;
};
//=================================================================================================
/**
* @brief Specialisation for void-returning coroutines.
*/
template <>
struct CppCoroutine<void>
{
struct promise_type
{
lua_State* L = nullptr;
int nresults = 0;
bool is_done = false;
std::exception_ptr exception;
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() noexcept
{
exception = std::current_exception();
}
void return_void()
{
nresults = 0;
is_done = true;
}
CppCoroutine get_return_object()
{
return CppCoroutine{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
};
std::coroutine_handle<promise_type> handle;
explicit CppCoroutine(std::coroutine_handle<promise_type> h) noexcept
: handle(h)
{
}
CppCoroutine(CppCoroutine&& other) noexcept
: handle(std::exchange(other.handle, {}))
{
}
CppCoroutine(const CppCoroutine&) = delete;
CppCoroutine& operator=(const CppCoroutine&) = delete;
~CppCoroutine() = default;
};
//=================================================================================================
/**
* @brief An awaitable wrapper around a Lua coroutine thread.
*
* Use inside a CppCoroutine body to synchronously resume a child Lua thread and obtain
* the number of values it left on its stack (either from yield or return).
*
* @note Runs the Lua thread synchronously (no external event loop required).
*/
class LuaCoroutine
{
public:
LuaCoroutine(lua_State* thread, lua_State* from = nullptr) noexcept
: m_thread(thread)
, m_from(from)
{
}
bool await_ready() noexcept
{
m_status = lua_resume_x(m_thread, m_from, 0, &m_nresults);
return true; // Always ready: runs synchronously
}
void await_suspend(std::coroutine_handle<>) noexcept
{
// Never called because await_ready always returns true
}
/**
* @returns {status, nresults} where status is LUA_OK or LUA_YIELD,
* and nresults is the number of values on the thread's stack.
*/
std::pair<int, int> await_resume() noexcept
{
return { m_status, m_nresults };
}
private:
lua_State* m_thread;
lua_State* m_from;
int m_status = LUABRIDGE_LUA_OK;
int m_nresults = 0;
};
//=================================================================================================
namespace detail {
/**
* @brief Trait: is T a CppCoroutine<R> specialisation?
*/
template <class T>
struct is_cpp_coroutine : std::false_type
{
};
template <class R>
struct is_cpp_coroutine<CppCoroutine<R>> : std::true_type
{
};
/**
* @brief Trait: does callable F return a CppCoroutine<R>?
*/
template <class F, class = void>
struct is_cpp_coroutine_factory : std::false_type
{
};
template <class F>
struct is_cpp_coroutine_factory<F, std::void_t<typename function_traits<std::remove_reference_t<F>>::result_type>>
: is_cpp_coroutine<typename function_traits<std::remove_reference_t<F>>::result_type>
{
};
template <class F>
inline constexpr bool is_cpp_coroutine_factory_v = is_cpp_coroutine_factory<F>::value;
//=================================================================================================
/**
* @brief RAII frame for a suspended CppCoroutine, stored as a Lua full userdata.
*
* Kept alive on the Lua thread's own stack (not in the registry) so that abandoning the
* coroutine — i.e. letting the Lua thread be collected by the GC — automatically triggers
* the __gc metamethod, which calls the destructor and destroys the coroutine handle.
*/
template <class CoroType>
struct CppCoroutineFrame
{
using HandleType = std::coroutine_handle<typename CoroType::promise_type>;
HandleType handle;
explicit CppCoroutineFrame(HandleType h) noexcept
: handle(h)
{
}
CppCoroutineFrame(const CppCoroutineFrame&) = delete;
CppCoroutineFrame& operator=(const CppCoroutineFrame&) = delete;
~CppCoroutineFrame()
{
if (handle && !handle.done())
handle.destroy();
}
};
//=================================================================================================
// Version-portable yield helpers.
//
// Lua 5.2: lua_yieldk ctx is int; continuation signature is (lua_State*, int) — ctx
// retrieved inside via lua_getctx().
// Lua 5.3+: lua_yieldk ctx is lua_KContext; continuation signature is
// (lua_State*, int, lua_KContext) — ctx passed directly.
// Forward declarations
template <class F> int coroutine_continuation_body(lua_State* L, int frame_abs_idx);
#if LUA_VERSION_NUM < 503
// Lua 5.2: lua_yieldk takes lua_CFunction (int(*)(lua_State*)) as continuation.
// The context is recovered inside via lua_getctx().
template <class F>
int coroutine_continuation(lua_State* L)
{
int frame_abs_idx = 0;
lua_getctx(L, &frame_abs_idx);
return coroutine_continuation_body<F>(L, frame_abs_idx);
}
template <class F>
int do_yield(lua_State* L, int nresults, int frame_abs_idx)
{
return lua_yieldk(L, nresults, frame_abs_idx, &coroutine_continuation<F>);
}
#else
// Lua 5.3+: continuation receives lua_KContext directly.
template <class F>
int coroutine_continuation(lua_State* L, int /*status*/, lua_KContext ctx)
{
return coroutine_continuation_body<F>(L, static_cast<int>(ctx));
}
template <class F>
int do_yield(lua_State* L, int nresults, int frame_abs_idx)
{
return lua_yieldk(L, nresults, static_cast<lua_KContext>(frame_abs_idx), &coroutine_continuation<F>);
}
#endif
//=================================================================================================
/**
* @brief Raises a Lua error from a stored C++ exception (or a generic message).
* Removes the frame userdata from the stack before raising so GC can collect it.
*/
[[noreturn]] inline void raise_from_exception(lua_State* L, int frame_abs_idx, std::exception_ptr ex)
{
lua_settop(L, frame_abs_idx - 1); // pop frame (and any value above it) — GC will collect it
#if LUABRIDGE_HAS_EXCEPTIONS
try
{
std::rethrow_exception(ex);
}
catch (const std::exception& e)
{
raise_lua_error(L, "%s", e.what());
}
catch (...)
{
#endif
raise_lua_error(L, "unknown exception in C++ coroutine");
#if LUABRIDGE_HAS_EXCEPTIONS
}
#endif
}
//=================================================================================================
/**
* @brief Common body for the coroutine continuation: resumes the C++ coroutine handle
* and either yields again or returns the final result.
*
* @param frame_abs_idx Absolute stack index where the CppCoroutineFrame userdata lives.
* Any resume arguments pushed above it are discarded first.
*/
template <class F>
int coroutine_continuation_body(lua_State* L, int frame_abs_idx)
{
using CoroType = typename function_traits<std::remove_reference_t<F>>::result_type;
using FrameType = CppCoroutineFrame<CoroType>;
// Discard resume arguments pushed above the frame (we don't expose them to C++ yet)
lua_settop(L, frame_abs_idx);
// Recover the frame from its stable stack position
auto* frame = align<FrameType>(lua_touserdata(L, frame_abs_idx));
// Resume the C++ coroutine body; yield_value/return_value will push at frame_abs_idx+1
frame->handle.resume();
auto& promise = frame->handle.promise();
if (promise.exception)
raise_from_exception(L, frame_abs_idx, promise.exception);
if (promise.is_done)
{
if (promise.nresults == 1)
lua_replace(L, frame_abs_idx); // swap return value into frame slot; pops frame userdata
else
lua_settop(L, frame_abs_idx - 1); // void: remove frame entirely
return promise.nresults;
}
// yield_value pushed one value above the frame; yield it, keeping frame below
return do_yield<F>(L, promise.nresults, frame_abs_idx);
}
//=================================================================================================
/**
* @brief lua_CFunction entry point for a registered CppCoroutine factory.
*
* Upvalue 1: the factory functor F (as aligned full userdata).
*
* The CppCoroutineFrame userdata is left on the Lua thread's own stack (not in the registry).
* This means an abandoned coroutine is naturally cleaned up when the Lua thread is GC'd.
*/
template <class F>
int invoke_coroutine_entry(lua_State* L)
{
using FnTraits = function_traits<std::remove_reference_t<F>>;
using ArgsPack = typename FnTraits::argument_types;
using CoroType = typename FnTraits::result_type;
using FrameType = CppCoroutineFrame<CoroType>;
LUABRIDGE_ASSERT(isfulluserdata(L, lua_upvalueindex(1)));
auto& factory = *align<F>(lua_touserdata(L, lua_upvalueindex(1)));
// Invoke the factory to create the coroutine object.
// The coroutine body does not run yet (initial_suspend returns suspend_always).
auto coro = invoke_callable_from_stack<ArgsPack, 1>(L, factory);
// Push the frame as a Lua full userdata and remember its absolute stack position.
// It is NOT pinned in the registry; keeping it on the thread's stack means GC will
// collect it (via __gc) when the Lua thread is abandoned.
lua_newuserdata_aligned<FrameType>(L, std::move(coro.handle));
coro.handle = {}; // ownership transferred to frame
int frame_abs_idx = lua_gettop(L);
auto* frame = align<FrameType>(lua_touserdata(L, frame_abs_idx));
// Give the promise access to the Lua state so yield_value/return_value can push values
frame->handle.promise().L = L;
// First resume: runs the body to the first co_yield or co_return
frame->handle.resume();
auto& promise = frame->handle.promise();
if (promise.exception)
raise_from_exception(L, frame_abs_idx, promise.exception);
if (promise.is_done)
{
if (promise.nresults == 1)
lua_replace(L, frame_abs_idx); // swap return value into frame slot
else
lua_settop(L, frame_abs_idx - 1); // void: remove frame
return promise.nresults;
}
// yield_value pushed one value above the frame; yield it, keeping frame below
return do_yield<F>(L, promise.nresults, frame_abs_idx);
}
//=================================================================================================
/**
* @brief Pushes a CppCoroutine factory as a Lua closure onto the stack.
*/
template <class F, class = std::enable_if_t<is_cpp_coroutine_factory_v<F>>>
inline void push_coroutine_function(lua_State* L, F&& f, const char* debugname)
{
using FDecay = std::decay_t<F>;
lua_newuserdata_aligned<FDecay>(L, std::forward<F>(f));
lua_pushcclosure_x(L, &invoke_coroutine_entry<FDecay>, debugname, 1);
}
} // namespace detail
} // namespace luabridge
#endif // !Lua 5.1 / LuaJIT / Luau
#endif // LUABRIDGE_HAS_CXX20_COROUTINES
+67
View File
@@ -0,0 +1,67 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2023, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "Errors.h"
#include "LuaHelpers.h"
#include "Stack.h"
#include <type_traits>
namespace luabridge {
//=================================================================================================
/**
* @brief LuaBridge enum wrapper for enums as integers.
*
* An enum exposed with this class will just be decayed to lua as integer. It's responsibility of
* the developer to make sure that a lua integer could be converted back to C++. Failing to validate a lua
* integer before converting to the corresponding C++ enum value could lead to a C++ enum that has no defined value.
*
* For improved security, specify which values the enum will have, so runtime validation could be performed.
*/
template <class T, T... Values>
struct Enum
{
static_assert(std::is_enum_v<T>);
using Type = std::underlying_type_t<T>;
[[nodiscard]] static Result push(lua_State* L, T value)
{
return Stack<Type>::push(L, static_cast<Type>(value));
}
[[nodiscard]] static TypeResult<T> get(lua_State* L, int index)
{
const auto result = Stack<Type>::get(L, index);
if (! result)
return result.error();
if constexpr (sizeof...(Values) > 0)
{
constexpr Type values[] = { static_cast<Type>(Values)... };
for (std::size_t i = 0; i < sizeof...(Values); ++i)
{
if (values[i] == *result)
return static_cast<T>(*result);
}
return makeErrorCode(ErrorCode::InvalidTypeCast);
}
else
{
return static_cast<T>(*result);
}
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_type(L, index) == LUA_TNUMBER;
}
};
} // namespace luabridge
+123
View File
@@ -0,0 +1,123 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2021, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include <system_error>
namespace luabridge {
//=================================================================================================
namespace detail {
static inline constexpr char error_lua_stack_overflow[] = "stack overflow";
} // namespace detail
//=================================================================================================
/**
* @brief LuaBridge error codes.
*/
enum class ErrorCode
{
ClassNotRegistered = 1,
LuaStackOverflow,
LuaFunctionCallFailed,
IntegerDoesntFitIntoLuaInteger,
FloatingPointDoesntFitIntoLuaNumber,
InvalidTypeCast,
InvalidTableSizeInCast,
CoroutineYieldFromNonCoroutine,
CoroutineAlreadyDone
};
//=================================================================================================
namespace detail {
struct ErrorCategory : std::error_category
{
const char* name() const noexcept override
{
return "luabridge";
}
std::string message(int ev) const override
{
return errorString(ev);
}
static const char* errorString(int ev) noexcept
{
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 cast to desired type";
case ErrorCode::InvalidTableSizeInCast:
return "The lua table has different size than expected";
case ErrorCode::CoroutineYieldFromNonCoroutine:
return "Cannot yield from a non-coroutine Lua state";
case ErrorCode::CoroutineAlreadyDone:
return "The Lua coroutine has already finished execution";
default:
return "Unknown error";
}
}
static const ErrorCategory& getInstance() noexcept
{
static ErrorCategory category;
return category;
}
};
} // namespace detail
//=================================================================================================
/**
* @brief Construct an error code from the error enum.
*/
inline std::error_code makeErrorCode(ErrorCode e)
{
return { static_cast<int>(e), detail::ErrorCategory::getInstance() };
}
/**
* @brief Supports std::error_code construction.
*/
inline std::error_code make_error_code(ErrorCode e)
{
return { static_cast<int>(e), detail::ErrorCategory::getInstance() };
}
} // namespace luabridge
namespace std {
template <> struct is_error_code_enum<luabridge::ErrorCode> : true_type {};
} // namespace std
File diff suppressed because it is too large Load Diff
+124
View File
@@ -0,0 +1,124 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2023, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include <type_traits>
#include <algorithm>
#include <string>
namespace luabridge {
template <class T, class... Ts>
class FlagSet
{
static_assert(std::is_integral_v<T>);
public:
constexpr FlagSet() noexcept = default;
constexpr void set(FlagSet other) noexcept
{
flags |= other.flags;
}
constexpr FlagSet withSet(FlagSet other) const noexcept
{
FlagSet result { flags };
result.flags |= other.flags;
return result;
}
constexpr void unset(FlagSet other) noexcept
{
flags &= ~other.flags;
}
constexpr FlagSet withUnset(FlagSet other) const noexcept
{
FlagSet result { flags };
result.flags &= ~other.flags;
return result;
}
constexpr bool test(FlagSet other) const noexcept
{
return (flags & other.flags) != 0;
}
constexpr FlagSet operator|(FlagSet other) const noexcept
{
return FlagSet(flags | other.flags);
}
constexpr FlagSet operator&(FlagSet other) const noexcept
{
return FlagSet(flags & other.flags);
}
constexpr FlagSet operator~() const noexcept
{
return FlagSet(~flags);
}
constexpr T toUnderlying() const noexcept
{
return flags;
}
std::string toString() const
{
std::string result;
result.reserve(sizeof(T) * std::numeric_limits<uint8_t>::digits);
(result.append((mask<Ts>() & flags) ? "1" : "0"), ...);
for (std::size_t i = sizeof...(Ts); i < sizeof(T) * std::numeric_limits<uint8_t>::digits; ++i)
result.append("0");
std::reverse(result.begin(), result.end());
return result;
}
template <class... Us>
static constexpr FlagSet Value() noexcept
{
return FlagSet{ mask<Us...>() };
}
template <class U>
static constexpr auto fromUnderlying(U newFlags) noexcept
-> std::enable_if_t<std::is_integral_v<U> && std::is_convertible_v<U, T>, FlagSet>
{
return { static_cast<T>(newFlags) };
}
private:
template <class U, class V, class... Us>
static constexpr T indexOf() noexcept
{
if constexpr (std::is_same_v<U, V>)
return static_cast<T>(0);
else
return static_cast<T>(1) + indexOf<U, Us...>();
}
template <class... Us>
static constexpr T mask() noexcept
{
return ((static_cast<T>(1) << indexOf<Us, Ts...>()) | ...);
}
constexpr FlagSet(T flags) noexcept
: flags(flags)
{
}
T flags = 0;
};
} // namespace luabridge
+872
View File
@@ -0,0 +1,872 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2020, Dmitry Tarakanov
// Copyright 2019, George Tokmaji
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include <functional>
#include <tuple>
namespace luabridge {
namespace detail {
//=================================================================================================
/**
* @brief Invokes undefined behavior when an unreachable part of the code is reached.
*
* An implementation may use this to optimize impossible code branches away (typically, in optimized builds) or to trap them to prevent
* further execution (typically, in debug builds).
*/
[[noreturn]] inline void unreachable()
{
#if defined(__GNUC__) // GCC, Clang, ICC
__builtin_unreachable();
#elif defined(_MSC_VER) // MSVC
__assume(false);
#endif
}
//=================================================================================================
/**
* @brief Provides the member typedef type which is the type referred to by T with its topmost cv-qualifiers removed.
*/
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;
//=================================================================================================
/**
* @brief Generic function traits.
*
* @tparam IsMember True if the function is a member function pointer.
* @tparam IsConst True if the function is const.
* @tparam R Return type of the function.
* @tparam Args Arguments types as variadic parameter pack.
*/
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, bool Enable>
struct function_traits_impl;
template <class R, class... Args>
struct function_traits_impl<R(Args...), true> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (*)(Args...), true> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (C::*)(Args...), true> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (C::*)(Args...) const, true> : function_traits_base<true, true, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R(Args...) noexcept, true> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (*)(Args...) noexcept, true> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (C::*)(Args...) noexcept, true> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (C::*)(Args...) const noexcept, true> : function_traits_base<true, true, R, Args...>
{
};
#if defined(_MSC_VER) && defined(_M_IX86) // Windows: WINAPI (a.k.a. __stdcall) function pointers (32bit only).
inline static constexpr bool is_stdcall_default_calling_convention = std::is_same_v<void __stdcall(), void()>;
inline static constexpr bool is_fastcall_default_calling_convention = std::is_same_v<void __fastcall(), void()>;
template <class R, class... Args>
struct function_traits_impl<R __stdcall(Args...), !is_stdcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (__stdcall *)(Args...), !is_stdcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__stdcall C::*)(Args...), true> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__stdcall C::*)(Args...) const, true> : function_traits_base<true, true, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R __stdcall(Args...) noexcept, !is_stdcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (__stdcall *)(Args...) noexcept, !is_stdcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__stdcall C::*)(Args...) noexcept, true> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__stdcall C::*)(Args...) const noexcept, true> : function_traits_base<true, true, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R __fastcall(Args...), !is_fastcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (__fastcall *)(Args...), !is_fastcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__fastcall C::*)(Args...), true> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__fastcall C::*)(Args...) const, true> : function_traits_base<true, true, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R __fastcall(Args...) noexcept, !is_fastcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (__fastcall *)(Args...) noexcept, !is_fastcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__fastcall C::*)(Args...) noexcept, true> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__fastcall C::*)(Args...) const noexcept, true> : function_traits_base<true, true, R, Args...>
{
};
#endif
template <class F, class = void>
struct has_call_operator : std::false_type
{
};
template <class F>
struct has_call_operator<F, std::void_t<decltype(&F::operator())>> : std::true_type
{
};
template <class F>
inline static constexpr bool has_call_operator_v = has_call_operator<F>::value;
template <class F>
struct is_move_only_function : std::false_type {};
#if LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION
template <class R, class... Args> struct is_move_only_function<std::move_only_function<R(Args...)>> : std::true_type {};
template <class R, class... Args> struct is_move_only_function<std::move_only_function<R(Args...) noexcept>> : std::true_type {};
template <class R, class... Args> struct is_move_only_function<std::move_only_function<R(Args...) const>> : std::true_type {};
template <class R, class... Args> struct is_move_only_function<std::move_only_function<R(Args...) const noexcept>> : std::true_type {};
#endif
template <class F>
inline static constexpr bool is_move_only_function_v = is_move_only_function<F>::value;
template <class F, class = void>
struct functor_traits_impl
{
};
template <class F>
struct functor_traits_impl<F, std::enable_if_t<has_call_operator_v<F>>> : function_traits_impl<decltype(&F::operator()), true>
{
};
template <class F>
struct functor_traits_impl<F, std::enable_if_t<!has_call_operator_v<F> && std::is_invocable_v<F&> && !is_move_only_function_v<F>>>
: function_traits_base<false, false, std::invoke_result_t<F&>>
{
};
#if LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION
template <class R, class... Args>
struct functor_traits_impl<std::move_only_function<R(Args...)>>
: function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct functor_traits_impl<std::move_only_function<R(Args...) noexcept>>
: function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct functor_traits_impl<std::move_only_function<R(Args...) const>>
: function_traits_base<false, true, R, Args...>
{
};
template <class R, class... Args>
struct functor_traits_impl<std::move_only_function<R(Args...) const noexcept>>
: function_traits_base<false, true, R, Args...>
{
};
#endif // LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION
//=================================================================================================
/**
* @brief Traits class for callable objects (e.g. function pointers, lambdas)
*
* @tparam F Callable object.
*/
template <class F>
struct function_traits : std::conditional_t<std::is_class_v<F>,
detail::functor_traits_impl<F>,
detail::function_traits_impl<F, true>>
{
};
template <class T, bool IsClass = std::is_class_v<T>, class = void>
struct has_function_traits : std::false_type
{
};
template <class T>
struct has_function_traits<T, true, std::void_t<typename function_traits<T>::result_type, typename function_traits<T>::argument_types>> : std::true_type
{
};
template <class T>
inline static constexpr bool has_function_traits_v = has_function_traits<T>::value;
//=================================================================================================
/**
* @brief Deduces the argument type of a callble object or void in case it has no argument.
*
* @tparam I Argument index.
* @tparam F Callable object.
*/
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;
//=================================================================================================
/**
* @brief Deduces the return type of a callble object.
*
* @tparam F Callable object.
*/
template <class F>
using function_result_t = typename function_traits<F>::result_type;
/**
* @brief Deduces the argument type of a callble object.
*
* @tparam I Argument index.
* @tparam F Callable object.
*/
template <std::size_t I, class F>
using function_argument_t = std::tuple_element_t<I, typename function_traits<F>::argument_types>;
/**
* @brief Deduces the arguments type of a callble object.
*
* @tparam F Callable object.
*/
template <class F>
using function_arguments_t = typename function_traits<F>::argument_types;
/**
* @brief An integral constant expression that gives the number of arguments accepted by the callable object.
*
* @tparam F Callable object.
*/
template <class F>
static constexpr std::size_t function_arity_v = function_traits<F>::arity;
/**
* @brief An boolean constant expression that checks if the callable object is a member function.
*
* @tparam F Callable object.
*/
template <class F>
static constexpr bool function_is_member_v = function_traits<F>::is_member;
/**
* @brief An boolean constant expression that checks if the callable object is const.
*
* @tparam F Callable object.
*/
template <class F>
static constexpr bool function_is_const_v = function_traits<F>::is_const;
//=================================================================================================
/**
* @brief Detect if we T is a callable object.
*
* @tparam T Potentially callable object.
*/
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_class_v<T> && !has_call_operator_v<T> && has_function_traits_v<T>>>
{
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;
//=================================================================================================
/**
* @brief Detect if we T is a const member function pointer.
*
* @tparam T Potentially const member function pointer.
*/
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;
//=================================================================================================
/**
* @brief Detect if T is a lua cfunction pointer.
*
* @tparam T Potentially lua cfunction pointer.
*/
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;
//=================================================================================================
/**
* @brief Detect if T is a member lua cfunction pointer.
*
* @tparam T Potentially member lua cfunction pointer.
*/
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;
/**
* @brief Detect if T is a const member lua cfunction pointer.
*
* @tparam T Potentially const member lua cfunction pointer.
*/
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;
//=================================================================================================
/**
* @brief Detect if T is a member or non member lua cfunction pointer.
*
* @tparam T Potentially member or non member lua cfunction pointer.
*/
template <class T>
inline static constexpr bool is_any_cfunction_pointer_v = is_cfunction_pointer_v<T> || is_member_cfunction_pointer_v<T>;
//=================================================================================================
/**
* @brief A constexpr check for proxy_member functions.
*
* @tparam T Type where the callable should be able to operate.
* @tparam F Callable object.
*/
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_reference_t<std::remove_pointer_t<function_argument_or_void_t<0, F>>>>;
//=================================================================================================
/**
* @brief An integral constant expression that gives the number of arguments excluding one type (usually used with lua_State*) accepted by the callable object.
*
* @tparam F Callable object.
*/
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;
/**
* @brief An integral constant expression that gives the number of arguments excluding one type (usually used with lua_State*) accepted by the callable object.
*
* @tparam F Callable object.
*/
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;
//=================================================================================================
/**
* @brief Detectors for const and non const functions in packs and counting them.
*/
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));
//=================================================================================================
/**
* @brief Simple make_tuple alternative that doesn't decay the types.
*
* @tparam Types Argument types that will compose the tuple.
*/
template <class... Types>
constexpr auto tupleize(Types&&... types)
{
return std::tuple<Types...>(std::forward<Types>(types)...);
}
//=================================================================================================
/**
* @brief Remove first type from tuple.
*/
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;
//=================================================================================================
/**
* @brief Drop the first N types from a tuple.
*/
template <std::size_t N, class Tuple>
struct tuple_drop_first
{
using type = typename tuple_drop_first<N - 1, remove_first_type_t<Tuple>>::type;
};
template <class Tuple>
struct tuple_drop_first<0, Tuple>
{
using type = Tuple;
};
template <std::size_t N, class Tuple>
using tuple_drop_first_t = typename tuple_drop_first<N, Tuple>::type;
//=================================================================================================
/**
* @brief Prepend a type to a tuple.
*/
template <class T, class Tuple>
struct tuple_prepend;
template <class T, class... Ts>
struct tuple_prepend<T, std::tuple<Ts...>>
{
using type = std::tuple<T, Ts...>;
};
template <class T, class Tuple>
using tuple_prepend_t = typename tuple_prepend<T, Tuple>::type;
//=================================================================================================
/**
* @brief Take only the first N types from a tuple (uses an accumulator to avoid ambiguity).
*/
template <std::size_t N, class Tuple, class Accum = std::tuple<>>
struct tuple_take_first_impl
{
using type = Accum;
};
template <std::size_t N, class T, class... Ts, class... Acc>
struct tuple_take_first_impl<N, std::tuple<T, Ts...>, std::tuple<Acc...>>
{
using type = typename tuple_take_first_impl<N - 1, std::tuple<Ts...>, std::tuple<Acc..., T>>::type;
};
template <class T, class... Ts, class... Acc>
struct tuple_take_first_impl<0, std::tuple<T, Ts...>, std::tuple<Acc...>>
{
using type = std::tuple<Acc...>;
};
template <std::size_t N, class Tuple>
using tuple_take_first_t = typename tuple_take_first_impl<N, Tuple>::type;
//=================================================================================================
/**
* @brief Extracts the class type from a member function pointer.
*/
template <class F>
struct member_function_class;
template <class C, class R, class... Args>
struct member_function_class<R (C::*)(Args...)> { using type = C; };
template <class C, class R, class... Args>
struct member_function_class<R (C::*)(Args...) const> { using type = const C; };
template <class C, class R, class... Args>
struct member_function_class<R (C::*)(Args...) noexcept> { using type = C; };
template <class C, class R, class... Args>
struct member_function_class<R (C::*)(Args...) const noexcept> { using type = const C; };
template <class F>
using member_function_class_t = typename member_function_class<F>::type;
//=================================================================================================
/**
* @brief Computes the leading argument tuple for bind_back: for member function pointers,
* prepends ClassType* to the explicit remaining args; for all other callables, returns
* the explicit remaining args unchanged.
*/
template <class Fn, class ExplicitRemaining, bool IsMember>
struct bind_back_leading_impl
{
using type = ExplicitRemaining;
};
template <class Fn, class ExplicitRemaining>
struct bind_back_leading_impl<Fn, ExplicitRemaining, true>
{
using type = tuple_prepend_t<member_function_class_t<Fn>*, ExplicitRemaining>;
};
template <class Fn, class ExplicitRemaining>
using bind_back_leading_t =
typename bind_back_leading_impl<Fn, ExplicitRemaining, std::is_member_function_pointer_v<Fn>>::type;
//=================================================================================================
/**
* @brief Internal storage for luabridge::bind_front — exposes a non-template operator() so that
* function_traits can statically resolve result_type and argument_types.
*/
template <class R, class RemainingArgsTuple, class Fn, class... BoundArgs>
struct bind_front_wrapper;
template <class R, class... Remaining, class Fn, class... BoundArgs>
struct bind_front_wrapper<R, std::tuple<Remaining...>, Fn, BoundArgs...>
{
Fn fn_;
std::tuple<BoundArgs...> bound_;
template <class F, class... BA>
bind_front_wrapper(F&& f, BA&&... ba)
: fn_(std::forward<F>(f)), bound_(std::forward<BA>(ba)...)
{
}
R operator()(Remaining... args) const
{
return std::apply([&](const auto&... ba) { return std::invoke(fn_, ba..., args...); }, bound_);
}
};
} // namespace detail
//=================================================================================================
/**
* @brief Drop-in replacement for std::bind_front with statically introspectable argument types.
*
* std::bind_front returns an object whose operator() is a template, so its argument and result
* types cannot be resolved at compile time without an explicit std::function<Sig> cast. This
* wrapper stores the callable and its leading bound arguments, then exposes a concrete
* non-template operator() whose parameter types are derived directly from the underlying
* callable's signature.
*
* For member function pointers the implicit object argument consumed by std::invoke is not
* counted as part of the remaining (Lua-visible) parameter list, matching std::bind_front
* semantics.
*
* @tparam F Callable type (function pointer, member function pointer, functor).
* @tparam BoundArgs Leading argument types to bind.
* @param f The callable to wrap.
* @param args Leading arguments forwarded into the wrapper by value.
* @return A callable object whose operator() accepts the remaining (unbound) arguments.
*/
template <class F, class... BoundArgs>
auto bind_front(F&& f, BoundArgs&&... args)
{
using Fn = std::decay_t<F>;
using FnTraits = detail::function_traits<Fn>;
static constexpr std::size_t skip = std::is_member_function_pointer_v<Fn> ? 1u : 0u;
static constexpr std::size_t num_effective_bound = sizeof...(BoundArgs) - skip;
using remaining = detail::tuple_drop_first_t<num_effective_bound, typename FnTraits::argument_types>;
using R = typename FnTraits::result_type;
return detail::bind_front_wrapper<R, remaining, Fn, std::decay_t<BoundArgs>...>(
std::forward<F>(f), std::forward<BoundArgs>(args)...);
}
//=================================================================================================
namespace detail {
/**
* @brief Internal storage for luabridge::bind_back — exposes a non-template operator() so that
* function_traits can statically resolve result_type and argument_types.
*
* LeadingArgsTuple is the tuple of arguments that the caller must provide; BoundArgs are
* the trailing arguments captured at bind time. For member function pointers, the class
* pointer is included as the first element of LeadingArgsTuple.
*/
template <class R, class LeadingArgsTuple, class Fn, class... BoundArgs>
struct bind_back_wrapper;
template <class R, class... Leading, class Fn, class... BoundArgs>
struct bind_back_wrapper<R, std::tuple<Leading...>, Fn, BoundArgs...>
{
Fn fn_;
std::tuple<BoundArgs...> bound_;
template <class F, class... BA>
bind_back_wrapper(F&& f, BA&&... ba)
: fn_(std::forward<F>(f)), bound_(std::forward<BA>(ba)...)
{
}
R operator()(Leading... args) const
{
return std::apply([&](const auto&... ba) { return std::invoke(fn_, args..., ba...); }, bound_);
}
};
} // namespace detail
//=================================================================================================
/**
* @brief Drop-in replacement for std::bind_back with statically introspectable argument types.
*
* Stores the callable and its trailing bound arguments, then exposes a concrete non-template
* operator() whose parameter types are derived directly from the underlying callable's signature.
* This lets LuaBridge register the result with addFunction / addStaticFunction without any extra
* annotation.
*
* For member function pointers the class pointer is automatically prepended to the remaining
* (Lua-visible) parameter list so that LuaBridge can dispatch it as a proxy member function.
*
* @tparam F Callable type (function pointer, member function pointer, functor).
* @tparam BoundArgs Trailing argument types to bind.
* @param f The callable to wrap.
* @param args Trailing arguments forwarded into the wrapper by value.
* @return A callable object whose operator() accepts the remaining (leading) arguments.
*/
template <class F, class... BoundArgs>
auto bind_back(F&& f, BoundArgs&&... args)
{
using Fn = std::decay_t<F>;
using FnTraits = detail::function_traits<Fn>;
static constexpr std::size_t num_explicit = FnTraits::arity;
static constexpr std::size_t num_bound = sizeof...(BoundArgs);
static constexpr std::size_t num_remaining = num_explicit - num_bound;
using explicit_remaining = detail::tuple_take_first_t<num_remaining, typename FnTraits::argument_types>;
using leading = detail::bind_back_leading_t<Fn, explicit_remaining>;
using R = typename FnTraits::result_type;
return detail::bind_back_wrapper<R, leading, Fn, std::decay_t<BoundArgs>...>(
std::forward<F>(f), std::forward<BoundArgs>(args)...);
}
} // namespace luabridge
+77
View File
@@ -0,0 +1,77 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2023, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "Stack.h"
#include <optional>
#include <type_traits>
namespace luabridge {
//=================================================================================================
/**
* @brief Get a global value from the lua_State.
*
* @note This works on any type specialized by `Stack`, including `LuaRef` and its table proxies.
*/
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;
}
//=================================================================================================
/**
* @brief Try to get a field from a global table without creating a LuaRef.
*
* This is a fast-path helper for optional lookup patterns. It invokes normal Lua field access
* on the table, including metamethods, and returns std::nullopt when the global is not a table
* or the field cannot be converted to the requested type.
*/
template <class T>
std::optional<T> tryGetGlobalField(lua_State* L, const char* globalName, const char* fieldName)
{
const StackRestore stackRestore(L);
lua_getglobal(L, globalName);
if (! lua_istable(L, -1))
return std::nullopt;
lua_getfield(L, -1, fieldName);
auto result = Stack<std::decay_t<T>>::get(L, -1);
if (! result)
return std::nullopt;
return *result;
}
//=================================================================================================
/**
* @brief Set a global value in the lua_State.
*
* @note This works on any type specialized by `Stack`, including `LuaRef` and its table proxies.
*/
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;
}
} // namespace luabridge
+263
View File
@@ -0,0 +1,263 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2021, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "Errors.h"
#include "Stack.h"
#include "LuaRef.h"
#include "LuaException.h"
#include <functional>
#include <tuple>
#include <type_traits>
#include <utility>
namespace luabridge {
//=================================================================================================
namespace detail {
template <class F>
bool is_handler_valid(const F& f) noexcept
{
if constexpr (std::is_pointer_v<remove_cvref_t<F>>)
return f != nullptr;
else if constexpr (std::is_constructible_v<bool, remove_cvref_t<F>>)
return static_cast<bool>(f);
else
return true;
}
template <class Tuple, std::size_t... Indices>
TypeResult<Tuple> decode_tuple_result(lua_State* L, int first_result_index, std::index_sequence<Indices...>)
{
auto results = std::make_tuple(
Stack<std::tuple_element_t<Indices, Tuple>>::get(L, first_result_index + static_cast<int>(Indices))...);
std::error_code ec;
const bool ok =
(([&]()
{
const auto& element = std::get<Indices>(results);
if (! element)
{
ec = element.error();
return false;
}
return true;
}())
&& ...);
if (! ok)
return ec;
return Tuple{ std::move(*std::get<Indices>(results))... };
}
template <class R>
TypeResult<R> decode_call_result(lua_State* L, int first_result_index, int num_returned_values)
{
if constexpr (std::is_same_v<R, void> || std::is_same_v<R, std::tuple<>>)
{
if (num_returned_values != 0)
return makeErrorCode(ErrorCode::InvalidTableSizeInCast);
return {};
}
else if constexpr (is_tuple_v<R>)
{
constexpr auto expected_size = static_cast<int>(std::tuple_size_v<R>);
if (num_returned_values != expected_size)
return makeErrorCode(ErrorCode::InvalidTableSizeInCast);
return decode_tuple_result<R>(L, first_result_index, std::make_index_sequence<expected_size>{});
}
else
{
if (num_returned_values < 1)
return makeErrorCode(ErrorCode::InvalidTypeCast);
return Stack<R>::get(L, first_result_index);
}
}
} // namespace detail
//=================================================================================================
/**
* @brief Safely call Lua code and decode the return values to R.
*/
template <class R, class Ref, class F, class... Args>
TypeResult<R> callWithHandler(const Ref& object, F&& errorHandler, Args&&... args)
{
static_assert(std::is_same_v<detail::remove_cvref_t<F>, detail::remove_cvref_t<decltype(std::ignore)>> || std::is_invocable_r_v<int, F, lua_State*>);
static constexpr bool isValidHandler =
!std::is_same_v<detail::remove_cvref_t<F>, detail::remove_cvref_t<decltype(std::ignore)>>;
lua_State* L = object.state();
const StackRestore stackRestore(L);
const int initialTop = lua_gettop(L);
bool hasHandler = false;
if constexpr (isValidHandler)
{
hasHandler = detail::is_handler_valid(errorHandler);
if (hasHandler)
detail::push_function(L, std::forward<F>(errorHandler), "");
}
object.push();
{
const auto [result, index] = detail::push_arguments(L, std::forward_as_tuple(args...));
if (! result)
return result.error();
}
const int messageHandlerIndex = hasHandler ? (initialTop + 1) : 0;
const int code = lua_pcall(L, sizeof...(Args), LUA_MULTRET, messageHandlerIndex);
if (code != LUABRIDGE_LUA_OK)
{
auto ec = makeErrorCode(ErrorCode::LuaFunctionCallFailed);
#if LUABRIDGE_HAS_EXCEPTIONS
if constexpr (! isValidHandler)
{
if (LuaException::areExceptionsEnabled(L))
LuaException::raise(L, ec);
}
#endif
lua_pop(L, 1);
return ec;
}
if (hasHandler)
lua_remove(L, initialTop + 1);
const int firstResultIndex = initialTop + 1;
const int numReturnedValues = lua_gettop(L) - initialTop;
return detail::decode_call_result<R>(L, firstResultIndex, numReturnedValues);
}
template <class Ref, class F, class... Args>
TypeResult<void> callWithHandler(const Ref& object, F&& errorHandler, Args&&... args)
{
return callWithHandler<void, Ref, F, Args...>(object, std::forward<F>(errorHandler), std::forward<Args>(args)...);
}
template <class R = void, class Ref, class... Args>
TypeResult<R> call(const Ref& object, Args&&... args)
{
return callWithHandler<R>(object, std::ignore, std::forward<Args>(args)...);
}
template <class Signature>
class LuaFunction;
template <class R, class... Args>
class LuaFunction<R(Args...)>
{
public:
LuaFunction() = default;
explicit LuaFunction(const LuaRef& function)
: m_function(function)
{
}
explicit LuaFunction(LuaRef&& function)
: m_function(std::move(function))
{
}
[[nodiscard]] TypeResult<R> operator()(Args... args) const
{
return call(std::forward<Args>(args)...);
}
[[nodiscard]] TypeResult<R> call(Args... args) const
{
return luabridge::call<R>(m_function, std::forward<Args>(args)...);
}
template <class F>
[[nodiscard]] TypeResult<R> callWithHandler(F&& errorHandler, Args... args) const
{
return luabridge::callWithHandler<R>(m_function, std::forward<F>(errorHandler), std::forward<Args>(args)...);
}
[[nodiscard]] bool isValid() const
{
return m_function.isCallable();
}
[[nodiscard]] const LuaRef& ref() const
{
return m_function;
}
private:
LuaRef m_function;
};
//=============================================================================================
/**
* @brief Wrapper for `lua_pcall` that throws if exceptions are enabled.
*/
inline int pcall(lua_State* L, int nargs = 0, int nresults = 0, int msgh = 0)
{
const int code = lua_pcall(L, nargs, nresults, msgh);
#if LUABRIDGE_HAS_EXCEPTIONS
if (code != LUABRIDGE_LUA_OK && LuaException::areExceptionsEnabled(L))
LuaException::raise(L, makeErrorCode(ErrorCode::LuaFunctionCallFailed));
#endif
return code;
}
//=============================================================================================
template <class Impl, class LuaRef>
template <class R, class... Args>
TypeResult<R> LuaRefBase<Impl, LuaRef>::call(Args&&... args) const
{
return luabridge::call<R>(impl(), std::forward<Args>(args)...);
}
template <class Impl, class LuaRef>
template <class... Args>
TypeResult<void> LuaRefBase<Impl, LuaRef>::operator()(Args&&... args) const
{
return call<void>(std::forward<Args>(args)...);
}
template <class Impl, class LuaRef>
template <class R, class F, class... Args>
TypeResult<R> LuaRefBase<Impl, LuaRef>::callWithHandler(F&& errorHandler, Args&&... args) const
{
return luabridge::callWithHandler<R>(impl(), std::forward<F>(errorHandler), std::forward<Args>(args)...);
}
template <class Impl, class LuaRef>
template <class F, class... Args>
TypeResult<void> LuaRefBase<Impl, LuaRef>::callWithHandler(F&& errorHandler, Args&&... args) const
{
return callWithHandler<void>(std::forward<F>(errorHandler), std::forward<Args>(args)...);
}
template <class Impl, class LuaRef>
template <class Signature>
LuaFunction<Signature> LuaRefBase<Impl, LuaRef>::callable() const
{
const StackRestore stackRestore(m_L);
impl().push(m_L);
return LuaFunction<Signature>(LuaRef::fromStack(m_L));
}
} // namespace luabridge
+256
View File
@@ -0,0 +1,256 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2018, Dmitry Tarakanov
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "LuaRef.h"
#include <utility>
#if LUABRIDGE_HAS_CXX20_RANGES
#include <iterator>
#include <ranges>
#endif
namespace luabridge {
//=================================================================================================
/**
* @brief Iterator class to allow table iteration.
*
* @see Range class.
*/
class Iterator
{
public:
explicit Iterator(const LuaRef& table, bool isEnd = false)
: m_L(table.state())
, m_table(table)
, m_key(table.state()) // m_key is nil
, m_value(table.state()) // m_value is nil
{
if (! isEnd)
{
next(); // get the first (key, value) pair from table
}
}
#if LUABRIDGE_HAS_CXX20_RANGES
using value_type = std::pair<LuaRef, LuaRef>;
using difference_type = std::ptrdiff_t;
using iterator_concept = std::input_iterator_tag;
#endif
/**
* @brief Return an associated Lua state.
*
* @return A Lua state.
*/
lua_State* state() const noexcept
{
return m_L;
}
/**
* @brief Dereference the iterator.
*
* @return A key-value pair for a current table entry.
*/
std::pair<LuaRef, LuaRef> operator*() const
{
return std::make_pair(m_key, m_value);
}
/**
* @brief Return the value referred by the iterator.
*
* @return A value for the current table entry.
*/
LuaRef operator->() const
{
return m_value;
}
/**
* @brief Compare two iterators.
*
* @param rhs Another iterator.
*
* @return True if iterators point to the same entry of the same table, false otherwise.
*/
bool operator!=(const Iterator& rhs) const
{
LUABRIDGE_ASSERT(m_L == rhs.m_L);
return ! m_table.rawequal(rhs.m_table) || ! m_key.rawequal(rhs.m_key);
}
/**
* @brief Move the iterator to the next table entry.
*
* @return This iterator.
*/
Iterator& operator++()
{
if (isNil())
{
// if the iterator reaches the end, do nothing
return *this;
}
else
{
next();
return *this;
}
}
/**
* @brief Check if the iterator points after the last table entry.
*
* @return True if there are no more table entries to iterate, false otherwise.
*/
bool isNil() const noexcept
{
return m_key.isNil();
}
/**
* @brief Return the key for the current table entry.
*
* @return A reference to the entry key.
*/
LuaRef key() const
{
return m_key;
}
/**
* @brief Return the key for the current table entry.
*
* @return A reference to the entry value.
*/
LuaRef value() const
{
return m_value;
}
private:
// Don't use postfix increment, it is less efficient
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;
};
//=================================================================================================
/**
* @brief Range class taking two table iterators.
*/
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;
};
//=================================================================================================
/**
* @brief Return a range for the Lua table reference.
*
* @return A range suitable for range-based for statement.
*/
inline Range pairs(const LuaRef& table)
{
return Range{ Iterator(table, false), Iterator(table, true) };
}
#if LUABRIDGE_HAS_CXX20_RANGES
/**
* @brief Equality comparison for Iterator.
*/
inline bool operator==(const Iterator& lhs, const Iterator& rhs)
{
if (lhs.isNil() && rhs.isNil())
return true;
if (lhs.isNil() != rhs.isNil())
return false;
return lhs.key().rawequal(rhs.key()) && lhs.value().rawequal(rhs.value());
}
/**
* @brief Sentinel type for Iterator end detection.
*/
struct IteratorSentinel {};
/**
* @brief Sentinel equality: Iterator is at end when isNil().
*/
inline bool operator==(const Iterator& it, IteratorSentinel)
{
return it.isNil();
}
inline bool operator==(IteratorSentinel, const Iterator& it)
{
return it.isNil();
}
#endif // LUABRIDGE_HAS_CXX20_RANGES
} // namespace luabridge
#if LUABRIDGE_HAS_CXX20_RANGES
template <>
inline constexpr bool std::ranges::enable_borrowed_range<luabridge::Range> = false;
#endif
+189
View File
@@ -0,0 +1,189 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2021, kunitoki
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// Copyright 2008, Nigel Atkinson <suprapilot+LuaCode@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "ClassInfo.h"
#include "LuaHelpers.h"
#include <string>
#include <sstream>
#include <exception>
namespace luabridge {
//================================================================================================
class LuaException : public std::exception
{
public:
//=============================================================================================
/**
* @brief Construct a LuaException after a lua_pcall().
*
* Assumes the error string is on top of the stack, but provides a generic error message otherwise.
*/
LuaException(lua_State* L, std::error_code code)
: m_L(L)
, m_code(code)
{
}
~LuaException() noexcept override
{
}
//=============================================================================================
/**
* @brief Return the error message.
*/
const char* what() const noexcept override
{
return m_what.c_str();
}
//=============================================================================================
/**
* @brief Throw an exception or raises a luaerror when exceptions are disabled.
*/
static void raise(lua_State* L, std::error_code code)
{
LUABRIDGE_ASSERT(areExceptionsEnabled(L));
#if LUABRIDGE_HAS_EXCEPTIONS
throw LuaException(L, code, FromLua{});
#else
unused(L, code);
std::abort();
#endif
}
//=============================================================================================
/**
* @brief Check if exceptions are enabled.
*/
static bool areExceptionsEnabled(lua_State* L) noexcept
{
lua_pushlightuserdata(L, detail::getExceptionsKey());
lua_gettable(L, LUA_REGISTRYINDEX);
const bool enabled = lua_isboolean(L, -1) ? static_cast<bool>(lua_toboolean(L, -1)) : false;
lua_pop(L, 1);
return enabled;
}
/**
* @brief Initializes error handling.
*
* Subsequent Lua errors are translated to C++ exceptions, or logging and abort if exceptions are disabled.
*/
static void enableExceptions(lua_State* L) noexcept
{
lua_pushlightuserdata(L, detail::getExceptionsKey());
lua_pushboolean(L, true);
lua_settable(L, LUA_REGISTRYINDEX);
#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
}
//=============================================================================================
/**
* @brief Retrieve the lua_State associated with the exception.
*
* @return A Lua state.
*/
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());
lua_error_x(L);
}
}
#endif
lua_State* m_L = nullptr;
std::error_code m_code;
std::string m_what;
};
//=================================================================================================
/**
* @brief Initializes error handling using C++ exceptions.
*
* Subsequent Lua errors are translated to C++ exceptions. It aborts the application if called when no exceptions.
*/
inline void enableExceptions(lua_State* L) noexcept
{
#if LUABRIDGE_HAS_EXCEPTIONS
LuaException::enableExceptions(L);
#else
unused(L);
LUABRIDGE_ASSERT(false); // Never call this function when exceptions are not enabled.
#endif
}
} // namespace luabridge
+756
View File
@@ -0,0 +1,756 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// Copyright 2007, Nathan Reed
// SPDX-License-Identifier: MIT
#pragma once
#include "FuncTraits.h"
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <limits>
#include <type_traits>
#include <utility>
namespace luabridge {
/**
* @brief Helper for unused vars.
*/
template <class... Args>
constexpr void unused(Args&&...)
{
}
// These are for Lua versions prior to 5.2.0.
#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
#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;
}
}
#if ! LUABRIDGE_ON_LUAJIT
inline void* luaL_testudata(lua_State* L, int ud, const char* tname)
{
void* p = lua_touserdata(L, ud);
if (p == nullptr)
return nullptr;
if (! lua_getmetatable(L, ud))
return nullptr;
luaL_getmetatable(L, tname);
if (! lua_rawequal(L, -1, -2))
p = nullptr;
lua_pop(L, 2);
return p;
}
#endif
inline int get_length(lua_State* L, int idx)
{
return static_cast<int>(lua_objlen(L, idx));
}
#else // LUA_VERSION_NUM >= 502
inline int get_length(lua_State* L, int idx)
{
return static_cast<int>(lua_rawlen(L, idx));
}
#endif // LUA_VERSION_NUM < 502
// These functions and defines are for Luau.
#if LUABRIDGE_ON_LUAU
inline int luaL_ref(lua_State* L, int idx)
{
LUABRIDGE_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, const char* debugname)
{
lua_pushcfunction(L, fn, debugname);
}
inline void lua_pushcclosure_x(lua_State* L, lua_CFunction fn, const char* debugname, int n)
{
lua_pushcclosure(L, fn, debugname, n);
}
[[noreturn]] inline void lua_error_x(lua_State* L)
{
lua_error(L);
}
inline int lua_getstack_x(lua_State* L, int level, lua_Debug* ar)
{
return lua_getinfo(L, level, "nlS", ar);
}
inline int lua_getstack_info_x(lua_State* L, int level, const char* what, lua_Debug* ar)
{
return lua_getinfo(L, level, what, ar);
}
inline int lua_rawgetp_x(lua_State* L, int idx, void* p)
{
return lua_rawgetp(L, idx, p);
}
inline void lua_rawsetp_x(lua_State* L, int idx, void* p)
{
lua_rawsetp(L, idx, p);
}
#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, const char* debugname)
{
unused(debugname);
lua_pushcfunction(L, fn);
}
inline void lua_pushcclosure_x(lua_State* L, lua_CFunction fn, const char* debugname, int n)
{
unused(debugname);
lua_pushcclosure(L, fn, n);
}
[[noreturn]] inline void lua_error_x(lua_State* L)
{
lua_error(L);
detail::unreachable();
}
inline int lua_getstack_x(lua_State* L, int level, lua_Debug* ar)
{
return lua_getstack(L, level, ar);
}
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);
}
inline int lua_rawgetp_x(lua_State* L, int idx, void* p)
{
#if LUA_VERSION_NUM < 503
idx = lua_absindex(L, idx);
luaL_checkstack(L, 1, "not enough stack slots");
lua_pushlightuserdata(L, p);
lua_rawget(L, idx);
return lua_type(L, -1);
#else
return lua_rawgetp(L, idx, p);
#endif
}
inline void lua_rawsetp_x(lua_State* L, int idx, void* p)
{
#if LUA_VERSION_NUM < 503
idx = lua_absindex(L, idx);
luaL_checkstack(L, 1, "not enough stack slots");
lua_pushlightuserdata(L, p);
lua_insert(L, -2);
lua_rawset(L, idx);
#else
lua_rawsetp(L, idx, p);
#endif
}
#endif // LUABRIDGE_ON_LUAU
// These are for Lua versions prior to 5.5.0.
#if LUA_VERSION_NUM < 505
inline lua_State* lua_newstate_x(lua_Alloc f, void* ud, [[maybe_unused]] unsigned seed)
{
return lua_newstate(f, ud);
}
#else
inline lua_State* lua_newstate_x(lua_Alloc f, void* ud, unsigned seed)
{
return lua_newstate(f, ud, seed);
}
#endif
// These are for Lua versions prior to 5.3.0.
#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)
{
if (n < static_cast<lua_Number>(std::numeric_limits<lua_Integer>::min()) ||
n >= -static_cast<lua_Number>(std::numeric_limits<lua_Integer>::min()))
{
if (isnum)
*isnum = 0;
return 0;
}
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 // LUA_VERSION_NUM < 503
inline int lua_rawgetp_x(lua_State* L, int idx, const void* p)
{
return lua_rawgetp_x(L, idx, const_cast<void*>(p));
}
inline void lua_rawsetp_x(lua_State* L, int idx, const void* p)
{
lua_rawsetp_x(L, idx, const_cast<void*>(p));
}
#ifndef LUA_OK
#define LUABRIDGE_LUA_OK 0
#else
#define LUABRIDGE_LUA_OK LUA_OK
#endif
/**
* @brief Helper to throw or return an error code.
*/
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
}
/**
* @brief Helper to throw or LUABRIDGE_ASSERT.
*/
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)...);
LUABRIDGE_ASSERT(false);
#endif
}
/**
* @brief Helper to set unsigned.
*/
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));
}
/**
* @brief Helper to convert to integer.
*/
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
}
/**
* @brief Helper to convert to integer.
*/
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
}
/**
* @brief Register main thread, only supported on 5.1.
*/
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
}
/**
* @brief Get main thread, not supported on 5.1.
*/
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;
}
LUABRIDGE_ASSERT(false); // Have you forgot to call luabridge::registerMainThread ?
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
}
/**
* @brief Get a table value, bypassing metamethods.
*/
inline int rawgetfield(lua_State* L, int index, const char* key)
{
LUABRIDGE_ASSERT(lua_istable(L, index));
index = lua_absindex(L, index);
lua_pushstring(L, key);
#if LUA_VERSION_NUM <= 502
lua_rawget(L, index);
return lua_type(L, -1);
#else
return lua_rawget(L, index);
#endif
}
/**
* @brief Set a table value, bypassing metamethods.
*/
inline void rawsetfield(lua_State* L, int index, const char* key)
{
LUABRIDGE_ASSERT(lua_istable(L, index));
index = lua_absindex(L, index);
lua_pushstring(L, key);
lua_insert(L, -2);
lua_rawset(L, index);
}
/**
* @brief Returns true if the value is a full userdata (not light).
*/
[[nodiscard]] inline bool isfulluserdata(lua_State* L, int index)
{
return lua_isuserdata(L, index) && !lua_islightuserdata(L, index);
}
/**
* @brief Test lua_State objects for global equality.
*
* This can determine if two different lua_State objects really point
* to the same global state, such as when using coroutines.
*
* @note This is used for assertions.
*/
[[nodiscard]] inline bool equalstates(lua_State* L1, lua_State* L2)
{
return lua_topointer(L1, LUA_REGISTRYINDEX) == lua_topointer(L2, LUA_REGISTRYINDEX);
}
/**
* @brief Return the size of lua table, even if not a sequence { 1=x, 2=y, 3=... }.
*/
[[nodiscard]] inline int table_length(lua_State* L, int index)
{
LUABRIDGE_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;
}
/**
* @brief Return an aligned pointer of type T.
*/
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);
}
/**
* @brief Return if a pointer of type T is aligned.
*/
template <std::size_t Alignment, class T, std::enable_if_t<std::is_pointer_v<T>, int> = 0>
[[nodiscard]] bool is_aligned(T address) noexcept
{
static_assert(Alignment > 0u);
return (reinterpret_cast<std::uintptr_t>(address) & (Alignment - 1u)) == 0u;
}
/**
* @brief Return the space needed to align the type T on an unaligned address.
*/
template <class T>
[[nodiscard]] constexpr size_t maximum_space_needed_to_align() noexcept
{
return sizeof(T) + alignof(T) - 1;
}
/**
* @brief Deallocate lua userdata taking into account alignment.
*/
template <class T>
int lua_deleteuserdata_aligned(lua_State* L)
{
LUABRIDGE_ASSERT(isfulluserdata(L, 1));
T* aligned = align<T>(lua_touserdata(L, 1));
aligned->~T();
return 0;
}
/**
* @brief Allocate lua userdata taking into account alignment.
*
* Using this instead of lua_newuserdata directly prevents alignment warnings on 64bits platforms.
*/
template <class T, class... Args>
void* lua_newuserdata_aligned(lua_State* L, Args&&... args)
{
using U = std::remove_reference_t<T>;
#if LUABRIDGE_ON_LUAU
void* pointer = lua_newuserdatadtor(L, maximum_space_needed_to_align<U>(), [](void* x)
{
U* aligned = align<U>(x);
aligned->~U();
});
#else
void* pointer = lua_newuserdata_x<U>(L, maximum_space_needed_to_align<U>());
lua_newtable(L);
lua_pushcfunction_x(L, &lua_deleteuserdata_aligned<U>, "");
rawsetfield(L, -2, "__gc");
lua_setmetatable(L, -2);
#endif
U* aligned = align<U>(pointer);
new (aligned) U(std::forward<Args>(args)...);
return pointer;
}
/**
* @brief Safe error able to walk backwards for error reporting correctly.
*/
[[noreturn]] inline void raise_lua_error(lua_State* L, const char* fmt, ...)
{
va_list argp;
va_start(argp, fmt);
lua_pushvfstring(L, fmt, argp);
va_end(argp);
const char* message = lua_tostring(L, -1);
if (message != nullptr)
{
if (auto str = std::string_view(message); !str.empty() && str[0] == '[')
lua_error_x(L);
}
bool pushed_error = false;
for (int level = 1; level <= 2; ++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_pushvalue(L, -2);
lua_remove(L, -3);
lua_concat(L, 2);
lua_error_x(L);
}
/**
* @brief Checks if the value on the stack is a number type and can fit into the corresponding c++ integral type..
*/
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;
}
else if constexpr (std::is_unsigned_v<T>)
{
return value <= static_cast<T>((std::numeric_limits<U>::max)());
}
else
{
return value >= static_cast<T>((std::numeric_limits<U>::min)())
&& static_cast<U>(value) <= (std::numeric_limits<U>::max)();
}
}
else 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)();
}
else if constexpr (std::is_unsigned_v<T>)
{
return value <= static_cast<T>((std::numeric_limits<U>::max)());
}
else
{
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;
}
/**
* @brief Checks if the value on the stack is a number type and can fit into the corresponding c++ numerical type..
*/
template <class U = lua_Number, class T>
bool is_floating_point_representable_by(T value)
{
if constexpr (sizeof(T) == sizeof(U))
{
return true;
}
else if constexpr (sizeof(T) < sizeof(U))
{
if (std::isnan(value) || std::isinf(value))
return true;
return static_cast<U>(value) >= -(std::numeric_limits<U>::max)()
&& static_cast<U>(value) <= (std::numeric_limits<U>::max)();
}
else
{
if (std::isnan(value) || std::isinf(value))
return true;
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;
}
/**
* @brief Portable wrapper for lua_resume that normalises calling convention differences
* across Lua 5.1/LuaJIT (no from, no nresults), 5.2-5.3 (from but no nresults), and 5.4+ (from + nresults).
*
* @param L The coroutine thread to resume.
* @param from The thread doing the resuming (may be nullptr on older Lua).
* @param nargs Number of arguments on L's stack to pass to the resumed function.
* @param nresults Output: number of values on L's stack after resume (yielded or returned).
* For Lua 5.4+, filled directly by lua_resume. For older versions, computed via lua_gettop.
* @returns LUA_OK, LUA_YIELD, or an error code.
*/
inline int lua_resume_x(lua_State* L, lua_State* from, int nargs, int* nresults = nullptr)
{
#if LUABRIDGE_ON_LUAJIT || LUA_VERSION_NUM == 501
unused(from);
int status = lua_resume(L, nargs);
if (nresults)
*nresults = lua_gettop(L);
return status;
#elif LUABRIDGE_ON_LUAU || LUABRIDGE_ON_RAVI || LUA_VERSION_NUM < 504
int status = lua_resume(L, from, nargs);
if (nresults)
*nresults = lua_gettop(L);
return status;
#else
int nr = 0;
int status = lua_resume(L, from, nargs, &nr);
if (nresults)
*nresults = nr;
return status;
#endif
}
/**
* @brief Returns true if the currently running C function can yield via lua_yieldk.
*
* Returns false on Lua 5.1, LuaJIT, and Luau where lua_yieldk is unavailable.
*/
inline bool lua_isyieldable_x(lua_State* L)
{
#if LUABRIDGE_ON_LUAJIT || LUA_VERSION_NUM == 501 || LUABRIDGE_ON_LUAU
unused(L);
return false;
#elif LUA_VERSION_NUM < 503
unused(L);
return true; // lua_yieldk exists in 5.2; assume yieldable when reached
#else
return lua_isyieldable(L) != 0;
#endif
}
} // namespace luabridge
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+50
View File
@@ -0,0 +1,50 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2023, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "FlagSet.h"
#include <cstdint>
namespace luabridge {
//=================================================================================================
namespace detail {
struct OptionExtensibleClass;
struct OptionAllowOverridingMethods;
struct OptionVisibleMetatables;
} // namespace Detail
/**
* @brief Options for the library.
*/
using Options = FlagSet<uint32_t,
detail::OptionExtensibleClass,
detail::OptionAllowOverridingMethods,
detail::OptionVisibleMetatables>;
/**
* @brief Set of default options.
*
* This setting means all options are not enabled.
*/
static inline constexpr Options defaultOptions = Options();
/**
* @brief Enable extensible C++ classes when registering them.
*/
static inline constexpr Options extensibleClass = Options::Value<detail::OptionExtensibleClass>();
/**
* @brief Allow to be able to override methods from lua in extensible C++ classes.
*/
static inline constexpr Options allowOverridingMethods = Options::Value<detail::OptionAllowOverridingMethods>();
/**
* @brief Specify if metatables are visible for namespaces and classes.
*/
static inline constexpr Options visibleMetatables = Options::Value<detail::OptionVisibleMetatables>();
} // namespace luabridge
+84
View File
@@ -0,0 +1,84 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2020, Dmitry Tarakanov
// Copyright 2019, George Tokmaji
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "Errors.h"
#include "Stack.h"
#include "TypeTraits.h"
#include "Userdata.h"
#include <functional>
#include <tuple>
namespace luabridge {
//=================================================================================================
/**
* @brief Overloaded objects.
*/
template <class... Args>
struct NonConstOverload
{
template <class R, class T>
constexpr auto operator()(R (T::*ptr)(Args...)) const noexcept -> decltype(ptr)
{
return ptr;
}
template <class R, class T>
static constexpr auto with(R (T::*ptr)(Args...)) noexcept -> decltype(ptr)
{
return ptr;
}
};
template <class... Args>
struct ConstOverload
{
template <class R, class T>
constexpr auto operator()(R (T::*ptr)(Args...) const) const noexcept -> decltype(ptr)
{
return ptr;
}
template <class R, class T>
static constexpr auto with(R (T::*ptr)(Args...) const) noexcept -> decltype(ptr)
{
return ptr;
}
};
template <class... Args>
struct Overload : ConstOverload<Args...>, NonConstOverload<Args...>
{
using ConstOverload<Args...>::operator();
using NonConstOverload<Args...>::operator();
template <class R>
constexpr auto operator()(R (*ptr)(Args...)) const noexcept -> decltype(ptr)
{
return ptr;
}
template <class R, class T>
static constexpr auto with(R (T::*ptr)(Args...)) noexcept -> decltype(ptr)
{
return ptr;
}
};
//=================================================================================================
/**
* @brief Overload resolution.
*/
template <class... Args> [[maybe_unused]] constexpr Overload<Args...> overload = {};
template <class... Args> [[maybe_unused]] constexpr ConstOverload<Args...> constOverload = {};
template <class... Args> [[maybe_unused]] constexpr NonConstOverload<Args...> nonConstOverload = {};
} // namespace luabridge
+257
View File
@@ -0,0 +1,257 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2022, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Errors.h"
#include <type_traits>
namespace luabridge {
//=================================================================================================
/**
* @brief Simple result class containing a result.
*/
struct Result
{
Result() noexcept = default;
Result(std::error_code ec) noexcept
: m_ec(ec)
{
}
Result(const Result&) noexcept = default;
Result(Result&&) noexcept = default;
Result& operator=(const Result&) noexcept = default;
Result& operator=(Result&&) noexcept = default;
explicit operator bool() const noexcept
{
return !m_ec;
}
std::error_code error() const noexcept
{
return m_ec;
}
const char* error_cstr() const noexcept
{
return detail::ErrorCategory::errorString(m_ec.value());
}
operator std::error_code() const noexcept
{
return m_ec;
}
std::string message() const
{
return m_ec.message();
}
#if LUABRIDGE_HAS_EXCEPTIONS
void throw_on_error() const
{
if (m_ec)
throw std::system_error(m_ec);
}
#endif
private:
std::error_code m_ec;
};
//=================================================================================================
/**
* @brief Simple result class containing or a type T or an error code.
*/
template <class T>
struct TypeResult
{
TypeResult() noexcept = 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 noexcept
{
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();
}
T operator*() const&&
{
return std::move(m_value.value());
}
T* operator->()
{
return &m_value.value();
}
const T* operator->() const
{
return &m_value.value();
}
template <class U>
T valueOr(U&& defaultValue) const&
{
return m_value.valueOr(std::forward<U>(defaultValue));
}
template <class U>
T valueOr(U&& defaultValue) &&
{
return m_value.valueOr(std::forward<U>(defaultValue));
}
std::error_code error() const
{
return m_value.error();
}
const char* error_cstr() const noexcept
{
return detail::ErrorCategory::errorString(m_value.error().value());
}
operator std::error_code() const
{
return m_value.error();
}
std::string message() const
{
return m_value.error().message();
}
#if LUABRIDGE_HAS_EXCEPTIONS
void throw_on_error() const
{
if (! m_value.hasValue())
throw std::system_error(m_value.error());
}
#endif
private:
Expected<T, std::error_code> m_value;
};
template <>
struct TypeResult<void>
{
TypeResult() noexcept = default;
TypeResult(std::error_code ec) noexcept
: m_ec(ec)
{
}
TypeResult(const TypeResult&) noexcept = default;
TypeResult(TypeResult&&) noexcept = default;
TypeResult& operator=(const TypeResult&) noexcept = default;
TypeResult& operator=(TypeResult&&) noexcept = default;
explicit operator bool() const noexcept
{
return ! m_ec;
}
void value() const noexcept
{
}
std::error_code error() const noexcept
{
return m_ec;
}
const char* error_cstr() const noexcept
{
return detail::ErrorCategory::errorString(m_ec.value());
}
operator std::error_code() const noexcept
{
return m_ec;
}
std::string message() const
{
return m_ec.message();
}
#if LUABRIDGE_HAS_EXCEPTIONS
void throw_on_error() const
{
if (m_ec)
throw std::system_error(m_ec);
}
#endif
private:
std::error_code m_ec;
};
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);
}
} // namespace luabridge
+47
View File
@@ -0,0 +1,47 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2021, kunitoki
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "Stack.h"
namespace luabridge::detail {
//=================================================================================================
/**
* @brief Scope guard.
*/
template <class F>
class ScopeGuard
{
public:
template <class V>
explicit ScopeGuard(V&& v)
: m_func(std::forward<V>(v))
, m_shouldRun(true)
{
}
~ScopeGuard()
{
if (m_shouldRun)
m_func();
}
void reset() noexcept
{
m_shouldRun = false;
}
private:
F m_func;
bool m_shouldRun;
};
template <class F>
ScopeGuard(F&&) -> ScopeGuard<F>;
} // namespace luabridge::detail
File diff suppressed because it is too large Load Diff
+159
View File
@@ -0,0 +1,159 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2019, Dmitry Tarakanov
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include <memory>
#include <tuple>
namespace luabridge {
namespace detail {
template <template <class...> class C, class... Ts>
std::true_type is_base_of_template_impl(const C<Ts...>*);
template <template <class...> class C>
std::false_type is_base_of_template_impl(...);
template <class T, template <class...> class C>
using is_base_of_template = decltype(is_base_of_template_impl<C>(std::declval<T*>()));
template <class T, template <class...> class C>
static inline constexpr bool is_base_of_template_v = is_base_of_template<T, C>::value;
template <class... Args>
constexpr bool dependent_false = false;
template <class T>
struct is_tuple : std::false_type
{
};
template <class... Ts>
struct is_tuple<std::tuple<Ts...>> : std::true_type
{
};
template <class T>
constexpr bool is_tuple_v = is_tuple<T>::value;
} // namespace detail
//=================================================================================================
/**
* @brief Container traits.
*
* Unspecialized ContainerTraits has the isNotContainer typedef for SFINAE. All user defined containers must supply an appropriate
* specialization for ContinerTraits (without the alias isNotContainer). The containers that come with LuaBridge also come with the
* appropriate ContainerTraits specialization.
*
* @note See the corresponding declaration for details.
*
* A specialization of ContainerTraits for some generic type ContainerType looks like this:
*
* @code
*
* template <class T>
* struct ContainerTraits<ContainerType<T>>
* {
* using Type = T;
*
* static ContainerType<T> construct(T* c)
* {
* return c; // Implementation-dependent on ContainerType
* }
*
* static T* get(const ContainerType<T>& c)
* {
* return c.get(); // Implementation-dependent on ContainerType
* }
* };
*
* @endcode
*/
template <class T>
struct ContainerTraits
{
using IsNotContainer = bool;
using Type = T;
};
/**
* @brief Register shared_ptr support as container.
*
* @tparam T Class that is hold by the shared_ptr, must inherit from std::enable_shared_from_this to support Stack::get to reconstruct it from lua.
*/
template <class T>
struct ContainerTraits<std::shared_ptr<T>>
{
using Type = T;
template <class U = T>
static std::shared_ptr<U> construct(U* t)
{
if constexpr (detail::is_base_of_template_v<U, std::enable_shared_from_this>)
{
return std::static_pointer_cast<U>(t->shared_from_this());
}
else
{
static_assert(detail::dependent_false<U>,
"Failed reconstructing the reference count of the object instance, class must inherit from std::enable_shared_from_this");
}
}
static T* get(const std::shared_ptr<T>& c)
{
return c.get();
}
};
/**
* @brief Register unique_ptr support as container.
*
* @note Lua gets a non-owning view of the object. The C++ owner must outlive any Lua reference.
*
* @tparam T Class that is held by the unique_ptr.
*/
template <class T>
struct ContainerTraits<std::unique_ptr<T>>
{
using Type = T;
static T* get(const std::unique_ptr<T>& c)
{
return c.get();
}
};
namespace detail {
//=================================================================================================
/**
* @brief Determine if type T is a container.
*
* To be considered a container, there must be a specialization of ContainerTraits with the required fields.
*/
template <class T>
class IsContainer
{
private:
typedef char yes[1]; // sizeof (yes) == 1
typedef char no[2]; // sizeof (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>>(nullptr)) == sizeof(yes);
};
} // namespace detail
} // namespace luabridge
File diff suppressed because it is too large Load Diff