初始化内容
This commit is contained in:
+3015
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
|
||||
+1632
File diff suppressed because it is too large
Load Diff
+124
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
+1920
File diff suppressed because it is too large
Load Diff
+2173
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
+1722
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
+1157
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user