@@ -195,8 +195,32 @@ struct function_traits_impl<R (__fastcall C::*)(Args...) const noexcept, true> :
195195};
196196#endif
197197
198+ template <class F , class = void >
199+ struct has_call_operator : std::false_type
200+ {
201+ };
202+
203+ template <class F >
204+ struct has_call_operator <F, std::void_t <decltype (&F::operator ())>> : std::true_type
205+ {
206+ };
207+
208+ template <class F >
209+ inline static constexpr bool has_call_operator_v = has_call_operator<F>::value;
210+
211+ template <class F , class = void >
212+ struct functor_traits_impl
213+ {
214+ };
215+
216+ template <class F >
217+ struct functor_traits_impl <F, std::enable_if_t <has_call_operator_v<F>>> : function_traits_impl<decltype (&F::operator ()), true>
218+ {
219+ };
220+
198221template <class F >
199- struct functor_traits_impl : function_traits_impl<decltype (&F::operator ()), true>
222+ struct functor_traits_impl <F, std::enable_if_t <!has_call_operator_v<F> && std::is_invocable_v<F&>>>
223+ : function_traits_base<false , false , std::invoke_result_t <F&>>
200224{
201225};
202226
@@ -213,6 +237,19 @@ struct function_traits : std::conditional_t<std::is_class_v<F>,
213237{
214238};
215239
240+ template <class T , bool IsClass = std::is_class_v<T>, class = void >
241+ struct has_function_traits : std::false_type
242+ {
243+ };
244+
245+ template <class T >
246+ struct has_function_traits <T, true , std::void_t <typename function_traits<T>::result_type, typename function_traits<T>::argument_types>> : std::true_type
247+ {
248+ };
249+
250+ template <class T >
251+ inline static constexpr bool has_function_traits_v = has_function_traits<T>::value;
252+
216253// =================================================================================================
217254/* *
218255 * @brief Deduces the argument type of a callble object or void in case it has no argument.
@@ -303,6 +340,12 @@ struct is_callable<T, std::void_t<decltype(&T::operator())>>
303340 static constexpr bool value = true ;
304341};
305342
343+ template <class T >
344+ struct is_callable <T, std::enable_if_t <std::is_class_v<T> && !has_call_operator_v<T> && has_function_traits_v<T>>>
345+ {
346+ static constexpr bool value = true ;
347+ };
348+
306349template <class T >
307350struct is_callable <T, std::enable_if_t <std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t <T>>>>
308351{
@@ -547,5 +590,96 @@ struct remove_first_type<std::tuple<T, Ts...>>
547590template <class T >
548591using remove_first_type_t = typename remove_first_type<T>::type;
549592
593+ // =================================================================================================
594+ /* *
595+ * @brief Drop the first N types from a tuple.
596+ */
597+ template <std::size_t N, class Tuple >
598+ struct tuple_drop_first
599+ {
600+ using type = typename tuple_drop_first<N - 1 , remove_first_type_t <Tuple>>::type;
601+ };
602+
603+ template <class Tuple >
604+ struct tuple_drop_first <0 , Tuple>
605+ {
606+ using type = Tuple;
607+ };
608+
609+ template <std::size_t N, class Tuple >
610+ using tuple_drop_first_t = typename tuple_drop_first<N, Tuple>::type;
611+
612+ // =================================================================================================
613+ /* *
614+ * @brief Internal storage for luabridge::bind_front — exposes a non-template operator() so that
615+ * function_traits can statically resolve result_type and argument_types.
616+ */
617+ template <class R , class RemainingArgsTuple , class Fn , class ... BoundArgs>
618+ struct bind_front_wrapper ;
619+
620+ template <class R , class ... Remaining, class Fn , class ... BoundArgs>
621+ struct bind_front_wrapper <R, std::tuple<Remaining...>, Fn, BoundArgs...>
622+ {
623+ Fn fn_;
624+ std::tuple<BoundArgs...> bound_;
625+
626+ template <class F , class ... BA >
627+ bind_front_wrapper (F&& f, BA &&... ba)
628+ : fn_(std::forward<F>(f)), bound_(std::forward<BA >(ba)...)
629+ {
630+ }
631+
632+ R operator ()(Remaining... args) const
633+ {
634+ return std::apply ([&](const auto &... ba) { return std::invoke (fn_, ba..., args...); }, bound_);
635+ }
636+ };
637+
550638} // namespace detail
639+
640+ // =================================================================================================
641+ /* *
642+ * @brief Drop-in replacement for std::bind_front with statically introspectable argument types.
643+ *
644+ * std::bind_front returns an object whose operator() is a template, so its argument and result
645+ * types cannot be resolved at compile time without an explicit std::function<Sig> cast. This
646+ * wrapper stores the callable and its leading bound arguments, then exposes a concrete
647+ * non-template operator() whose parameter types are derived directly from the underlying
648+ * callable's signature. This lets LuaBridge register the result with addFunction / addProperty /
649+ * addMethod without any extra annotation.
650+ *
651+ * @code
652+ * // Instead of:
653+ * ns.addFunction("add", std::function<int(int)>(std::bind_front(&add2, 10)));
654+ *
655+ * // Write:
656+ * ns.addFunction("add", luabridge::bind_front(&add2, 10));
657+ * @endcode
658+ *
659+ * For member function pointers the implicit object argument consumed by std::invoke is not
660+ * counted as part of the remaining (Lua-visible) parameter list, matching std::bind_front
661+ * semantics.
662+ *
663+ * @tparam F Callable type (function pointer, member function pointer, functor).
664+ * @tparam BoundArgs Leading argument types to bind.
665+ * @param f The callable to wrap.
666+ * @param args Leading arguments forwarded into the wrapper by value.
667+ * @return A callable object whose operator() accepts the remaining (unbound) arguments.
668+ */
669+ template <class F , class ... BoundArgs>
670+ auto bind_front (F&& f, BoundArgs&&... args)
671+ {
672+ using Fn = std::decay_t <F>;
673+ using FnTraits = detail::function_traits<Fn>;
674+
675+ static constexpr std::size_t skip = FnTraits::is_member ? 1u : 0u ;
676+ static constexpr std::size_t num_effective_bound = sizeof ...(BoundArgs) - skip;
677+
678+ using remaining = detail::tuple_drop_first_t <num_effective_bound, typename FnTraits::argument_types>;
679+ using R = typename FnTraits::result_type;
680+
681+ return detail::bind_front_wrapper<R, remaining, Fn, std::decay_t <BoundArgs>...>(
682+ std::forward<F>(f), std::forward<BoundArgs>(args)...);
683+ }
684+
551685} // namespace luabridge
0 commit comments