Range adaptor support (#7388)
* Updated the SFINAE checks in concepts.h and range.h To use conjunction and disjunction for short-circuiting behavior. Replaced AZStd::optional implementation with std::optional alias Added range adaptor support and the following views: ref_view, owning_view Added bitwise or(|) overload for chaining range adaptor closures together Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding indirectly invocable concepts. These concepts are used to determine whether a callable can be invoked with a dereferenced iterator instance. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Add implementation of range relational function objects Add implementation of range min max functions which uses the range relation function objects(ranges::less, ranges::equal_to, etc...) This is needed to implement ranges::zip_view Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding interface for zip_view which compiles successfully The implementation for the zip view functions still need to be filled. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding function definitions for zip_view classes. Adding empty header of subrange.h for the ranges::subrange class Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding additional view implementations. The following range and view classes have been added: empty_view, single_view and subrange. Moved the AZ_NO_UNIQUE_ADDRESS macro to PlatformDef.h to allow other code to specify the [[no_unique_address]] attribute. Added additional test for view structures. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding missing includes for non-unity builds Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Workaround for NDK21 clang 9.0.9 compile issue. The AZStd::ranges::zip_view::iterator::iter_swap friend function is in the AZStd::ranges namespace, while the customization point object of `AZStd::ranges::customization_point_object::iter_swap` is in the regular namespace of `AZStd::ranges` and the inline namespace of `customization_point`. This issue is fixed in NDK23, but as Jenkins uses NDK21 at the time, the entire zip_view implementation has moved to inline namespace of `zip_view_internal` Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Added iterator algorithm requiremetn concepts Fixed the ambiguity in the ranges::iter_swap exchange overload to exclude itself as a candidate if the iterator reference types are swappable with each other. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding type alias for borrowed_subrange_t Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Fixed convertible to ref_view check in the ranges::all customization_point Updated SFINAE detection of whether AZStd::to_address is invocable Moved the Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Fixed private variable access in ranges::subrange get specialization. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removing ranges::view constraint from the ranges::views::single customization_point. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding C++23 range overload for string_view. It is detailed in the [C++draft strings](https://eel.is/c++draft/strings#lib:basic_string_view,constructor____) section Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding implementations of ranges, find, search, mismatch and equal functions. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding implementation of ranges split_view along with test. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding const overloads to SceneAPI ProxyPointer container The Proxy Pointer class operator* and operator-> was unable to be invoked with a const instance before. Now it returns a const view of the pointer it contains. This allows it to be invoked in `AZStd::to_address` as part of an SINAE context for the contiguous_iterator concept Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Refactored the to_address implementation to better work with SFINAE. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding general non-unity build fixes This is unrelated to the RangeAdaptor changes. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Allow range algorithms to be used with rvalue ranges Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Workaround MSVC Internal Compiler erroy by removing enable_if condition in the operator bool of the view_interface class. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Corrected the non_propagating_cache helper class to have public functions Fixed the order of creating the perfect forwarding call wrapper for an outer closure around another closure. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Specialized the borrowed_range and view concepts For the AZ PathView class, since it is a immutable view around a path. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed inline namespace around the zip_view class. It was needed to workaround a clang 10 or below issue where a friend function in a namespace and a variable within underneath an inline namespace within the function namespace would cause an improper symbol redefinition. The workaround is to create a placeholder namespace containing the inline namespace and then bring that placeholder namespace into the parent scope. https://bugs.llvm.org/show_bug.cgi?id=37556 Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding implementation of the elemetns_view and join_view classes It is up to date with the standard as of the current draft: https://eel.is/c++draft/ranges. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Separated definitions of concepts out of concepts.h This allows the ranges::iter_swap and ranges::swap customization point to be moved outside of the concepts folder and into the ranges folder. The concepts.h header previously had to define those objects to avoid circular dependencies. Added the work around for ranges::iter_swap and ranges::iter_move customization_point causing an improper symbol redefinition in clang 10 or below: https://bugs.llvm.org/show_bug.cgi?id=37556 Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Alias more std:: names into the AZStd namespace. Removed our custom implementation of toaddress. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding more range view test. The join_view and elements_view classes now have UnitTest. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding deduction guides for AZStd associative containers Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Moved zip_view::sentinel iterator accessor function to zip_view.inl Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Fixed variable shadowing issues with clang 12+ Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
5f334e30f9
commit
d7ee248df5
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/std/concepts/concepts.h>
|
||||
#include <AzCore/std/ranges/ranges_functional.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
@@ -199,4 +200,122 @@ namespace UnitTest
|
||||
static_assert(!AZStd::strict_weak_order<RelationPredicate, int, Base>);
|
||||
static_assert(!AZStd::strict_weak_order<RelationPredicate, Base, int>);
|
||||
}
|
||||
|
||||
TEST_F(ConceptsTestFixture, IteratorInvocableConcepts)
|
||||
{
|
||||
// concept indirectly unary invocable
|
||||
// i.e Is deferencing an iterator like type and invoking
|
||||
// a unary callable a well formed expression
|
||||
auto CharUnaryCallable = [](const char) -> int { return {}; };
|
||||
auto IntUnaryCallable = [](int) -> int { return {}; };
|
||||
auto IntRefUnaryCallable = [](int&) -> int { return {}; };
|
||||
static_assert(AZStd::indirectly_unary_invocable<decltype(CharUnaryCallable), AZStd::string_view::iterator>);
|
||||
static_assert(AZStd::indirectly_unary_invocable<decltype(IntUnaryCallable), AZStd::string_view::iterator>);
|
||||
static_assert(!AZStd::indirectly_unary_invocable<decltype(IntRefUnaryCallable), AZStd::string_view::iterator>);
|
||||
|
||||
// concept indirectly regular unary invocable
|
||||
// i.e Is deferencing an iterator like type and invoking with a unary callable
|
||||
// which will not modify the input arguments(hence the term "regular") a well formed expression
|
||||
static_assert(AZStd::indirectly_regular_unary_invocable<decltype(CharUnaryCallable), AZStd::string_view::iterator>);
|
||||
static_assert(AZStd::indirectly_regular_unary_invocable<decltype(IntUnaryCallable), AZStd::string_view::iterator>);
|
||||
static_assert(!AZStd::indirectly_regular_unary_invocable<decltype(IntRefUnaryCallable), AZStd::string_view::iterator>);
|
||||
|
||||
// concept indirect unary predicate
|
||||
// i.e Is deferencing an iterator like type and invoking with a unary predicate
|
||||
// i.e which is a callable that accepts one argument and returns value testable in a boolean context
|
||||
auto CharUnaryPredicate = [](const char) -> bool { return {}; };
|
||||
auto IntUnaryPredicate = [](int) -> int { return {}; };// Return value is an int which is convertible to bool
|
||||
auto IntRefUnaryPredicate = [](int&) -> int { return {}; }; // string_view iterator value type(char) can't bind to an int&
|
||||
auto CharUnaryNonPredicate = [](const char) -> AZStd::string_view { return {}; }; // string_view is not convertible to bool
|
||||
|
||||
static_assert(AZStd::indirect_unary_predicate<decltype(CharUnaryPredicate), AZStd::string_view::iterator>);
|
||||
static_assert(AZStd::indirect_unary_predicate<decltype(IntUnaryPredicate), AZStd::string_view::iterator>);
|
||||
static_assert(!AZStd::indirect_unary_predicate<decltype(IntRefUnaryPredicate), AZStd::string_view::iterator>);
|
||||
static_assert(!AZStd::indirect_unary_predicate<decltype(CharUnaryNonPredicate), AZStd::string_view::iterator>);
|
||||
|
||||
// concept indirect binary predicate
|
||||
// i.e Is deferencing two iterator like types and invoking a binary predicate with those values
|
||||
// well formed and returns value testable in a boolean context.
|
||||
auto CharIntBinaryPredicate = [](const char, int) -> bool { return{}; };
|
||||
auto CharCharRefBinaryPredicate = [](const char, const char&) -> uint32_t { return{}; };
|
||||
auto UIntRefCharBinaryPredicate = [](uint32_t&, char) -> bool { return{}; };
|
||||
auto CharCharBinaryNonPredicate = [](const char, const char) -> AZStd::string_view { return {}; };
|
||||
|
||||
static_assert(AZStd::indirect_binary_predicate<decltype(CharIntBinaryPredicate),
|
||||
AZStd::string_view::iterator, AZStd::string_view::iterator>);
|
||||
static_assert(AZStd::indirect_binary_predicate<decltype(CharCharRefBinaryPredicate),
|
||||
AZStd::string_view::iterator, AZStd::string_view::iterator>);
|
||||
// string_view iterator value type(char) cannot bind to int&
|
||||
static_assert(!AZStd::indirect_binary_predicate<decltype(UIntRefCharBinaryPredicate),
|
||||
AZStd::string_view::iterator, AZStd::string_view::iterator>);
|
||||
// string_view is not convertible to bool
|
||||
static_assert(!AZStd::indirect_binary_predicate<decltype(CharCharBinaryNonPredicate),
|
||||
AZStd::string_view::iterator, AZStd::string_view::iterator>);
|
||||
// Ok - iter_reference_t<uint32_t*> = uint32_t&
|
||||
static_assert(AZStd::indirect_binary_predicate<decltype(UIntRefCharBinaryPredicate),
|
||||
uint32_t*, AZStd::string_view::iterator>);
|
||||
|
||||
// concept indirect equivalence relation
|
||||
// i.e Is deferencing two iterator like types and invoking a binary predicate with those values
|
||||
// well formed and returns value testable in a boolean context.
|
||||
// The dereferenced iterator types should be model an equivalence relationship
|
||||
// (a == b) && (b == c) == (a ==c)
|
||||
static_assert(AZStd::indirect_equivalence_relation<decltype(CharIntBinaryPredicate),
|
||||
AZStd::string_view::iterator, AZStd::string_view::iterator>);
|
||||
static_assert(AZStd::indirect_equivalence_relation<decltype(CharCharRefBinaryPredicate),
|
||||
AZStd::string_view::iterator, AZStd::string_view::iterator>);
|
||||
static_assert(!AZStd::indirect_equivalence_relation<decltype(UIntRefCharBinaryPredicate),
|
||||
AZStd::string_view::iterator, AZStd::string_view::iterator>);
|
||||
static_assert(!AZStd::indirect_equivalence_relation<decltype(CharCharBinaryNonPredicate),
|
||||
AZStd::string_view::iterator, AZStd::string_view::iterator>);
|
||||
// The "relation" concept requires that both both arguments can be bind to
|
||||
// either of the two binary parameters
|
||||
static_assert(!AZStd::indirect_equivalence_relation<decltype(UIntRefCharBinaryPredicate),
|
||||
uint32_t*, AZStd::string_view::iterator>);
|
||||
|
||||
// concept indirect strict weak order
|
||||
// i.e Is deferencing two iterator like types and invoking a binary predicate with those values
|
||||
// well formed and returns value testable in a boolean context.
|
||||
// The dereferenced iterator types should be model a strict weak order relation
|
||||
// (a < b) && (b < c) == (a < c)
|
||||
static_assert(AZStd::indirect_strict_weak_order<decltype(CharIntBinaryPredicate),
|
||||
AZStd::string_view::iterator, AZStd::string_view::iterator>);
|
||||
static_assert(AZStd::indirect_strict_weak_order<decltype(CharCharRefBinaryPredicate),
|
||||
AZStd::string_view::iterator, AZStd::string_view::iterator>);
|
||||
static_assert(!AZStd::indirect_strict_weak_order<decltype(UIntRefCharBinaryPredicate),
|
||||
AZStd::string_view::iterator, AZStd::string_view::iterator>);
|
||||
static_assert(!AZStd::indirect_strict_weak_order<decltype(CharCharBinaryNonPredicate),
|
||||
AZStd::string_view::iterator, AZStd::string_view::iterator>);
|
||||
// The "relation" concept requires that both both arguments can be bind to
|
||||
// either of the two binary parameters
|
||||
static_assert(!AZStd::indirect_strict_weak_order<decltype(UIntRefCharBinaryPredicate),
|
||||
uint32_t*, AZStd::string_view::iterator>);
|
||||
|
||||
// indirect_result_t type alias
|
||||
static_assert(AZStd::same_as<AZStd::indirect_result_t<decltype(CharCharRefBinaryPredicate),
|
||||
AZStd::string_view::iterator, const char*>, uint32_t>);
|
||||
|
||||
// projected operator* returns indirect result of the projection function
|
||||
static_assert(AZStd::same_as<AZStd::iter_reference_t<AZStd::projected<int*, AZStd::identity>>, int&>);
|
||||
}
|
||||
|
||||
TEST_F(ConceptsTestFixture, IteratorAlgorithmConcepts)
|
||||
{
|
||||
static_assert(AZStd::indirectly_swappable<int*, int*>);
|
||||
static_assert(!AZStd::indirectly_swappable<int*, const int*>);
|
||||
auto CharIntIndirectlyComparable = [](const char lhs, int rhs) -> bool { return lhs == rhs; };
|
||||
static_assert(AZStd::indirectly_comparable<const char*, int*, decltype(CharIntIndirectlyComparable)>);
|
||||
static_assert(!AZStd::indirectly_comparable<AZStd::string_view, int*, decltype(CharIntIndirectlyComparable)>);
|
||||
|
||||
static_assert(AZStd::permutable<typename AZStd::vector<int>::iterator>);
|
||||
// const iterator isn't indirectlly swappable or indirectly movable
|
||||
static_assert(!AZStd::permutable<typename AZStd::vector<int>::const_iterator>);
|
||||
|
||||
static_assert(AZStd::mergeable<AZStd::vector<int>::iterator, AZStd::string_view::iterator, AZStd::vector<int>::iterator>);
|
||||
static_assert(!AZStd::mergeable<AZStd::vector<int>::iterator, AZStd::string_view::iterator, AZStd::string_view::iterator>);
|
||||
|
||||
static_assert(AZStd::sortable<int*>);
|
||||
// Not sortable becaue the iter_reference_t<const int*> = const int& which isn't swappable
|
||||
static_assert(!AZStd::sortable<const int*>);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
#include <AzCore/std/ranges/ranges_algorithm.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class RangesAlgorithmTestFixture
|
||||
: public ScopedAllocatorSetupFixture
|
||||
{};
|
||||
|
||||
// range algorithm min and max
|
||||
TEST_F(RangesAlgorithmTestFixture, RangesMin_ReturnsSmallestElement)
|
||||
{
|
||||
// Simple Elements
|
||||
EXPECT_EQ(-1, AZStd::ranges::min(5, -1));
|
||||
EXPECT_EQ(78235, AZStd::ranges::min(78235, 124785));
|
||||
EXPECT_EQ(7, AZStd::ranges::min(7, 7));
|
||||
|
||||
// Initializer list
|
||||
AZStd::initializer_list<int> testIList{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
|
||||
EXPECT_EQ(-8, AZStd::ranges::min(testIList));
|
||||
|
||||
// Range
|
||||
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
|
||||
EXPECT_EQ(-8, AZStd::ranges::min(testVector));
|
||||
}
|
||||
|
||||
TEST_F(RangesAlgorithmTestFixture, RangesMax_ReturnsLargestElement)
|
||||
{
|
||||
// Simple Elements
|
||||
EXPECT_EQ(5, AZStd::ranges::max(5, -1));
|
||||
EXPECT_EQ(124785, AZStd::ranges::max(78235, 124785));
|
||||
EXPECT_EQ(7, AZStd::ranges::max(7, 7));
|
||||
|
||||
// Initializer list
|
||||
AZStd::initializer_list<int> testIList{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
|
||||
EXPECT_EQ(1000, AZStd::ranges::max(testIList));
|
||||
|
||||
// Range
|
||||
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
|
||||
EXPECT_EQ(1000, AZStd::ranges::max(testVector));
|
||||
}
|
||||
|
||||
TEST_F(RangesAlgorithmTestFixture, RangesMinMax_ReturnsSmallestAndLargestValue)
|
||||
{
|
||||
// Simple Elements
|
||||
AZStd::ranges::minmax_result<int> expectedMinMax{ -1, 5 };
|
||||
AZStd::ranges::minmax_result<int> testMinMax = AZStd::ranges::minmax(5, -1);
|
||||
EXPECT_EQ(expectedMinMax.min, testMinMax.min);
|
||||
EXPECT_EQ(expectedMinMax.max, testMinMax.max);
|
||||
|
||||
expectedMinMax = { 78235, 124785 };
|
||||
testMinMax = AZStd::ranges::minmax(78235, 124785);
|
||||
EXPECT_EQ(expectedMinMax.min, testMinMax.min);
|
||||
EXPECT_EQ(expectedMinMax.max, testMinMax.max);
|
||||
|
||||
expectedMinMax = { 7, 7 };
|
||||
testMinMax = AZStd::ranges::minmax(7, 7);
|
||||
EXPECT_EQ(expectedMinMax.min, testMinMax.min);
|
||||
EXPECT_EQ(expectedMinMax.max, testMinMax.max);
|
||||
|
||||
// Initializer list
|
||||
AZStd::initializer_list<int> testIList{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
|
||||
expectedMinMax = { -8, 1000 };
|
||||
testMinMax = AZStd::ranges::minmax(testIList);
|
||||
EXPECT_EQ(expectedMinMax.min, testMinMax.min);
|
||||
EXPECT_EQ(expectedMinMax.max, testMinMax.max);
|
||||
|
||||
// Range
|
||||
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
|
||||
expectedMinMax = { -8, 1000 };
|
||||
testMinMax = AZStd::ranges::minmax(testVector);
|
||||
EXPECT_EQ(expectedMinMax.min, testMinMax.min);
|
||||
EXPECT_EQ(expectedMinMax.max, testMinMax.max);
|
||||
}
|
||||
|
||||
TEST_F(RangesAlgorithmTestFixture, RangesMinElement_ReturnsIteratorToLeftmostSmallestElement)
|
||||
{
|
||||
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
|
||||
// iterator
|
||||
auto leftmostSmallestIt = AZStd::ranges::min_element(testVector.begin(), testVector.end());
|
||||
ASSERT_EQ(testVector.begin() + 4, leftmostSmallestIt);
|
||||
EXPECT_EQ(-8, *leftmostSmallestIt);
|
||||
|
||||
// Range
|
||||
leftmostSmallestIt = AZStd::ranges::min_element(testVector);
|
||||
ASSERT_EQ(testVector.begin() + 4, leftmostSmallestIt);
|
||||
EXPECT_EQ(-8, *leftmostSmallestIt);
|
||||
}
|
||||
|
||||
TEST_F(RangesAlgorithmTestFixture, RangesMaxElement_ReturnsIteratorToLeftmostLargestElement)
|
||||
{
|
||||
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
|
||||
// iterator
|
||||
auto leftmostLargestIt = AZStd::ranges::max_element(testVector.begin(), testVector.end());
|
||||
ASSERT_EQ(testVector.begin() + 6, leftmostLargestIt);
|
||||
EXPECT_EQ(1000, *leftmostLargestIt);
|
||||
|
||||
// Range
|
||||
leftmostLargestIt = AZStd::ranges::max_element(testVector);
|
||||
ASSERT_EQ(testVector.begin() + 6, leftmostLargestIt);
|
||||
EXPECT_EQ(1000, *leftmostLargestIt);
|
||||
}
|
||||
TEST_F(RangesAlgorithmTestFixture, RangesMinMaxElement_ReturnsIteratorToLeftmostSmallestElement_IteratorToRightmostLargestElement)
|
||||
{
|
||||
// The behavior of minmax_element is explicitly different from max_element
|
||||
// as it relates to the max element being returned
|
||||
// mixmax_element returns the rightmost largest element(assuming comparison function object is ranges::less)
|
||||
// https://eel.is/c++draft/algorithms#footnoteref-225
|
||||
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
|
||||
// iterator
|
||||
{
|
||||
auto [leftmostSmallestIt, rightmostLargestIt] = AZStd::ranges::minmax_element(testVector.begin(), testVector.end());
|
||||
ASSERT_EQ(testVector.begin() + 4, leftmostSmallestIt);
|
||||
ASSERT_EQ(testVector.begin() + 10, rightmostLargestIt);
|
||||
EXPECT_EQ(-8, *leftmostSmallestIt);
|
||||
EXPECT_EQ(1000, *rightmostLargestIt);
|
||||
}
|
||||
|
||||
// Range
|
||||
auto [leftmostSmallestIt, rightmostLargestIt] = AZStd::ranges::minmax_element(testVector);
|
||||
ASSERT_EQ(testVector.begin() + 4, leftmostSmallestIt);
|
||||
ASSERT_EQ(testVector.begin() + 10, rightmostLargestIt);
|
||||
EXPECT_EQ(-8, *leftmostSmallestIt);
|
||||
EXPECT_EQ(1000, *rightmostLargestIt);
|
||||
}
|
||||
|
||||
TEST_F(RangesAlgorithmTestFixture, RangesFind_LocatesElementInContainer_Succeeds)
|
||||
{
|
||||
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
|
||||
|
||||
auto foundIt = AZStd::ranges::find(testVector, 22);
|
||||
ASSERT_NE(testVector.end(), foundIt);
|
||||
EXPECT_EQ(22, *foundIt);
|
||||
|
||||
foundIt = AZStd::ranges::find_if(testVector, [](int value) { return value < 0; });
|
||||
ASSERT_NE(testVector.end(), foundIt);
|
||||
EXPECT_EQ(-8, *foundIt);
|
||||
|
||||
foundIt = AZStd::ranges::find_if_not(testVector, [](int value) { return value < 1000; });
|
||||
ASSERT_NE(testVector.end(), foundIt);
|
||||
EXPECT_EQ(1000, *foundIt);
|
||||
}
|
||||
|
||||
TEST_F(RangesAlgorithmTestFixture, RangesFindFirstOf_LocatesElementInContainer_Succeeds)
|
||||
{
|
||||
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
|
||||
|
||||
AZStd::array testArray{ -8, 47 };
|
||||
auto foundIt = AZStd::ranges::find_first_of(testVector, testArray);
|
||||
ASSERT_NE(testVector.end(), foundIt);
|
||||
EXPECT_EQ(47, *foundIt);
|
||||
|
||||
AZStd::array<int, 0> emptyArray{};
|
||||
foundIt = AZStd::ranges::find_first_of(testVector, emptyArray);
|
||||
EXPECT_EQ(testVector.end(), foundIt);
|
||||
}
|
||||
|
||||
TEST_F(RangesAlgorithmTestFixture, RangesSearch_LocatesElementInContainer_Succeeds)
|
||||
{
|
||||
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, -8, 1000, 45 };
|
||||
|
||||
AZStd::array testArray{ 1000 };
|
||||
auto testSubrange = AZStd::ranges::search(testVector, testArray);
|
||||
ASSERT_FALSE(testSubrange.empty());
|
||||
EXPECT_EQ(1000, testSubrange.front());
|
||||
|
||||
AZStd::array testArray2{ 1000, 45 };
|
||||
testSubrange = AZStd::ranges::search(testVector, testArray2);
|
||||
ASSERT_EQ(2, testSubrange.size());
|
||||
EXPECT_EQ(1000, testSubrange[0]);
|
||||
EXPECT_EQ(45, testSubrange[1]);
|
||||
|
||||
testSubrange = AZStd::ranges::search_n(testVector, 1, 22);
|
||||
ASSERT_FALSE(testSubrange.empty());
|
||||
EXPECT_EQ(22, testSubrange.front());
|
||||
|
||||
// 2 Consecutive values of 22 does not exist in vector
|
||||
testSubrange = AZStd::ranges::search_n(testVector, 2, 22);
|
||||
EXPECT_TRUE(testSubrange.empty());
|
||||
|
||||
// 2 Consecutive values of -8 does exist in vector
|
||||
testSubrange = AZStd::ranges::search_n(testVector, 2,- 8);
|
||||
ASSERT_EQ(2, testSubrange.size());
|
||||
EXPECT_EQ(-8, testSubrange[0]);
|
||||
EXPECT_EQ(-8, testSubrange[1]);
|
||||
}
|
||||
|
||||
TEST_F(RangesAlgorithmTestFixture, RangesFindEnd_LocatesElementLastElement_Succeeds)
|
||||
{
|
||||
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, -8, 1000, 45 };
|
||||
|
||||
AZStd::array testArray{ -8 };
|
||||
auto testSubrange = AZStd::ranges::find_end(testVector, testArray);
|
||||
ASSERT_FALSE(testSubrange.empty());
|
||||
EXPECT_EQ(-8, testSubrange.front());
|
||||
EXPECT_EQ(testVector.end() - 3, testSubrange.begin());
|
||||
}
|
||||
|
||||
TEST_F(RangesAlgorithmTestFixture, RangesEqual_IsAbleToCompareTwoRanges_Succeeds)
|
||||
{
|
||||
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, -8, 1000, 45 };
|
||||
AZStd::vector longerVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, -8, 1000, 45, 929 };
|
||||
AZStd::vector shorterVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, -8, 1000 };
|
||||
AZStd::vector unequalVector{ 5, 1, 22, 47, -8, -5, 1000, 14, 22, -8, -8, 1000, 25 };
|
||||
|
||||
EXPECT_TRUE(AZStd::ranges::equal(testVector, testVector));
|
||||
EXPECT_FALSE(AZStd::ranges::equal(testVector, longerVector));
|
||||
EXPECT_FALSE(AZStd::ranges::equal(longerVector, testVector));
|
||||
EXPECT_FALSE(AZStd::ranges::equal(testVector, shorterVector));
|
||||
EXPECT_FALSE(AZStd::ranges::equal(shorterVector, testVector));
|
||||
EXPECT_FALSE(AZStd::ranges::equal(testVector, unequalVector));
|
||||
}
|
||||
|
||||
TEST_F(RangesAlgorithmTestFixture, RangesMismatch_Returns_IteratorsToMismatchElements)
|
||||
{
|
||||
AZStd::vector testVector{ 1, 2, 3, 4, 5 ,6 };
|
||||
AZStd::vector secondVector{ 1, 2, 3, 14, 5, 6 };
|
||||
AZStd::vector<int> emptyVector;
|
||||
AZStd::vector<int> longerVector{ 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
{
|
||||
auto [mismatchIt1, mismatchIt2] = AZStd::ranges::mismatch(testVector, secondVector);
|
||||
ASSERT_NE(testVector.end(), mismatchIt1);
|
||||
EXPECT_EQ(4, *mismatchIt1);
|
||||
ASSERT_NE(secondVector.end(), mismatchIt2);
|
||||
EXPECT_EQ(14, *mismatchIt2);
|
||||
}
|
||||
{
|
||||
auto [mismatchIt1, mismatchIt2] = AZStd::ranges::mismatch(testVector, emptyVector);
|
||||
ASSERT_NE(testVector.end(), mismatchIt1);
|
||||
EXPECT_EQ(1, *mismatchIt1);
|
||||
EXPECT_EQ(emptyVector.end(), mismatchIt2);
|
||||
}
|
||||
{
|
||||
auto [mismatchIt1, mismatchIt2] = AZStd::ranges::mismatch(testVector, longerVector);
|
||||
EXPECT_EQ(testVector.end(), mismatchIt1);
|
||||
ASSERT_NE(longerVector.end(), mismatchIt2);
|
||||
EXPECT_EQ(7, *mismatchIt2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/std/containers/list.h>
|
||||
#include <AzCore/std/containers/map.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/ranges/all_view.h>
|
||||
#include <AzCore/std/ranges/elements_view.h>
|
||||
#include <AzCore/std/ranges/empty_view.h>
|
||||
#include <AzCore/std/ranges/join_view.h>
|
||||
#include <AzCore/std/ranges/ranges_adaptor.h>
|
||||
#include <AzCore/std/ranges/single_view.h>
|
||||
#include <AzCore/std/ranges/split_view.h>
|
||||
#include <AzCore/std/ranges/subrange.h>
|
||||
#include <AzCore/std/ranges/zip_view.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class RangesViewTestFixture
|
||||
: public ScopedAllocatorSetupFixture
|
||||
{};
|
||||
|
||||
TEST_F(RangesViewTestFixture, AllRangeAdaptor_Succeeds)
|
||||
{
|
||||
AZStd::string_view testString{ "Hello World" };
|
||||
auto testAllView = AZStd::ranges::views::all(testString);
|
||||
EXPECT_EQ(testString.size(), testAllView.size());
|
||||
EXPECT_EQ(testString.data(), testAllView.data());
|
||||
EXPECT_EQ(testString.begin(), testAllView.begin());
|
||||
EXPECT_EQ(testString.end(), testAllView.end());
|
||||
EXPECT_EQ(testString.empty(), testAllView.empty());
|
||||
EXPECT_EQ(testString.front(), testAllView.front());
|
||||
EXPECT_EQ(testString.back(), testAllView.back());
|
||||
EXPECT_EQ(testString[5], testAllView[5]);
|
||||
|
||||
auto testAllViewChain = testString | AZStd::ranges::views::all;
|
||||
EXPECT_EQ(testString.size(), testAllViewChain.size());
|
||||
EXPECT_EQ(testString.data(), testAllViewChain.data());
|
||||
EXPECT_EQ(testString.begin(), testAllViewChain.begin());
|
||||
EXPECT_EQ(testString.end(), testAllViewChain.end());
|
||||
EXPECT_EQ(testString.empty(), testAllViewChain.empty());
|
||||
EXPECT_EQ(testString.front(), testAllViewChain.front());
|
||||
EXPECT_EQ(testString.back(), testAllViewChain.back());
|
||||
EXPECT_EQ(testString[5], testAllViewChain[5]);
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, Subrange_DeductionGuides_Compile)
|
||||
{
|
||||
AZStd::string_view testString{ "Hello World" };
|
||||
AZStd::ranges::subrange rangeDeduction(testString);
|
||||
AZStd::ranges::subrange rangeDeductionWithSize(testString, testString.size());
|
||||
AZStd::ranges::subrange iteratorDeduction(testString.begin(), testString.end());
|
||||
AZStd::ranges::subrange iteratorDeductionWithSize(testString.begin(), testString.end(),
|
||||
testString.size());
|
||||
EXPECT_TRUE(rangeDeduction);
|
||||
EXPECT_TRUE(rangeDeductionWithSize);
|
||||
EXPECT_TRUE(iteratorDeduction);
|
||||
EXPECT_TRUE(iteratorDeductionWithSize);
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, Subrange_CanTakeSubsetOfContainer_Succeeds)
|
||||
{
|
||||
AZStd::vector<int> testVector{ 1, 3, 5, 6, 7, 6, 89, -178 };
|
||||
AZStd::ranges::subrange subVector(testVector);
|
||||
EXPECT_TRUE(subVector);
|
||||
ASSERT_FALSE(subVector.empty());
|
||||
EXPECT_EQ(testVector.data(), subVector.data());
|
||||
EXPECT_EQ(testVector.begin(), subVector.begin());
|
||||
EXPECT_EQ(testVector.end(), subVector.end());
|
||||
EXPECT_EQ(testVector.size(), subVector.size());
|
||||
EXPECT_EQ(testVector[0], subVector[0]);
|
||||
EXPECT_EQ(testVector.front(), subVector.front());
|
||||
EXPECT_EQ(testVector.back(), subVector.back());
|
||||
|
||||
// Now validate the iterator operations
|
||||
subVector.advance(2);
|
||||
subVector = subVector.prev();
|
||||
subVector.advance(2);
|
||||
subVector = subVector.next();
|
||||
subVector.advance(-4);
|
||||
EXPECT_EQ(testVector.begin(), subVector.begin());
|
||||
|
||||
// Obtain a copy of the subrange with the first and last elements removed
|
||||
AZStd::ranges::subrange subVectorSplice(subVector.begin() + 1, subVector.end() - 1);
|
||||
EXPECT_TRUE(subVectorSplice);
|
||||
ASSERT_FALSE(subVector.empty());
|
||||
EXPECT_LT(testVector.data(), subVectorSplice.data());
|
||||
EXPECT_EQ(testVector.begin() + 1, subVectorSplice.begin());
|
||||
EXPECT_EQ(testVector.end() - 1, subVectorSplice.end());
|
||||
ASSERT_EQ(testVector.size() - 2, subVectorSplice.size());
|
||||
EXPECT_EQ(testVector[1], subVectorSplice.front());
|
||||
EXPECT_EQ(testVector[testVector.size() - 2], subVectorSplice.back());
|
||||
EXPECT_NE(testVector.front(), subVectorSplice.front());
|
||||
EXPECT_NE(testVector.back(), subVectorSplice.back());
|
||||
|
||||
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, EmptyView_ReturnsEmptyViewRange_Succeeds)
|
||||
{
|
||||
AZStd::ranges::empty_view<int> emptyView;
|
||||
|
||||
EXPECT_EQ(nullptr, emptyView.data());
|
||||
EXPECT_EQ(0, emptyView.size());
|
||||
EXPECT_TRUE(emptyView.empty());
|
||||
EXPECT_EQ(emptyView.end(), emptyView.begin());
|
||||
|
||||
EXPECT_EQ(nullptr, AZStd::ranges::views::empty<AZStd::string_view>.data());
|
||||
EXPECT_EQ(0, AZStd::ranges::views::empty<AZStd::string_view>.size());
|
||||
EXPECT_TRUE(AZStd::ranges::views::empty<AZStd::string_view>.empty());
|
||||
EXPECT_EQ(AZStd::ranges::views::empty<AZStd::string_view>.end(), AZStd::ranges::views::empty<AZStd::string_view>.begin());
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, SingleView_ReturnsViewOverSingleElement_Succeeds)
|
||||
{
|
||||
AZStd::string_view testString{ "Hello World" };
|
||||
AZStd::ranges::single_view singleView{ AZStd::move(testString) };
|
||||
EXPECT_NE(nullptr, singleView.data());
|
||||
EXPECT_EQ(1, singleView.size());
|
||||
EXPECT_FALSE(singleView.empty());
|
||||
ASSERT_NE(singleView.end(), singleView.begin());
|
||||
|
||||
auto singleViewStringIt = singleView.begin();
|
||||
EXPECT_EQ("Hello World", *singleViewStringIt);
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, RefView_CanWrapStringView_Succeeds)
|
||||
{
|
||||
AZStd::string_view testString{ "Hello World" };
|
||||
AZStd::ranges::ref_view refView(testString);
|
||||
EXPECT_EQ(testString.size(), refView.size());
|
||||
EXPECT_EQ(testString.data(), refView.data());
|
||||
EXPECT_EQ(testString.begin(), refView.begin());
|
||||
EXPECT_EQ(testString.end(), refView.end());
|
||||
EXPECT_EQ(testString.empty(), refView.empty());
|
||||
EXPECT_EQ(testString.front(), refView.front());
|
||||
EXPECT_EQ(testString.back(), refView.back());
|
||||
EXPECT_EQ(testString[5], refView[5]);
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, OwningView_CanWrapStringView_Succeeds)
|
||||
{
|
||||
AZStd::string_view sourceView{ "Hello World" };
|
||||
AZStd::string_view testString{ sourceView };
|
||||
AZStd::ranges::owning_view owningView(AZStd::move(testString));
|
||||
EXPECT_TRUE(testString.empty());
|
||||
EXPECT_FALSE(owningView.empty());
|
||||
EXPECT_EQ(sourceView.size(), owningView.size());
|
||||
EXPECT_EQ(sourceView.data(), owningView.data());
|
||||
EXPECT_EQ(sourceView.begin(), owningView.begin());
|
||||
EXPECT_EQ(sourceView.end(), owningView.end());
|
||||
EXPECT_EQ(sourceView.empty(), owningView.empty());
|
||||
EXPECT_EQ(sourceView.front(), owningView.front());
|
||||
EXPECT_EQ(sourceView.back(), owningView.back());
|
||||
EXPECT_EQ(sourceView[5], owningView[5]);
|
||||
}
|
||||
|
||||
MATCHER_P(ZipViewAtSentinel, sentinel, "") {
|
||||
*result_listener << "zip view has iterated to sentinel";
|
||||
return !(arg == sentinel);
|
||||
}
|
||||
TEST_F(RangesViewTestFixture, ZipView_CompilesWithRange_Succeeds)
|
||||
{
|
||||
AZStd::string_view sourceView{ "Hello World" };
|
||||
AZStd::ranges::zip_view zipView(AZStd::move(sourceView));
|
||||
auto zipItTuple = zipView.begin();
|
||||
auto zipSentinelTuple = zipView.end();
|
||||
ASSERT_THAT(zipItTuple, ZipViewAtSentinel(zipSentinelTuple));
|
||||
EXPECT_EQ('H', AZStd::get<0>(*zipItTuple));
|
||||
ptrdiff_t zipDistance = zipSentinelTuple - zipItTuple;
|
||||
EXPECT_EQ(11, zipDistance);
|
||||
|
||||
AZStd::list<int> sourceList{ 1, 2, 3, 4, 5 };
|
||||
AZStd::ranges::zip_view zipView2(AZStd::move(sourceList));
|
||||
auto zipListTupleIt = zipView2.begin();
|
||||
auto zipListTupleEnd = zipView2.end();
|
||||
ASSERT_THAT(zipListTupleIt, ZipViewAtSentinel(zipListTupleEnd));
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, ZipView_CanIteratOverMultipleContainers_Succeeds)
|
||||
{
|
||||
AZStd::string_view sourceView{ "abcdef" };
|
||||
AZStd::vector intVector{ 1, 2, 3, 4, 5 };
|
||||
AZStd::list<uint32_t> uintList{ 2, 4, 6, 8, 10 };
|
||||
constexpr int expectedIterations = 5;
|
||||
|
||||
int iterationCount{};
|
||||
for (auto [charX, intY, uintZ] : (AZStd::ranges::views::zip(sourceView, AZStd::move(intVector), AZStd::move(uintList))))
|
||||
{
|
||||
++iterationCount;
|
||||
switch (charX)
|
||||
{
|
||||
case 'a':
|
||||
EXPECT_EQ(1, intY);
|
||||
EXPECT_EQ(2, uintZ);
|
||||
break;
|
||||
case 'b':
|
||||
EXPECT_EQ(2, intY);
|
||||
EXPECT_EQ(4, uintZ);
|
||||
break;
|
||||
case 'c':
|
||||
EXPECT_EQ(3, intY);
|
||||
EXPECT_EQ(6, uintZ);
|
||||
break;
|
||||
case 'd':
|
||||
EXPECT_EQ(4, intY);
|
||||
EXPECT_EQ(8, uintZ);
|
||||
break;
|
||||
case 'e':
|
||||
EXPECT_EQ(5, intY);
|
||||
EXPECT_EQ(10, uintZ);
|
||||
break;
|
||||
default:
|
||||
ADD_FAILURE() << "Unexpected character value " << charX << " found when iterating zip view";
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(expectedIterations, iterationCount);
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, SplitView_CanSplitPatterns_Succeeds)
|
||||
{
|
||||
AZStd::string_view emptyView{ "" };
|
||||
auto splitView = AZStd::ranges::views::split(emptyView, " ");
|
||||
auto splitIt = splitView.begin();
|
||||
EXPECT_EQ(splitView.end(), splitIt);
|
||||
|
||||
AZStd::string_view testView1{ "Hello" };
|
||||
auto splitViewCharPattern = AZStd::ranges::views::split(testView1, ' ');
|
||||
auto splitCharIt = splitViewCharPattern.begin();
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
|
||||
|
||||
AZStd::string_view testView2{ "Hello World" };
|
||||
splitViewCharPattern = AZStd::ranges::views::split(testView2, ' ');
|
||||
splitCharIt = splitViewCharPattern.begin();
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
|
||||
|
||||
AZStd::string_view testView3{ "Hello World Moon" };
|
||||
splitViewCharPattern = AZStd::ranges::views::split(testView3, ' ');
|
||||
splitCharIt = splitViewCharPattern.begin();
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("Moon", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
|
||||
|
||||
|
||||
AZStd::string_view testView4{ "Hello World Moon " };
|
||||
splitViewCharPattern = AZStd::ranges::views::split(testView4, ' ');
|
||||
splitCharIt = splitViewCharPattern.begin();
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("Moon", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
|
||||
|
||||
AZStd::string_view testView5{ "Hello World Moon " };
|
||||
splitViewCharPattern = AZStd::ranges::views::split(testView5, ' ');
|
||||
splitCharIt = splitViewCharPattern.begin();
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("Moon", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
|
||||
|
||||
AZStd::string_view testView6{ "Hello World Moon" };
|
||||
splitViewCharPattern = AZStd::ranges::views::split(testView6, ' ');
|
||||
splitCharIt = splitViewCharPattern.begin();
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
|
||||
EXPECT_EQ("Moon", AZStd::string_view(*splitCharIt));
|
||||
++splitCharIt;
|
||||
EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, SplitView_SplitsFromNonString_Succeeds)
|
||||
{
|
||||
const AZStd::vector<int> testVector{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
||||
// Split the vector on 3
|
||||
auto splitView = AZStd::ranges::views::split(testVector, 3);
|
||||
auto splitIt = splitView.begin();
|
||||
ASSERT_NE(splitView.end(), splitIt);
|
||||
auto splitSubrange = *splitIt;
|
||||
{
|
||||
AZStd::array expectedValue{ 1, 2 };
|
||||
EXPECT_TRUE(AZStd::ranges::equal(expectedValue, splitSubrange));
|
||||
}
|
||||
|
||||
++splitIt;
|
||||
ASSERT_NE(splitView.end(), splitIt);
|
||||
splitSubrange = *splitIt;
|
||||
{
|
||||
AZStd::array expectedValue{ 4, 5, 6, 7, 8, 9, 10 };
|
||||
EXPECT_TRUE(AZStd::ranges::equal(expectedValue, splitSubrange));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, JoinView_IteratesOverInnerViews_Succeeds)
|
||||
{
|
||||
constexpr AZStd::string_view expectedString = "HelloWorldMoonSun";
|
||||
using Rope = AZStd::fixed_vector<AZStd::string_view, 32>;
|
||||
Rope rope{ "Hello", "World", "Moon", "Sun" };
|
||||
AZStd::fixed_string<128> accumString;
|
||||
for (auto&& charElement : AZStd::ranges::join_view(rope))
|
||||
{
|
||||
accumString.push_back(charElement);
|
||||
}
|
||||
|
||||
EXPECT_EQ(expectedString, accumString);
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, JoinView_IteratesCanIterateOverSplitView_Succeeds)
|
||||
{
|
||||
constexpr AZStd::string_view expectedString = "HelloWorldMoonSun";
|
||||
constexpr AZStd::string_view splitExpression = "Hello,World,Moon,Sun";
|
||||
AZStd::fixed_string<128> accumString;
|
||||
|
||||
for (auto&& charElement : AZStd::ranges::views::join(AZStd::ranges::views::split(splitExpression, ',')))
|
||||
{
|
||||
accumString.push_back(charElement);
|
||||
}
|
||||
|
||||
EXPECT_EQ(expectedString, accumString);
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, JoinView_IterSwapCustomization_Succeeds)
|
||||
{
|
||||
AZStd::fixed_vector<AZStd::string, 8> testVector1{ "World", "Hello" };
|
||||
AZStd::fixed_vector<AZStd::string, 8> testVector2{ "Value", "First" };
|
||||
auto joinView1 = AZStd::ranges::views::join(testVector1);
|
||||
auto joinView2 = AZStd::ranges::views::join(testVector2);
|
||||
auto joinViewIter1 = joinView1.begin();
|
||||
auto joinViewIter2 = joinView2.begin();
|
||||
// swaps the 'W' and 'V'
|
||||
AZStd::ranges::iter_swap(joinViewIter1, joinViewIter2);
|
||||
AZStd::ranges::advance(joinViewIter1, 5, joinView1.end());
|
||||
AZStd::ranges::advance(joinViewIter2, 5, joinView2.end());
|
||||
// swaps the 'H' and 'F' of the second string of each vector
|
||||
AZStd::ranges::iter_swap(joinViewIter1, joinViewIter2);
|
||||
EXPECT_EQ("Vorld", testVector1[0]);
|
||||
EXPECT_EQ("Fello", testVector1[1]);
|
||||
EXPECT_EQ("Walue", testVector2[0]);
|
||||
EXPECT_EQ("Hirst", testVector2[1]);
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, JoinView_IterMoveCustomization_Succeeds)
|
||||
{
|
||||
using StringWrapper = AZStd::ranges::single_view<AZStd::string>;
|
||||
|
||||
AZStd::fixed_vector<StringWrapper, 8> testVector1{ StringWrapper{"5"}, StringWrapper{"10"} };
|
||||
AZStd::fixed_vector<StringWrapper, 8> testVector2{ StringWrapper{"15"}, StringWrapper{"20"} };
|
||||
auto joinView1 = AZStd::ranges::views::join(testVector1);
|
||||
auto joinView2 = AZStd::ranges::views::join(testVector2);
|
||||
auto joinViewIter1 = joinView1.begin();
|
||||
auto joinViewIter2 = joinView2.begin();
|
||||
AZStd::string value = AZStd::ranges::iter_move(joinViewIter1);
|
||||
|
||||
EXPECT_EQ("5", value);
|
||||
EXPECT_TRUE((*joinViewIter1).empty());
|
||||
++joinViewIter2;
|
||||
value = AZStd::ranges::iter_move(joinViewIter2);
|
||||
EXPECT_EQ("20", value);
|
||||
EXPECT_TRUE((*joinViewIter2).empty());
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, ElementsView_CanIterateVectorOfTuple_Succeeds)
|
||||
{
|
||||
using TestTuple = AZStd::tuple<int, AZStd::string, bool>;
|
||||
AZStd::vector testVector{ TestTuple{1, "hello", false}, TestTuple{2, "world", true},
|
||||
TestTuple{3, "Moon", false} };
|
||||
|
||||
auto firstElementView = AZStd::ranges::views::elements<0>(testVector);
|
||||
auto firstElementBegin = AZStd::ranges::begin(firstElementView);
|
||||
auto firstElementEnd = AZStd::ranges::end(firstElementView);
|
||||
EXPECT_NE(firstElementEnd, firstElementBegin);
|
||||
ASSERT_EQ(3, firstElementView.size());
|
||||
EXPECT_EQ(1, firstElementView[0]);
|
||||
EXPECT_EQ(2, firstElementView[1]);
|
||||
EXPECT_EQ(3, firstElementView[2]);
|
||||
|
||||
auto secondElementView = AZStd::ranges::views::elements<1>(testVector);
|
||||
ASSERT_EQ(3, secondElementView.size());
|
||||
EXPECT_EQ("hello", secondElementView[0]);
|
||||
EXPECT_EQ("world", secondElementView[1]);
|
||||
EXPECT_EQ("Moon", secondElementView[2]);
|
||||
|
||||
auto thirdElementView = AZStd::ranges::views::elements<2>(testVector);
|
||||
ASSERT_EQ(3, thirdElementView.size());
|
||||
EXPECT_FALSE(thirdElementView[0]);
|
||||
EXPECT_TRUE(thirdElementView[1]);
|
||||
EXPECT_FALSE(thirdElementView[2]);
|
||||
|
||||
using TestPair = AZStd::pair<AZStd::string, int>;
|
||||
AZStd::vector testPairVector{ TestPair{"hello", 5}, TestPair{"world", 10}, TestPair{"Sun", 15} };
|
||||
using ElementsViewBase = AZStd::ranges::views::all_t<decltype((testPairVector))>;
|
||||
using ElementsViewType = AZStd::ranges::elements_view<ElementsViewBase, 0>;
|
||||
|
||||
constexpr AZStd::string_view expectedString = "helloworldSun";
|
||||
AZStd::string accumString;
|
||||
for (auto&& stringValue : ElementsViewType(testPairVector))
|
||||
{
|
||||
accumString += stringValue;
|
||||
}
|
||||
|
||||
EXPECT_EQ(expectedString, accumString);
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, ElementsView_KeysAlias_CanIterateAssociativeContainerKeyType)
|
||||
{
|
||||
using PairType = AZStd::pair<int, const char*>;
|
||||
AZStd::map testMap{ PairType{1, "Hello"}, PairType{2, "World"}, PairType{3, "Sun"}, PairType{45, "RandomText"} };
|
||||
|
||||
int accumResult{};
|
||||
for (int key : AZStd::ranges::views::keys(testMap))
|
||||
{
|
||||
accumResult += key;
|
||||
}
|
||||
|
||||
EXPECT_EQ(51, accumResult);
|
||||
}
|
||||
|
||||
TEST_F(RangesViewTestFixture, ElementsView_ValuesAlias_CanIterateAssociativeContainerMappedType)
|
||||
{
|
||||
using PairType = AZStd::pair<int, const char*>;
|
||||
AZStd::map testMap{ PairType{1, "Hello"}, PairType{2, "World"}, PairType{3, "Sun"}, PairType{45, "RandomText"} };
|
||||
|
||||
AZStd::string accumResult{};
|
||||
for (const char* value : AZStd::ranges::views::values(testMap))
|
||||
{
|
||||
accumResult += value;
|
||||
}
|
||||
|
||||
EXPECT_EQ("HelloWorldSunRandomText", accumResult);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <AzCore/UnitTest/UnitTest.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
|
||||
#if defined(HAVE_BENCHMARK)
|
||||
|
||||
@@ -206,7 +206,9 @@ set(FILES
|
||||
AZStd/Optional.cpp
|
||||
AZStd/Pair.cpp
|
||||
AZStd/Parallel.cpp
|
||||
AZStd/RangesAlgorithmTests.cpp
|
||||
AZStd/RangesTests.cpp
|
||||
AZStd/RangesViewTests.cpp
|
||||
AZStd/ScopedLockTests.cpp
|
||||
AZStd/SetsIntrusive.cpp
|
||||
AZStd/SmartPtr.cpp
|
||||
|
||||
Reference in New Issue
Block a user