Add concepts for functions
This commit is contained in:
parent
e7cf38e2a9
commit
eaddebf1e5
1 changed files with 116 additions and 64 deletions
|
@ -150,16 +150,6 @@ struct TupleTieMaker<Tuple<Ts...>> {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, std::size_t N>
|
|
||||||
struct TupleTieMaker<std::array<T, N>> {
|
|
||||||
template <typename Arg, typename... Args>
|
|
||||||
static inline constexpr auto Make(Arg& arg, Args&... args) -> std::array<Arg&, sizeof...(Args) + 1>
|
|
||||||
requires(std::same_as<std::remove_cvref_t<Arg>, std::remove_cvref_t<Args>> && ...)
|
|
||||||
{
|
|
||||||
return {arg, args...};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T = Tuple<>, typename... Args>
|
template <typename T = Tuple<>, typename... Args>
|
||||||
inline constexpr auto MakeTuple(Args&&... args) -> decltype(TupleMaker<std::remove_cvref_t<T>>::Make(std::forward<Args>(args)...)) {
|
inline constexpr auto MakeTuple(Args&&... args) -> decltype(TupleMaker<std::remove_cvref_t<T>>::Make(std::forward<Args>(args)...)) {
|
||||||
return TupleMaker<std::remove_cvref_t<T>>::Make(std::forward<Args>(args)...);
|
return TupleMaker<std::remove_cvref_t<T>>::Make(std::forward<Args>(args)...);
|
||||||
|
@ -293,9 +283,9 @@ struct LazyTuple {
|
||||||
{
|
{
|
||||||
return Get<I>(tuple());
|
return Get<I>(tuple());
|
||||||
};
|
};
|
||||||
template <typename FF>
|
template <std::invocable<std::invoke_result_t<F>> FF>
|
||||||
inline constexpr auto operator|(FF&& ff) {
|
constexpr auto operator|(FF&& ff) {
|
||||||
auto f = [ff = std::forward<FF>(ff), self = (*this)]() {
|
auto f = [ff = std::forward<FF>(ff), self = (*this)] {
|
||||||
return ff(self());
|
return ff(self());
|
||||||
};
|
};
|
||||||
return LazyTuple<decltype(f)>{std::move(f)};
|
return LazyTuple<decltype(f)>{std::move(f)};
|
||||||
|
@ -318,7 +308,28 @@ inline constexpr auto operator|(Tuple&& tuple, FF&& f) {
|
||||||
}};
|
}};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <TupleLike Tuple, typename F>
|
namespace impl {
|
||||||
|
|
||||||
|
template <typename F, typename Tuple>
|
||||||
|
concept UnpackConcept = []<std::size_t... Is>(std::index_sequence<Is...>) {
|
||||||
|
return std::invocable<F, decltype(Get<Is>(std::declval<Tuple>()))...>;
|
||||||
|
}(std::make_index_sequence<kTupleSize<Tuple>>());
|
||||||
|
|
||||||
|
template <typename F, typename Tuple>
|
||||||
|
concept TransformConcept = []<std::size_t... Is>(std::index_sequence<Is...>) {
|
||||||
|
return ((std::invocable<F, decltype(Get<Is>(std::declval<Tuple>()))> &&
|
||||||
|
!std::same_as<std::invoke_result_t<F, decltype(Get<Is>(std::declval<Tuple>()))>, void>) &&
|
||||||
|
...);
|
||||||
|
}(std::make_index_sequence<kTupleSize<Tuple>>());
|
||||||
|
|
||||||
|
template <typename F, typename Tuple>
|
||||||
|
concept ForEachConcept = []<std::size_t... Is>(std::index_sequence<Is...>) {
|
||||||
|
return (std::invocable<F, decltype(Get<Is>(std::declval<Tuple>()))> && ...);
|
||||||
|
}(std::make_index_sequence<kTupleSize<Tuple>>());
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
template <TupleLike Tuple, impl::UnpackConcept<Tuple> F>
|
||||||
inline constexpr auto Unpack(Tuple&& tuple, F&& f) -> decltype(auto) {
|
inline constexpr auto Unpack(Tuple&& tuple, F&& f) -> decltype(auto) {
|
||||||
return [&](auto... is) -> decltype(auto) {
|
return [&](auto... is) -> decltype(auto) {
|
||||||
return f(Get<is>(std::forward<Tuple>(tuple))...);
|
return f(Get<is>(std::forward<Tuple>(tuple))...);
|
||||||
|
@ -326,55 +337,57 @@ inline constexpr auto Unpack(Tuple&& tuple, F&& f) -> decltype(auto) {
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
inline constexpr auto Unpack(F&& f) {
|
constexpr auto Unpack(F&& f) {
|
||||||
return [f = std::forward<F>(f)]<TupleLike Tuple>(Tuple&& tuple) {
|
return [f = std::forward<F>(f)]<TupleLike Tuple>(Tuple&& tuple) -> decltype(auto)
|
||||||
|
requires impl::UnpackConcept<F, Tuple>
|
||||||
|
{
|
||||||
return Unpack(std::forward<Tuple>(tuple), std::move(f));
|
return Unpack(std::forward<Tuple>(tuple), std::move(f));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <TupleLike Tuple, typename R = Tuple, typename F>
|
template <TupleLike Tuple, TupleLike R = Tuple, impl::TransformConcept<Tuple> F>
|
||||||
inline constexpr auto Transform(Tuple&& container, F&& f, TypeList<R> = {}) {
|
constexpr auto Transform(Tuple&& container, F&& f, TypeList<R> = {}) {
|
||||||
return Unpack(std::forward<Tuple>(container), [&]<typename... Ts>(Ts&&... args) {
|
return Unpack(std::forward<Tuple>(container), [&]<typename... Ts>(Ts&&... args) {
|
||||||
return MakeTuple<R>(f(std::forward<Ts>(args))...);
|
return MakeTuple<R>(f(std::forward<Ts>(args))...);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename F, typename R = void>
|
template <typename F, typename R = void>
|
||||||
inline constexpr auto Transform(F&& f, TypeList<R> result = {}) {
|
constexpr auto Transform(F&& f, TypeList<R> result = {}) {
|
||||||
return [f = std::forward<F>(f), result]<TupleLike Tuple>(Tuple&& tuple) {
|
return [f = std::forward<F>(f), result]<TupleLike TTuple, typename RR = decltype([] {
|
||||||
if constexpr(!std::is_same_v<R, void>) {
|
if constexpr(std::same_as<R, void>) {
|
||||||
return Transform(std::forward<Tuple>(tuple), std::move(f), result);
|
return kType<TTuple>;
|
||||||
} else {
|
} else {
|
||||||
return Transform(std::forward<Tuple>(tuple), std::move(f));
|
return kType<R>;
|
||||||
};
|
};
|
||||||
|
}())::Type>(TTuple&& tuple)
|
||||||
|
requires impl::TransformConcept<F, TTuple>
|
||||||
|
{
|
||||||
|
return Transform(std::forward<TTuple>(tuple), std::move(f), kType<RR>);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <TupleLike Tuple, typename R = Tuple, typename F>
|
template <TupleLike Tuple, TupleLike R = Tuple, impl::TransformConcept<Tuple> F>
|
||||||
inline constexpr auto Map(Tuple&& tuple, F&& f, TypeList<R> result = {}) {
|
constexpr auto Map(Tuple&& tuple, F&& f, TypeList<R> result = {}) {
|
||||||
return Transform(std::forward<Tuple>(tuple), std::forward<F>(f), result);
|
return Transform(std::forward<Tuple>(tuple), std::forward<F>(f), result);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename F, typename R = void>
|
template <typename F, typename R = void>
|
||||||
inline constexpr auto Map(F&& f, TypeList<R> result = {}) {
|
constexpr auto Map(F&& f, TypeList<R> result = {}) -> decltype(Transform(std::forward<F>(f), result)) {
|
||||||
return [f = std::forward<F>(f), result]<TupleLike Tuple>(Tuple&& tuple) {
|
return Transform(std::forward<F>(f), result);
|
||||||
if constexpr(!std::is_same_v<R, void>) {
|
|
||||||
return Map(std::forward<Tuple>(tuple), std::move(f), result);
|
|
||||||
} else {
|
|
||||||
return Map(std::forward<Tuple>(tuple), std::move(f));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <auto Tuple, typename To = decltype(Tuple)>
|
template <auto Tuple, TupleLike To = decltype(Tuple)>
|
||||||
consteval auto PackConstexprWrapper() {
|
consteval auto PackConstexprWrapper()
|
||||||
|
requires TupleLike<decltype(Tuple)>
|
||||||
|
{
|
||||||
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
|
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
|
||||||
return MakeTuple<To>(kWrapper<Get<Is>(Tuple)>...);
|
return MakeTuple<To>(kWrapper<Get<Is>(Tuple)>...);
|
||||||
}(std::make_index_sequence<kTupleSize<decltype(Tuple)>>());
|
}(std::make_index_sequence<kTupleSize<decltype(Tuple)>>());
|
||||||
};
|
};
|
||||||
|
|
||||||
template <TupleLike Tuple>
|
template <TupleLike Tuple>
|
||||||
inline constexpr auto Reverse(Tuple&& tuple) {
|
constexpr auto Reverse(Tuple&& tuple) {
|
||||||
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
|
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
|
||||||
return MakeTuple<Tuple>(Get<kTupleSize<Tuple> - Is - 1>(std::forward<Tuple>(tuple))...);
|
return MakeTuple<Tuple>(Get<kTupleSize<Tuple> - Is - 1>(std::forward<Tuple>(tuple))...);
|
||||||
}(std::make_index_sequence<kTupleSize<Tuple>>());
|
}(std::make_index_sequence<kTupleSize<Tuple>>());
|
||||||
|
@ -399,33 +412,40 @@ struct LeftFold<T> {
|
||||||
template <typename T, typename F>
|
template <typename T, typename F>
|
||||||
struct LeftFold<T, F> {
|
struct LeftFold<T, F> {
|
||||||
T data;
|
T data;
|
||||||
const F& f;
|
F f;
|
||||||
template <typename TT>
|
template <typename TT>
|
||||||
inline constexpr auto operator|(LeftFold<TT>&& other) {
|
constexpr auto operator|(LeftFold<TT>&& other) {
|
||||||
using R = decltype(f(std::move(this->data), std::move(other.data)));
|
using R = decltype(f(std::move(this->data), std::move(other.data)));
|
||||||
return LeftFold<R, F>{.data = f(std::move(this->data), std::move(other.data)), .f = this->f};
|
return LeftFold<R, F>{.data = f(std::move(this->data), std::move(other.data)), .f = this->f};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename F, typename T, typename Tuple>
|
||||||
|
concept LeftFoldConcept = decltype(Unpack(std::declval<Tuple>(), []<typename... Ts>(Ts&&...) {
|
||||||
|
return kWrapper<(std::convertible_to<std::invoke_result_t<F, T, Ts>, T> && ...)>;
|
||||||
|
}))::kValue;
|
||||||
|
|
||||||
} // namespace impl
|
} // namespace impl
|
||||||
|
|
||||||
template <TupleLike Tuple, typename T, typename F>
|
template <TupleLike Tuple, std::move_constructible T, impl::LeftFoldConcept<T, Tuple> F>
|
||||||
inline constexpr auto LeftFold(Tuple&& tuple, T&& init, F&& f) {
|
constexpr auto LeftFold(Tuple&& tuple, T&& init, F&& f) {
|
||||||
return Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) {
|
return Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) {
|
||||||
return (impl::LeftFold<std::remove_cvref_t<T>, std::remove_cvref_t<F>>{.data = std::forward<T>(init), .f = std::forward<F>(f)} | ... |
|
return (impl::LeftFold<std::remove_cvref_t<T>, F>{.data = std::forward<T>(init), .f = std::forward<F>(f)} | ... |
|
||||||
impl::LeftFold<std::remove_cvref_t<Ts>>{.data = std::forward<Ts>(args)})
|
impl::LeftFold<std::remove_cvref_t<Ts>>{.data = std::forward<Ts>(args)})
|
||||||
.data;
|
.data;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
template <TupleLike Tuple, typename T, typename F>
|
template <TupleLike Tuple, std::move_constructible T, impl::LeftFoldConcept<T, Tuple> F>
|
||||||
inline constexpr auto Reduce(Tuple&& tuple, T&& init, F&& f) {
|
constexpr auto Reduce(Tuple&& tuple, T&& init, F&& f) {
|
||||||
return LeftFold(std::forward<Tuple>(tuple), std::forward<T>(init), std::forward<F>(f));
|
return LeftFold(std::forward<Tuple>(tuple), std::forward<T>(init), std::forward<F>(f));
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename F>
|
template <typename T, typename F>
|
||||||
inline constexpr auto Reduce(T&& init, F&& f) {
|
constexpr auto Reduce(T&& init, F&& f) {
|
||||||
return [init = std::forward<T>(init), f = std::forward<F>(f)]<TupleLike Tuple>(Tuple&& tuple) {
|
return [init = std::forward<T>(init), f = std::forward<F>(f)]<TupleLike Tuple>(Tuple&& tuple)
|
||||||
|
requires impl::LeftFoldConcept<F, T, Tuple>
|
||||||
|
{
|
||||||
return Reduce(std::forward<Tuple>(tuple), std::move(init), std::move(f));
|
return Reduce(std::forward<Tuple>(tuple), std::move(init), std::move(f));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -475,15 +495,15 @@ consteval auto PartialCallerF(TypeList<Ts...>) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <TupleLike Tuple, typename T>
|
template <TupleLike Tuple, std::move_constructible T>
|
||||||
inline constexpr auto FirstOf(Tuple&& tuple, T&& init)
|
inline constexpr auto FirstOf(Tuple&& tuple, T&& init)
|
||||||
requires kEveryElement<std::is_invocable, Tuple>
|
requires kEveryElement<std::is_invocable, Tuple>
|
||||||
{
|
{
|
||||||
return LeftFold(std::forward<Tuple>(tuple), std::forward<T>(init), []<typename TT, typename F>(TT&& value, F&& f) -> TT {
|
return LeftFold(std::forward<Tuple>(tuple), std::forward<T>(init), []<typename TT, typename F>(TT&& value, F&& f) -> TT {
|
||||||
if(value) {
|
if(value) {
|
||||||
return value;
|
return std::forward<TT>(value);
|
||||||
};
|
};
|
||||||
return f();
|
return std::forward<F>(f)();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -494,8 +514,17 @@ inline constexpr auto FirstOf(T&& init) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <TupleLike Tuple>
|
namespace impl {
|
||||||
inline constexpr auto Filter(Tuple&& tuple, auto&& f) {
|
|
||||||
|
template <typename F, typename Tuple>
|
||||||
|
concept FilterConcept = decltype(Unpack(std::declval<Tuple>(), []<typename... Ts>(Ts&&...) {
|
||||||
|
return kWrapper<((std::invocable<F, Ts> && std::convertible_to<std::invoke_result_t<F, Ts>, bool>) && ...)>;
|
||||||
|
}))::kValue;
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
template <TupleLike Tuple, impl::FilterConcept<Tuple> F>
|
||||||
|
constexpr auto Filter(Tuple&& tuple, F&& f) {
|
||||||
return LeftFold(
|
return LeftFold(
|
||||||
std::forward<Tuple>(tuple), MakeTuple<Tuple>(), [&]<TupleLike Accumulator, typename T>(Accumulator&& accumulator, T&& add) {
|
std::forward<Tuple>(tuple), MakeTuple<Tuple>(), [&]<TupleLike Accumulator, typename T>(Accumulator&& accumulator, T&& add) {
|
||||||
if constexpr(decltype(f(std::forward<T>(add))){}) {
|
if constexpr(decltype(f(std::forward<T>(add))){}) {
|
||||||
|
@ -507,14 +536,16 @@ inline constexpr auto Filter(Tuple&& tuple, auto&& f) {
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
inline constexpr auto Filter(F&& f) {
|
constexpr auto Filter(F&& f) {
|
||||||
return [f = std::forward<F>(f)]<TupleLike Tuple>(Tuple&& tuple) {
|
return [f = std::forward<F>(f)]<TupleLike Tuple>(Tuple&& tuple)
|
||||||
|
requires impl::FilterConcept<F, Tuple>
|
||||||
|
{
|
||||||
return Filter(std::forward<Tuple>(tuple), std::move(f));
|
return Filter(std::forward<Tuple>(tuple), std::move(f));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <TupleLike Tuple>
|
template <TupleLike Tuple, impl::ForEachConcept<Tuple> F>
|
||||||
inline constexpr auto ForEach(Tuple&& tuple, auto&& f) {
|
constexpr auto ForEach(Tuple&& tuple, F&& f) {
|
||||||
Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) {
|
Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) {
|
||||||
(f(std::forward<Ts>(args)), ...);
|
(f(std::forward<Ts>(args)), ...);
|
||||||
});
|
});
|
||||||
|
@ -583,7 +614,7 @@ consteval auto Take() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <TupleLike Tuple, typename T>
|
template <TupleLike Tuple, std::move_constructible T>
|
||||||
inline constexpr auto operator<<(Tuple&& tuple, T&& t) {
|
inline constexpr auto operator<<(Tuple&& tuple, T&& t) {
|
||||||
return Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) {
|
return Unpack(std::forward<Tuple>(tuple), [&]<typename... Ts>(Ts&&... args) {
|
||||||
return MakeTuple<Tuple>(std::forward<Ts>(args)..., std::forward<T>(t));
|
return MakeTuple<Tuple>(std::forward<Ts>(args)..., std::forward<T>(t));
|
||||||
|
@ -591,10 +622,16 @@ inline constexpr auto operator<<(Tuple&& tuple, T&& t) {
|
||||||
};
|
};
|
||||||
|
|
||||||
template <std::size_t N, TupleLike Tuple = Tuple<>, typename T>
|
template <std::size_t N, TupleLike Tuple = Tuple<>, typename T>
|
||||||
inline constexpr auto Generate(T&& value) {
|
inline constexpr auto Generate(T&& value)
|
||||||
|
requires(std::copyable<T> || N == 1 && std::move_constructible<T>)
|
||||||
|
{
|
||||||
|
if constexpr(N == 1) {
|
||||||
|
return MakeTuple<Tuple>(std::forward<T>(value));
|
||||||
|
} else {
|
||||||
return [&](auto... is) {
|
return [&](auto... is) {
|
||||||
return MakeTuple<Tuple>((std::ignore = is, value)...);
|
return MakeTuple<Tuple>((std::ignore = is, value)...);
|
||||||
} | kSeq<N>;
|
} | kSeq<N>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <TupleLike Tuple>
|
template <TupleLike Tuple>
|
||||||
|
@ -607,15 +644,30 @@ constexpr auto Enumerate(Tuple&& tuple) {
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
requires requires { T{}; }
|
||||||
constexpr auto kDefaultCreator = [] {
|
constexpr auto kDefaultCreator = [] {
|
||||||
return T{};
|
return T{};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename R,
|
namespace impl {
|
||||||
|
|
||||||
|
template <typename Key, typename KeysTuple>
|
||||||
|
concept ComparableSwitchConcept = decltype(Unpack(std::declval<KeysTuple>(), []<typename... Ts>(Ts&&... args) {
|
||||||
|
return kWrapper<(requires { args == std::declval<Key>(); } && ...)>;
|
||||||
|
}))::kValue;
|
||||||
|
|
||||||
|
template <typename F, typename ValuesTuple, typename R>
|
||||||
|
concept CallableSwitchConcept = decltype(Unpack(std::declval<ValuesTuple>(), []<typename... Ts>(Ts&&...) {
|
||||||
|
return kWrapper<(std::convertible_to<std::invoke_result_t<F, Ts>, std::optional<R>> && ...)>;
|
||||||
|
}))::kValue;
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
template <std::move_constructible R,
|
||||||
TupleLike KeysTuple,
|
TupleLike KeysTuple,
|
||||||
TupleLike ValuesTuple,
|
TupleLike ValuesTuple,
|
||||||
typename Key,
|
impl::ComparableSwitchConcept<KeysTuple> Key,
|
||||||
typename F,
|
impl::CallableSwitchConcept<ValuesTuple, R> F,
|
||||||
std::invocable Default = decltype(kDefaultCreator<R>)>
|
std::invocable Default = decltype(kDefaultCreator<R>)>
|
||||||
constexpr auto Switch(KeysTuple&& keysTuple, ValuesTuple&& valuesTuple, Key&& key, F&& f, Default&& def = {}) {
|
constexpr auto Switch(KeysTuple&& keysTuple, ValuesTuple&& valuesTuple, Key&& key, F&& f, Default&& def = {}) {
|
||||||
return Unpack(std::forward<KeysTuple>(keysTuple), [&]<typename... Keys>(Keys&&... keys) {
|
return Unpack(std::forward<KeysTuple>(keysTuple), [&]<typename... Keys>(Keys&&... keys) {
|
||||||
|
|
Loading…
Reference in a new issue