Skip to content

Commit d4d57cc

Browse files
authored
Support for bind_front and bind_back (#232)
1 parent 4b64aec commit d4d57cc

15 files changed

Lines changed: 1296 additions & 34 deletions

File tree

‎Distribution/LuaBridge/LuaBridge.h‎

Lines changed: 298 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <type_traits>
3535
#include <unordered_map>
3636
#include <utility>
37+
#include <variant>
3738
#include <vector>
3839

3940

@@ -299,8 +300,32 @@ struct function_traits_impl<R (__fastcall C::*)(Args...) const noexcept, true> :
299300
};
300301
#endif
301302

303+
template <class F, class = void>
304+
struct has_call_operator : std::false_type
305+
{
306+
};
307+
302308
template <class F>
303-
struct functor_traits_impl : function_traits_impl<decltype(&F::operator()), true>
309+
struct has_call_operator<F, std::void_t<decltype(&F::operator())>> : std::true_type
310+
{
311+
};
312+
313+
template <class F>
314+
inline static constexpr bool has_call_operator_v = has_call_operator<F>::value;
315+
316+
template <class F, class = void>
317+
struct functor_traits_impl
318+
{
319+
};
320+
321+
template <class F>
322+
struct functor_traits_impl<F, std::enable_if_t<has_call_operator_v<F>>> : function_traits_impl<decltype(&F::operator()), true>
323+
{
324+
};
325+
326+
template <class F>
327+
struct functor_traits_impl<F, std::enable_if_t<!has_call_operator_v<F> && std::is_invocable_v<F&>>>
328+
: function_traits_base<false, false, std::invoke_result_t<F&>>
304329
{
305330
};
306331

@@ -311,6 +336,19 @@ struct function_traits : std::conditional_t<std::is_class_v<F>,
311336
{
312337
};
313338

339+
template <class T, bool IsClass = std::is_class_v<T>, class = void>
340+
struct has_function_traits : std::false_type
341+
{
342+
};
343+
344+
template <class T>
345+
struct has_function_traits<T, true, std::void_t<typename function_traits<T>::result_type, typename function_traits<T>::argument_types>> : std::true_type
346+
{
347+
};
348+
349+
template <class T>
350+
inline static constexpr bool has_function_traits_v = has_function_traits<T>::value;
351+
314352
template <std::size_t I, class F, class = void>
315353
struct function_argument_or_void
316354
{
@@ -356,6 +394,12 @@ struct is_callable<T, std::void_t<decltype(&T::operator())>>
356394
static constexpr bool value = true;
357395
};
358396

397+
template <class T>
398+
struct is_callable<T, std::enable_if_t<std::is_class_v<T> && !has_call_operator_v<T> && has_function_traits_v<T>>>
399+
{
400+
static constexpr bool value = true;
401+
};
402+
359403
template <class T>
360404
struct is_callable<T, std::enable_if_t<std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>>>
361405
{
@@ -539,7 +583,172 @@ struct remove_first_type<std::tuple<T, Ts...>>
539583
template <class T>
540584
using remove_first_type_t = typename remove_first_type<T>::type;
541585

586+
template <std::size_t N, class Tuple>
587+
struct tuple_drop_first
588+
{
589+
using type = typename tuple_drop_first<N - 1, remove_first_type_t<Tuple>>::type;
590+
};
591+
592+
template <class Tuple>
593+
struct tuple_drop_first<0, Tuple>
594+
{
595+
using type = Tuple;
596+
};
597+
598+
template <std::size_t N, class Tuple>
599+
using tuple_drop_first_t = typename tuple_drop_first<N, Tuple>::type;
600+
601+
template <class T, class Tuple>
602+
struct tuple_prepend;
603+
604+
template <class T, class... Ts>
605+
struct tuple_prepend<T, std::tuple<Ts...>>
606+
{
607+
using type = std::tuple<T, Ts...>;
608+
};
609+
610+
template <class T, class Tuple>
611+
using tuple_prepend_t = typename tuple_prepend<T, Tuple>::type;
612+
613+
template <std::size_t N, class Tuple, class Accum = std::tuple<>>
614+
struct tuple_take_first_impl
615+
{
616+
using type = Accum;
617+
};
618+
619+
template <std::size_t N, class T, class... Ts, class... Acc>
620+
struct tuple_take_first_impl<N, std::tuple<T, Ts...>, std::tuple<Acc...>>
621+
{
622+
using type = typename tuple_take_first_impl<N - 1, std::tuple<Ts...>, std::tuple<Acc..., T>>::type;
623+
};
624+
625+
template <class T, class... Ts, class... Acc>
626+
struct tuple_take_first_impl<0, std::tuple<T, Ts...>, std::tuple<Acc...>>
627+
{
628+
using type = std::tuple<Acc...>;
629+
};
630+
631+
template <std::size_t N, class Tuple>
632+
using tuple_take_first_t = typename tuple_take_first_impl<N, Tuple>::type;
633+
634+
template <class F>
635+
struct member_function_class;
636+
637+
template <class C, class R, class... Args>
638+
struct member_function_class<R (C::*)(Args...)> { using type = C; };
639+
640+
template <class C, class R, class... Args>
641+
struct member_function_class<R (C::*)(Args...) const> { using type = const C; };
642+
643+
template <class C, class R, class... Args>
644+
struct member_function_class<R (C::*)(Args...) noexcept> { using type = C; };
645+
646+
template <class C, class R, class... Args>
647+
struct member_function_class<R (C::*)(Args...) const noexcept> { using type = const C; };
648+
649+
template <class F>
650+
using member_function_class_t = typename member_function_class<F>::type;
651+
652+
template <class Fn, class ExplicitRemaining, bool IsMember>
653+
struct bind_back_leading_impl
654+
{
655+
using type = ExplicitRemaining;
656+
};
657+
658+
template <class Fn, class ExplicitRemaining>
659+
struct bind_back_leading_impl<Fn, ExplicitRemaining, true>
660+
{
661+
using type = tuple_prepend_t<member_function_class_t<Fn>*, ExplicitRemaining>;
662+
};
663+
664+
template <class Fn, class ExplicitRemaining>
665+
using bind_back_leading_t =
666+
typename bind_back_leading_impl<Fn, ExplicitRemaining, std::is_member_function_pointer_v<Fn>>::type;
667+
668+
template <class R, class RemainingArgsTuple, class Fn, class... BoundArgs>
669+
struct bind_front_wrapper;
670+
671+
template <class R, class... Remaining, class Fn, class... BoundArgs>
672+
struct bind_front_wrapper<R, std::tuple<Remaining...>, Fn, BoundArgs...>
673+
{
674+
Fn fn_;
675+
std::tuple<BoundArgs...> bound_;
676+
677+
template <class F, class... BA>
678+
bind_front_wrapper(F&& f, BA&&... ba)
679+
: fn_(std::forward<F>(f)), bound_(std::forward<BA>(ba)...)
680+
{
681+
}
682+
683+
R operator()(Remaining... args) const
684+
{
685+
return std::apply([&](const auto&... ba) { return std::invoke(fn_, ba..., args...); }, bound_);
686+
}
687+
};
688+
542689
}
690+
691+
template <class F, class... BoundArgs>
692+
auto bind_front(F&& f, BoundArgs&&... args)
693+
{
694+
using Fn = std::decay_t<F>;
695+
using FnTraits = detail::function_traits<Fn>;
696+
697+
static constexpr std::size_t skip = std::is_member_function_pointer_v<Fn> ? 1u : 0u;
698+
static constexpr std::size_t num_effective_bound = sizeof...(BoundArgs) - skip;
699+
700+
using remaining = detail::tuple_drop_first_t<num_effective_bound, typename FnTraits::argument_types>;
701+
using R = typename FnTraits::result_type;
702+
703+
return detail::bind_front_wrapper<R, remaining, Fn, std::decay_t<BoundArgs>...>(
704+
std::forward<F>(f), std::forward<BoundArgs>(args)...);
705+
}
706+
707+
namespace detail {
708+
709+
template <class R, class LeadingArgsTuple, class Fn, class... BoundArgs>
710+
struct bind_back_wrapper;
711+
712+
template <class R, class... Leading, class Fn, class... BoundArgs>
713+
struct bind_back_wrapper<R, std::tuple<Leading...>, Fn, BoundArgs...>
714+
{
715+
Fn fn_;
716+
std::tuple<BoundArgs...> bound_;
717+
718+
template <class F, class... BA>
719+
bind_back_wrapper(F&& f, BA&&... ba)
720+
: fn_(std::forward<F>(f)), bound_(std::forward<BA>(ba)...)
721+
{
722+
}
723+
724+
R operator()(Leading... args) const
725+
{
726+
return std::apply([&](const auto&... ba) { return std::invoke(fn_, args..., ba...); }, bound_);
727+
}
728+
};
729+
730+
}
731+
732+
template <class F, class... BoundArgs>
733+
auto bind_back(F&& f, BoundArgs&&... args)
734+
{
735+
using Fn = std::decay_t<F>;
736+
using FnTraits = detail::function_traits<Fn>;
737+
738+
static constexpr std::size_t num_explicit = FnTraits::arity;
739+
static constexpr std::size_t num_bound = sizeof...(BoundArgs);
740+
static constexpr std::size_t num_remaining = num_explicit - num_bound;
741+
742+
using explicit_remaining = detail::tuple_take_first_t<num_remaining, typename FnTraits::argument_types>;
743+
744+
using leading = detail::bind_back_leading_t<Fn, explicit_remaining>;
745+
746+
using R = typename FnTraits::result_type;
747+
748+
return detail::bind_back_wrapper<R, leading, Fn, std::decay_t<BoundArgs>...>(
749+
std::forward<F>(f), std::forward<BoundArgs>(args)...);
750+
}
751+
543752
}
544753

545754

@@ -6066,10 +6275,10 @@ inline void dumpState(lua_State* L, unsigned maxDepth = 1, std::ostream& stream
60666275

60676276
namespace luabridge {
60686277

6069-
template <class T>
6070-
struct Stack<std::list<T>>
6278+
template <class T, class Allocator>
6279+
struct Stack<std::list<T, Allocator>>
60716280
{
6072-
using Type = std::list<T>;
6281+
using Type = std::list<T, Allocator>;
60736282

60746283
[[nodiscard]] static Result push(lua_State* L, const Type& list)
60756284
{
@@ -12663,10 +12872,10 @@ ScopeGuard(F&&) -> ScopeGuard<F>;
1266312872
1266412873
namespace luabridge {
1266512874
12666-
template <class K, class V>
12667-
struct Stack<std::map<K, V>>
12875+
template <class K, class V, class Compare, class Allocator>
12876+
struct Stack<std::map<K, V, Compare, Allocator>>
1266812877
{
12669-
using Type = std::map<K, V>;
12878+
using Type = std::map<K, V, Compare, Allocator>;
1267012879
1267112880
[[nodiscard]] static Result push(lua_State* L, const Type& map)
1267212881
{
@@ -12740,10 +12949,10 @@ struct Stack<std::map<K, V>>
1274012949
1274112950
namespace luabridge {
1274212951
12743-
template <class K>
12744-
struct Stack<std::set<K>>
12952+
template <class K, class Compare, class Allocator>
12953+
struct Stack<std::set<K, Compare, Allocator>>
1274512954
{
12746-
using Type = std::set<K>;
12955+
using Type = std::set<K, Compare, Allocator>;
1274712956
1274812957
[[nodiscard]] static Result push(lua_State* L, const Type& set)
1274912958
{
@@ -12812,10 +13021,10 @@ struct Stack<std::set<K>>
1281213021
1281313022
namespace luabridge {
1281413023
12815-
template <class K, class V>
12816-
struct Stack<std::unordered_map<K, V>>
13024+
template <class K, class V, class Hash, class KeyEqual, class Allocator>
13025+
struct Stack<std::unordered_map<K, V, Hash, KeyEqual, Allocator>>
1281713026
{
12818-
using Type = std::unordered_map<K, V>;
13027+
using Type = std::unordered_map<K, V, Hash, KeyEqual, Allocator>;
1281913028
1282013029
[[nodiscard]] static Result push(lua_State* L, const Type& map)
1282113030
{
@@ -12885,14 +13094,87 @@ struct Stack<std::unordered_map<K, V>>
1288513094
1288613095
// End File: Source/LuaBridge/UnorderedMap.h
1288713096
13097+
// Begin File: Source/LuaBridge/Variant.h
13098+
13099+
namespace luabridge {
13100+
13101+
template <class... Args>
13102+
struct Stack<std::variant<Args...>>
13103+
{
13104+
using Type = std::variant<Args...>;
13105+
13106+
[[nodiscard]] static Result push(lua_State* L, const Type& variant)
13107+
{
13108+
return std::visit([L](const auto& value) { return Stack<std::decay_t<decltype(value)>>::push(L, value); }, variant);
13109+
}
13110+
13111+
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
13112+
{
13113+
return tryGet<Args...>(L, index);
13114+
}
13115+
13116+
[[nodiscard]] static bool isInstance(lua_State* L, int index)
13117+
{
13118+
return (Stack<Args>::isInstance(L, index) || ...);
13119+
}
13120+
13121+
private:
13122+
template <class T, class... Rest>
13123+
[[nodiscard]] static TypeResult<Type> tryGet(lua_State* L, int index)
13124+
{
13125+
if (auto value = Stack<T>::get(L, index))
13126+
return Type{ std::in_place_type<T>, std::move(*value) };
13127+
13128+
if constexpr (sizeof...(Rest) > 0)
13129+
return tryGet<Rest...>(L, index);
13130+
13131+
return makeErrorCode(ErrorCode::InvalidTypeCast);
13132+
}
13133+
};
13134+
13135+
template <>
13136+
struct Stack<std::monostate>
13137+
{
13138+
using Type = std::monostate;
13139+
13140+
[[nodiscard]] static Result push(lua_State* L, const Type&)
13141+
{
13142+
#if LUABRIDGE_SAFE_STACK_CHECKS
13143+
if (! lua_checkstack(L, 1))
13144+
return makeErrorCode(ErrorCode::LuaStackOverflow);
13145+
#endif
13146+
13147+
lua_pushnil(L);
13148+
return {};
13149+
}
13150+
13151+
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
13152+
{
13153+
if (lua_isnoneornil(L, index))
13154+
return Type{};
13155+
13156+
return makeErrorCode(ErrorCode::InvalidTypeCast);
13157+
}
13158+
13159+
[[nodiscard]] static bool isInstance(lua_State* L, int index)
13160+
{
13161+
return lua_isnoneornil(L, index);
13162+
}
13163+
};
13164+
13165+
}
13166+
13167+
13168+
// End File: Source/LuaBridge/Variant.h
13169+
1288813170
// Begin File: Source/LuaBridge/Vector.h
1288913171
1289013172
namespace luabridge {
1289113173
12892-
template <class T>
12893-
struct Stack<std::vector<T>>
13174+
template <class T, class Allocator>
13175+
struct Stack<std::vector<T, Allocator>>
1289413176
{
12895-
using Type = std::vector<T>;
13177+
using Type = std::vector<T, Allocator>;
1289613178
1289713179
[[nodiscard]] static Result push(lua_State* L, const Type& vector)
1289813180
{

0 commit comments

Comments
 (0)