// https://github.com/kunitoki/LuaBridge3 // Copyright 2020, kunitoki // Copyright 2020, Dmitry Tarakanov // Copyright 2019, George Tokmaji // Copyright 2012, Vinnie Falco // SPDX-License-Identifier: MIT #pragma once #include "Config.h" #include #include 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> type; }; template using remove_cvref_t = typename remove_cvref::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 struct function_traits_base { using result_type = R; using argument_types = std::tuple; static constexpr auto arity = sizeof...(Args); static constexpr auto is_member = IsMember; static constexpr auto is_const = IsConst; }; template struct function_traits_impl; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; #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; inline static constexpr bool is_fastcall_default_calling_convention = std::is_same_v; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; template struct function_traits_impl : function_traits_base { }; #endif template struct has_call_operator : std::false_type { }; template struct has_call_operator> : std::true_type { }; template inline static constexpr bool has_call_operator_v = has_call_operator::value; template struct is_move_only_function : std::false_type {}; #if LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION template struct is_move_only_function> : std::true_type {}; template struct is_move_only_function> : std::true_type {}; template struct is_move_only_function> : std::true_type {}; template struct is_move_only_function> : std::true_type {}; #endif template inline static constexpr bool is_move_only_function_v = is_move_only_function::value; template struct functor_traits_impl { }; template struct functor_traits_impl>> : function_traits_impl { }; template struct functor_traits_impl && std::is_invocable_v && !is_move_only_function_v>> : function_traits_base> { }; #if LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION template struct functor_traits_impl> : function_traits_base { }; template struct functor_traits_impl> : function_traits_base { }; template struct functor_traits_impl> : function_traits_base { }; template struct functor_traits_impl> : function_traits_base { }; #endif // LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION //================================================================================================= /** * @brief Traits class for callable objects (e.g. function pointers, lambdas) * * @tparam F Callable object. */ template struct function_traits : std::conditional_t, detail::functor_traits_impl, detail::function_traits_impl> { }; template , class = void> struct has_function_traits : std::false_type { }; template struct has_function_traits::result_type, typename function_traits::argument_types>> : std::true_type { }; template inline static constexpr bool has_function_traits_v = has_function_traits::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 struct function_argument_or_void { using type = void; }; template struct function_argument_or_void::argument_types>>> { using type = std::tuple_element_t::argument_types>; }; template using function_argument_or_void_t = typename function_argument_or_void::type; //================================================================================================= /** * @brief Deduces the return type of a callble object. * * @tparam F Callable object. */ template using function_result_t = typename function_traits::result_type; /** * @brief Deduces the argument type of a callble object. * * @tparam I Argument index. * @tparam F Callable object. */ template using function_argument_t = std::tuple_element_t::argument_types>; /** * @brief Deduces the arguments type of a callble object. * * @tparam F Callable object. */ template using function_arguments_t = typename function_traits::argument_types; /** * @brief An integral constant expression that gives the number of arguments accepted by the callable object. * * @tparam F Callable object. */ template static constexpr std::size_t function_arity_v = function_traits::arity; /** * @brief An boolean constant expression that checks if the callable object is a member function. * * @tparam F Callable object. */ template static constexpr bool function_is_member_v = function_traits::is_member; /** * @brief An boolean constant expression that checks if the callable object is const. * * @tparam F Callable object. */ template static constexpr bool function_is_const_v = function_traits::is_const; //================================================================================================= /** * @brief Detect if we T is a callable object. * * @tparam T Potentially callable object. */ template struct is_callable { static constexpr bool value = false; }; template struct is_callable> { static constexpr bool value = true; }; template struct is_callable && !has_call_operator_v && has_function_traits_v>> { static constexpr bool value = true; }; template struct is_callable && std::is_function_v>>> { static constexpr bool value = true; }; template struct is_callable>> { static constexpr bool value = true; }; template inline static constexpr bool is_callable_v = is_callable::value; //================================================================================================= /** * @brief Detect if we T is a const member function pointer. * * @tparam T Potentially const member function pointer. */ template struct is_const_member_function_pointer { static constexpr bool value = false; }; template struct is_const_member_function_pointer { static constexpr bool value = false; }; template struct is_const_member_function_pointer { static constexpr bool value = true; }; template struct is_const_member_function_pointer { static constexpr bool value = false; }; template struct is_const_member_function_pointer { static constexpr bool value = true; }; template inline static constexpr bool is_const_member_function_pointer_v = is_const_member_function_pointer::value; //================================================================================================= /** * @brief Detect if T is a lua cfunction pointer. * * @tparam T Potentially lua cfunction pointer. */ template struct is_cfunction_pointer { static constexpr bool value = false; }; template <> struct is_cfunction_pointer { static constexpr bool value = true; }; template inline static constexpr bool is_cfunction_pointer_v = is_cfunction_pointer::value; //================================================================================================= /** * @brief Detect if T is a member lua cfunction pointer. * * @tparam T Potentially member lua cfunction pointer. */ template struct is_member_cfunction_pointer { static constexpr bool value = false; }; template struct is_member_cfunction_pointer { static constexpr bool value = true; }; template struct is_member_cfunction_pointer { static constexpr bool value = true; }; template inline static constexpr bool is_member_cfunction_pointer_v = is_member_cfunction_pointer::value; /** * @brief Detect if T is a const member lua cfunction pointer. * * @tparam T Potentially const member lua cfunction pointer. */ template struct is_const_member_cfunction_pointer { static constexpr bool value = false; }; template struct is_const_member_cfunction_pointer { static constexpr bool value = false; }; template struct is_const_member_cfunction_pointer { static constexpr bool value = true; }; template inline static constexpr bool is_const_member_cfunction_pointer_v = is_const_member_cfunction_pointer::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 inline static constexpr bool is_any_cfunction_pointer_v = is_cfunction_pointer_v || is_member_cfunction_pointer_v; //================================================================================================= /** * @brief A constexpr check for proxy_member functions. * * @tparam T Type where the callable should be able to operate. * @tparam F Callable object. */ template inline static constexpr bool is_proxy_member_function_v = !std::is_member_function_pointer_v && std::is_same_v>>>; template inline static constexpr bool is_const_proxy_function_v = is_proxy_member_function_v && std::is_const_v>>>; //================================================================================================= /** * @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 struct function_arity_excluding { }; template < class... Ts, class ExclusionType> struct function_arity_excluding, ExclusionType> : std::integral_constant, ExclusionType> ? 0 : 1))> { }; template inline static constexpr std::size_t function_arity_excluding_v = function_arity_excluding, 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 struct member_function_arity_excluding { }; template struct member_function_arity_excluding, ExclusionType, std::enable_if_t>> : std::integral_constant, ExclusionType> ? 0 : 1))> { }; template struct member_function_arity_excluding, ExclusionType, std::enable_if_t>> : std::integral_constant, ExclusionType> ? 0 : 1)) - 1> { }; template inline static constexpr std::size_t member_function_arity_excluding_v = member_function_arity_excluding, ExclusionType>::value; //================================================================================================= /** * @brief Detectors for const and non const functions in packs and counting them. */ template static constexpr bool is_const_function = detail::is_const_member_function_pointer_v || (detail::function_arity_v > 0 && detail::is_const_proxy_function_v); template inline static constexpr std::size_t const_functions_count = (0 + ... + (is_const_function ? 1 : 0)); template inline static constexpr std::size_t non_const_functions_count = (0 + ... + (is_const_function ? 0 : 1)); //================================================================================================= /** * @brief Simple make_tuple alternative that doesn't decay the types. * * @tparam Types Argument types that will compose the tuple. */ template constexpr auto tupleize(Types&&... types) { return std::tuple(std::forward(types)...); } //================================================================================================= /** * @brief Remove first type from tuple. */ template struct remove_first_type { }; template struct remove_first_type> { using type = std::tuple; }; template using remove_first_type_t = typename remove_first_type::type; //================================================================================================= /** * @brief Drop the first N types from a tuple. */ template struct tuple_drop_first { using type = typename tuple_drop_first>::type; }; template struct tuple_drop_first<0, Tuple> { using type = Tuple; }; template using tuple_drop_first_t = typename tuple_drop_first::type; //================================================================================================= /** * @brief Prepend a type to a tuple. */ template struct tuple_prepend; template struct tuple_prepend> { using type = std::tuple; }; template using tuple_prepend_t = typename tuple_prepend::type; //================================================================================================= /** * @brief Take only the first N types from a tuple (uses an accumulator to avoid ambiguity). */ template > struct tuple_take_first_impl { using type = Accum; }; template struct tuple_take_first_impl, std::tuple> { using type = typename tuple_take_first_impl, std::tuple>::type; }; template struct tuple_take_first_impl<0, std::tuple, std::tuple> { using type = std::tuple; }; template using tuple_take_first_t = typename tuple_take_first_impl::type; //================================================================================================= /** * @brief Extracts the class type from a member function pointer. */ template struct member_function_class; template struct member_function_class { using type = C; }; template struct member_function_class { using type = const C; }; template struct member_function_class { using type = C; }; template struct member_function_class { using type = const C; }; template using member_function_class_t = typename member_function_class::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 struct bind_back_leading_impl { using type = ExplicitRemaining; }; template struct bind_back_leading_impl { using type = tuple_prepend_t*, ExplicitRemaining>; }; template using bind_back_leading_t = typename bind_back_leading_impl>::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 struct bind_front_wrapper; template struct bind_front_wrapper, Fn, BoundArgs...> { Fn fn_; std::tuple bound_; template bind_front_wrapper(F&& f, BA&&... ba) : fn_(std::forward(f)), bound_(std::forward(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 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 auto bind_front(F&& f, BoundArgs&&... args) { using Fn = std::decay_t; using FnTraits = detail::function_traits; static constexpr std::size_t skip = std::is_member_function_pointer_v ? 1u : 0u; static constexpr std::size_t num_effective_bound = sizeof...(BoundArgs) - skip; using remaining = detail::tuple_drop_first_t; using R = typename FnTraits::result_type; return detail::bind_front_wrapper...>( std::forward(f), std::forward(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 struct bind_back_wrapper; template struct bind_back_wrapper, Fn, BoundArgs...> { Fn fn_; std::tuple bound_; template bind_back_wrapper(F&& f, BA&&... ba) : fn_(std::forward(f)), bound_(std::forward(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 auto bind_back(F&& f, BoundArgs&&... args) { using Fn = std::decay_t; using FnTraits = detail::function_traits; 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; using leading = detail::bind_back_leading_t; using R = typename FnTraits::result_type; return detail::bind_back_wrapper...>( std::forward(f), std::forward(args)...); } } // namespace luabridge