diff --git a/include/utempl/utils.hpp b/include/utempl/utils.hpp index aca9248..999f2ca 100644 --- a/include/utempl/utils.hpp +++ b/include/utempl/utils.hpp @@ -150,16 +150,6 @@ struct TupleTieMaker> { }; }; -template -struct TupleTieMaker> { - template - static inline constexpr auto Make(Arg& arg, Args&... args) -> std::array - requires(std::same_as, std::remove_cvref_t> && ...) - { - return {arg, args...}; - }; -}; - template , typename... Args> inline constexpr auto MakeTuple(Args&&... args) -> decltype(TupleMaker>::Make(std::forward(args)...)) { return TupleMaker>::Make(std::forward(args)...); @@ -293,9 +283,9 @@ struct LazyTuple { { return Get(tuple()); }; - template - inline constexpr auto operator|(FF&& ff) { - auto f = [ff = std::forward(ff), self = (*this)]() { + template > FF> + constexpr auto operator|(FF&& ff) { + auto f = [ff = std::forward(ff), self = (*this)] { return ff(self()); }; return LazyTuple{std::move(f)}; @@ -318,7 +308,28 @@ inline constexpr auto operator|(Tuple&& tuple, FF&& f) { }}; }; -template +namespace impl { + +template +concept UnpackConcept = [](std::index_sequence) { + return std::invocable(std::declval()))...>; +}(std::make_index_sequence>()); + +template +concept TransformConcept = [](std::index_sequence) { + return ((std::invocable(std::declval()))> && + !std::same_as(std::declval()))>, void>) && + ...); +}(std::make_index_sequence>()); + +template +concept ForEachConcept = [](std::index_sequence) { + return (std::invocable(std::declval()))> && ...); +}(std::make_index_sequence>()); + +} // namespace impl + +template F> inline constexpr auto Unpack(Tuple&& tuple, F&& f) -> decltype(auto) { return [&](auto... is) -> decltype(auto) { return f(Get(std::forward(tuple))...); @@ -326,55 +337,57 @@ inline constexpr auto Unpack(Tuple&& tuple, F&& f) -> decltype(auto) { }; template -inline constexpr auto Unpack(F&& f) { - return [f = std::forward(f)](Tuple&& tuple) { +constexpr auto Unpack(F&& f) { + return [f = std::forward(f)](Tuple&& tuple) -> decltype(auto) + requires impl::UnpackConcept + { return Unpack(std::forward(tuple), std::move(f)); }; }; -template -inline constexpr auto Transform(Tuple&& container, F&& f, TypeList = {}) { +template F> +constexpr auto Transform(Tuple&& container, F&& f, TypeList = {}) { return Unpack(std::forward(container), [&](Ts&&... args) { return MakeTuple(f(std::forward(args))...); }); }; template -inline constexpr auto Transform(F&& f, TypeList result = {}) { - return [f = std::forward(f), result](Tuple&& tuple) { - if constexpr(!std::is_same_v) { - return Transform(std::forward(tuple), std::move(f), result); - } else { - return Transform(std::forward(tuple), std::move(f)); - }; +constexpr auto Transform(F&& f, TypeList result = {}) { + return [f = std::forward(f), result]) { + return kType; + } else { + return kType; + }; + }())::Type>(TTuple&& tuple) + requires impl::TransformConcept + { + return Transform(std::forward(tuple), std::move(f), kType); }; }; -template -inline constexpr auto Map(Tuple&& tuple, F&& f, TypeList result = {}) { +template F> +constexpr auto Map(Tuple&& tuple, F&& f, TypeList result = {}) { return Transform(std::forward(tuple), std::forward(f), result); }; template -inline constexpr auto Map(F&& f, TypeList result = {}) { - return [f = std::forward(f), result](Tuple&& tuple) { - if constexpr(!std::is_same_v) { - return Map(std::forward(tuple), std::move(f), result); - } else { - return Map(std::forward(tuple), std::move(f)); - }; - }; +constexpr auto Map(F&& f, TypeList result = {}) -> decltype(Transform(std::forward(f), result)) { + return Transform(std::forward(f), result); }; -template -consteval auto PackConstexprWrapper() { +template +consteval auto PackConstexprWrapper() + requires TupleLike +{ return [&](std::index_sequence) { return MakeTuple(kWrapper(Tuple)>...); }(std::make_index_sequence>()); }; template -inline constexpr auto Reverse(Tuple&& tuple) { +constexpr auto Reverse(Tuple&& tuple) { return [&](std::index_sequence) { return MakeTuple(Get - Is - 1>(std::forward(tuple))...); }(std::make_index_sequence>()); @@ -399,33 +412,40 @@ struct LeftFold { template struct LeftFold { T data; - const F& f; + F f; template - inline constexpr auto operator|(LeftFold&& other) { + constexpr auto operator|(LeftFold&& other) { using R = decltype(f(std::move(this->data), std::move(other.data))); return LeftFold{.data = f(std::move(this->data), std::move(other.data)), .f = this->f}; }; }; +template +concept LeftFoldConcept = decltype(Unpack(std::declval(), [](Ts&&...) { + return kWrapper<(std::convertible_to, T> && ...)>; +}))::kValue; + } // namespace impl -template -inline constexpr auto LeftFold(Tuple&& tuple, T&& init, F&& f) { +template F> +constexpr auto LeftFold(Tuple&& tuple, T&& init, F&& f) { return Unpack(std::forward(tuple), [&](Ts&&... args) { - return (impl::LeftFold, std::remove_cvref_t>{.data = std::forward(init), .f = std::forward(f)} | ... | + return (impl::LeftFold, F>{.data = std::forward(init), .f = std::forward(f)} | ... | impl::LeftFold>{.data = std::forward(args)}) .data; }); }; -template -inline constexpr auto Reduce(Tuple&& tuple, T&& init, F&& f) { +template F> +constexpr auto Reduce(Tuple&& tuple, T&& init, F&& f) { return LeftFold(std::forward(tuple), std::forward(init), std::forward(f)); }; template -inline constexpr auto Reduce(T&& init, F&& f) { - return [init = std::forward(init), f = std::forward(f)](Tuple&& tuple) { +constexpr auto Reduce(T&& init, F&& f) { + return [init = std::forward(init), f = std::forward(f)](Tuple&& tuple) + requires impl::LeftFoldConcept + { return Reduce(std::forward(tuple), std::move(init), std::move(f)); }; }; @@ -475,15 +495,15 @@ consteval auto PartialCallerF(TypeList) { }; }; -template +template inline constexpr auto FirstOf(Tuple&& tuple, T&& init) requires kEveryElement { return LeftFold(std::forward(tuple), std::forward(init), [](TT&& value, F&& f) -> TT { if(value) { - return value; + return std::forward(value); }; - return f(); + return std::forward(f)(); }); }; @@ -494,8 +514,17 @@ inline constexpr auto FirstOf(T&& init) { }; }; -template -inline constexpr auto Filter(Tuple&& tuple, auto&& f) { +namespace impl { + +template +concept FilterConcept = decltype(Unpack(std::declval(), [](Ts&&...) { + return kWrapper<((std::invocable && std::convertible_to, bool>) && ...)>; +}))::kValue; + +} // namespace impl + +template F> +constexpr auto Filter(Tuple&& tuple, F&& f) { return LeftFold( std::forward(tuple), MakeTuple(), [&](Accumulator&& accumulator, T&& add) { if constexpr(decltype(f(std::forward(add))){}) { @@ -507,14 +536,16 @@ inline constexpr auto Filter(Tuple&& tuple, auto&& f) { }; template -inline constexpr auto Filter(F&& f) { - return [f = std::forward(f)](Tuple&& tuple) { +constexpr auto Filter(F&& f) { + return [f = std::forward(f)](Tuple&& tuple) + requires impl::FilterConcept + { return Filter(std::forward(tuple), std::move(f)); }; }; -template -inline constexpr auto ForEach(Tuple&& tuple, auto&& f) { +template F> +constexpr auto ForEach(Tuple&& tuple, F&& f) { Unpack(std::forward(tuple), [&](Ts&&... args) { (f(std::forward(args)), ...); }); @@ -583,7 +614,7 @@ consteval auto Take() { }; }; -template +template inline constexpr auto operator<<(Tuple&& tuple, T&& t) { return Unpack(std::forward(tuple), [&](Ts&&... args) { return MakeTuple(std::forward(args)..., std::forward(t)); @@ -591,10 +622,16 @@ inline constexpr auto operator<<(Tuple&& tuple, T&& t) { }; template , typename T> -inline constexpr auto Generate(T&& value) { - return [&](auto... is) { - return MakeTuple((std::ignore = is, value)...); - } | kSeq; +inline constexpr auto Generate(T&& value) + requires(std::copyable || N == 1 && std::move_constructible) +{ + if constexpr(N == 1) { + return MakeTuple(std::forward(value)); + } else { + return [&](auto... is) { + return MakeTuple((std::ignore = is, value)...); + } | kSeq; + }; }; template @@ -607,15 +644,30 @@ constexpr auto Enumerate(Tuple&& tuple) { }; template + requires requires { T{}; } constexpr auto kDefaultCreator = [] { return T{}; }; -template +concept ComparableSwitchConcept = decltype(Unpack(std::declval(), [](Ts&&... args) { + return kWrapper<(requires { args == std::declval(); } && ...)>; +}))::kValue; + +template +concept CallableSwitchConcept = decltype(Unpack(std::declval(), [](Ts&&...) { + return kWrapper<(std::convertible_to, std::optional> && ...)>; +}))::kValue; + +} // namespace impl + +template Key, + impl::CallableSwitchConcept F, std::invocable Default = decltype(kDefaultCreator)> constexpr auto Switch(KeysTuple&& keysTuple, ValuesTuple&& valuesTuple, Key&& key, F&& f, Default&& def = {}) { return Unpack(std::forward(keysTuple), [&](Keys&&... keys) {