diff --git a/Redcraft.Utility/Source/Public/Range/Conversion.h b/Redcraft.Utility/Source/Public/Range/Conversion.h new file mode 100644 index 0000000..9e4b1fc --- /dev/null +++ b/Redcraft.Utility/Source/Public/Range/Conversion.h @@ -0,0 +1,145 @@ +#pragma once + +#include "CoreTypes.h" +#include "Range/View.h" +#include "Range/Utility.h" +#include "Range/AllView.h" +#include "Templates/Utility.h" +#include "Range/TransformView.h" +#include "TypeTraits/TypeTraits.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +/** A concept specifies a container that can reserve size. */ +template +concept CReservableContainer = CSizedRange + && requires (C& Container, size_t N) + { + Container.Reserve(N); + { Container.Max() } -> CSameAs; + }; + +/** A concept specifies a container that can append elements. */ +template +concept CAppendableContainer = + requires (C& Container, Ref&& Reference) + { + requires + ( + requires { Container.EmplaceBack (Forward(Reference)); } || + requires { Container.PushBack (Forward(Reference)); } || + requires { Container.Emplace(Container.End(), Forward(Reference)); } || + requires { Container.Insert (Container.End(), Forward(Reference)); } + ); + }; + +NAMESPACE_BEGIN(Range) + +/** Constructs a non-view object from the elements of the range. */ +template requires (!CView) +NODISCARD FORCEINLINE constexpr auto To(R&& Range, Ts&&... Args) +{ + if constexpr (!CInputRange || CConvertibleTo, TRangeElement>) + { + if constexpr (CConstructibleFrom) + { + return C(Forward(Range), Forward(Args)...); + } + + else if constexpr (CCommonRange && CInputRange && CConstructibleFrom, TRangeSentinel, Ts...>) + { + return C(Range::Begin(Range), Range::End(Range), Forward(Args)...); + } + + else if constexpr (CConstructibleFrom && CAppendableContainer>) + { + C Result(Forward(Args)...); + + if constexpr (CSizedRange && CReservableContainer) + { + Result.Reserve(Range::Num(Range)); + } + + for (TRangeReference Element : Range) + { + if constexpr (requires { Result.EmplaceBack(DeclVal>()); }) + { + Result.EmplaceBack(Forward>(Element)); + } + + else if constexpr (requires { Result.PushBack(DeclVal>()); }) + { + Result.PushBack(Forward>(Element)); + } + + else if constexpr (requires { Result.Emplace(Result.End(), DeclVal>()); }) + { + Result.Emplace(Result.End(), Forward>(Element)); + } + + else /* if constexpr (requires { Result.Insert(Result.End(), DeclVal>()); }) */ + { + Result.Insert(Result.End(), Forward>(Element)); + } + } + + return Result; + } + + else static_assert(sizeof(C) == -1, "The container type is not constructible from a range"); + } + else + { + if constexpr (CInputRange>) + { + return Range::To(Range::All(Range) | Range::Transform([](T&& Element) { return Range::To>(Forward(Element)); }), Forward(Args)...); + } + + else static_assert(sizeof(C) == -1, "The container type is not constructible from a range"); + } +} + +/** Constructs a non-view object from the elements of the range. */ +template