pulling in latest
Signed-off-by: Scott Murray <scottmur@amazon.com>monroegm-disable-blank-issue-2
commit
5124a6322d
@ -0,0 +1,39 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
|
||||
if(NOT PAL_TRAIT_BUILD_TESTS_SUPPORTED OR NOT PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
ly_add_pytest(
|
||||
NAME AutomatedTesting::EditorLevelLoadingPerfTests_DX12
|
||||
TEST_SUITE periodic
|
||||
TEST_REQUIRES gpu
|
||||
TEST_SERIAL
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Periodic_DX12.py
|
||||
RUNTIME_DEPENDENCIES
|
||||
Legacy::Editor
|
||||
AZ::AssetProcessor
|
||||
AutomatedTesting.Assets
|
||||
COMPONENT
|
||||
Performance
|
||||
)
|
||||
|
||||
ly_add_pytest(
|
||||
NAME AutomatedTesting::EditorLevelLoadingPerfTests_Vulkan
|
||||
TEST_SUITE periodic
|
||||
TEST_REQUIRES gpu
|
||||
TEST_SERIAL
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Periodic_Vulkan.py
|
||||
RUNTIME_DEPENDENCIES
|
||||
Legacy::Editor
|
||||
AZ::AssetProcessor
|
||||
AutomatedTesting.Assets
|
||||
COMPONENT
|
||||
Performance
|
||||
)
|
||||
@ -0,0 +1,31 @@
|
||||
"""
|
||||
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
|
||||
|
||||
"""
|
||||
|
||||
# This suite consists of all test cases that are passing and have been verified.
|
||||
|
||||
import pytest
|
||||
import os
|
||||
import sys
|
||||
|
||||
from ly_test_tools.o3de.editor_test import EditorTestSuite, EditorSingleTest
|
||||
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
class TestAutomation(EditorTestSuite):
|
||||
|
||||
class Time_EditorLevelLoading_10KEntityCpuPerfTest(EditorSingleTest):
|
||||
extra_cmdline_args = ['-rhi=dx12']
|
||||
use_null_renderer = False # needs renderer to validate test
|
||||
|
||||
from .tests import EditorLevelLoading_10KEntityCpuPerfTest as test_module
|
||||
|
||||
class Time_EditorLevelLoading_10kVegInstancesTest(EditorSingleTest):
|
||||
extra_cmdline_args = ['-rhi=dx12']
|
||||
use_null_renderer = False # needs renderer to validate test
|
||||
|
||||
from .tests import EditorLevelLoading_10kVegInstancesTest as test_module
|
||||
@ -0,0 +1,34 @@
|
||||
"""
|
||||
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
|
||||
|
||||
"""
|
||||
|
||||
# This suite consists of all test cases that are passing and have been verified.
|
||||
|
||||
import pytest
|
||||
import os
|
||||
import sys
|
||||
|
||||
from ly_test_tools.o3de.editor_test import EditorTestSuite, EditorSingleTest
|
||||
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
class TestAutomation(EditorTestSuite):
|
||||
|
||||
class Time_EditorLevelLoading_10KEntityCpuPerfTest(EditorSingleTest):
|
||||
# there is currently a huge discrepancy loading this level with vulkan compared to dx12 which requires the 10 min timeout
|
||||
# this should be removed once that issue has been sorted out
|
||||
timeout = 600
|
||||
extra_cmdline_args = ['-rhi=vulkan']
|
||||
use_null_renderer = False # needs renderer to validate test
|
||||
|
||||
from .tests import EditorLevelLoading_10KEntityCpuPerfTest as test_module
|
||||
|
||||
class Time_EditorLevelLoading_10kVegInstancesTest(EditorSingleTest):
|
||||
extra_cmdline_args = ['-rhi=vulkan']
|
||||
use_null_renderer = False # needs renderer to validate test
|
||||
|
||||
from .tests import EditorLevelLoading_10kVegInstancesTest as test_module
|
||||
@ -0,0 +1,6 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
@ -0,0 +1,15 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
from Performance.utils.perf_timer import time_editor_level_loading
|
||||
|
||||
def EditorLevelLoading_10KEntityCpuPerfTest():
|
||||
time_editor_level_loading('Performance', '10KEntityCpuPerfTest')
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(EditorLevelLoading_10KEntityCpuPerfTest)
|
||||
@ -0,0 +1,15 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
from Performance.utils.perf_timer import time_editor_level_loading
|
||||
|
||||
def EditorLevelLoading_10kVegInstancesTest():
|
||||
time_editor_level_loading('Performance', '10kVegInstancesTest')
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(EditorLevelLoading_10kVegInstancesTest)
|
||||
@ -0,0 +1,6 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
@ -0,0 +1,6 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
@ -0,0 +1,72 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
ENTER_MSG = ("Entered game mode", "Failed to enter game mode")
|
||||
EXIT_MSG = ("Exited game mode", "Couldn't exit game mode")
|
||||
|
||||
class Timer:
|
||||
unit_divisor = 60
|
||||
hour_divisor = unit_divisor * unit_divisor
|
||||
|
||||
def start(self):
|
||||
self._start_time = time.perf_counter()
|
||||
|
||||
def log_time(self, message):
|
||||
from editor_python_test_tools.utils import Report
|
||||
|
||||
elapsed_time = time.perf_counter() - self._start_time
|
||||
hours = int(elapsed_time / Timer.hour_divisor)
|
||||
minutes = int(elapsed_time % Timer.hour_divisor / Timer.unit_divisor)
|
||||
seconds = elapsed_time % Timer.unit_divisor
|
||||
|
||||
Report.info(f'{message}: {hours:0>2d}:{minutes:0>2d}:{seconds:0>5.2f}\n')
|
||||
|
||||
def time_editor_level_loading(level_dir, level_name):
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Time how long it takes to load an arbitrary level, entering game mode, and exiting game mode
|
||||
|
||||
Level Description:
|
||||
Preferably a level with a large number of entities
|
||||
|
||||
Expected Behavior:
|
||||
Level loads within a reasonable time frame e.i. doesn't trip the framework timeout
|
||||
|
||||
Benchmark Steps:
|
||||
1) Time opening the level
|
||||
2) Time entering game mode
|
||||
3) Time exiting game mode
|
||||
4) Close the editor
|
||||
|
||||
:return: None
|
||||
"""
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
timer = Timer()
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
timer.start()
|
||||
helper.open_level(level_dir, level_name)
|
||||
timer.log_time('Level load time')
|
||||
|
||||
# 2) Time how long it takes to enter game mode
|
||||
timer.start()
|
||||
helper.enter_game_mode(ENTER_MSG)
|
||||
timer.log_time('Enter game mode')
|
||||
|
||||
# 3) Exit game mode
|
||||
timer.start()
|
||||
helper.exit_game_mode(EXIT_MSG)
|
||||
timer.log_time('Exit game mode')
|
||||
|
||||
# 4) Close the editor
|
||||
helper.close_editor()
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <AzCore/std/typetraits/common_reference.h>
|
||||
#include <AzCore/std/typetraits/conjunction.h>
|
||||
#include <AzCore/std/typetraits/integral_constant.h>
|
||||
#include <AzCore/std/typetraits/is_same.h>
|
||||
#include <AzCore/std/typetraits/remove_cvref.h>
|
||||
#include <AzCore/std/utility/declval.h>
|
||||
|
||||
namespace AZStd::Internal
|
||||
{
|
||||
|
||||
template<class LHS, class RHS, class = void>
|
||||
constexpr bool assignable_from_impl = false;
|
||||
template<class LHS, class RHS>
|
||||
constexpr bool assignable_from_impl<LHS, RHS, enable_if_t<conjunction_v<
|
||||
is_lvalue_reference<LHS>,
|
||||
bool_constant<common_reference_with<const remove_reference_t<LHS>&, const remove_reference_t<RHS>&>>,
|
||||
bool_constant<same_as<decltype(declval<LHS>() = declval<RHS>()), LHS>> >>> = true;
|
||||
}
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
template<class LHS, class RHS>
|
||||
/*concept*/ constexpr bool assignable_from = Internal::assignable_from_impl<LHS, RHS>;
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/typetraits/conjunction.h>
|
||||
#include <AzCore/std/typetraits/integral_constant.h>
|
||||
#include <AzCore/std/typetraits/is_constructible.h>
|
||||
#include <AzCore/std/typetraits/is_convertible.h>
|
||||
#include <AzCore/std/typetraits/is_destructible.h>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
|
||||
template<class T, class... Args>
|
||||
/*concept*/ constexpr bool constructible_from = conjunction_v<bool_constant<destructible<T>>,
|
||||
is_constructible<T, Args...> >;
|
||||
|
||||
template<class T>
|
||||
/*concept*/ constexpr bool move_constructible = conjunction_v<bool_constant<constructible_from<T, T>>,
|
||||
bool_constant<convertible_to<T, T>>>;
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/concepts/concepts_assignable.h>
|
||||
#include <AzCore/std/concepts/concepts_constructible.h>
|
||||
#include <AzCore/std/concepts/concepts_movable.h>
|
||||
#include <AzCore/std/typetraits/integral_constant.h>
|
||||
#include <AzCore/std/typetraits/is_convertible.h>
|
||||
#include <AzCore/std/typetraits/conjunction.h>
|
||||
|
||||
namespace AZStd::Internal
|
||||
{
|
||||
template <class T, class = void>
|
||||
constexpr bool copy_constructible_impl = false;
|
||||
template <class T>
|
||||
constexpr bool copy_constructible_impl<T, enable_if_t<conjunction_v<
|
||||
bool_constant<move_constructible<T>>,
|
||||
bool_constant<constructible_from<T, T&>>,
|
||||
bool_constant<convertible_to<T&, T>>,
|
||||
bool_constant<constructible_from<T, const T&>>,
|
||||
bool_constant<convertible_to<const T&, T>>,
|
||||
bool_constant<constructible_from<T, const T>>,
|
||||
bool_constant<convertible_to<const T, T>>
|
||||
>>> = true;
|
||||
}
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
// copy constructible
|
||||
template<class T>
|
||||
/*concept*/ constexpr bool copy_constructible = Internal::copy_constructible_impl<T>;
|
||||
}
|
||||
|
||||
namespace AZStd::Internal
|
||||
{
|
||||
template <class T, class = void>
|
||||
constexpr bool copyable_impl = false;
|
||||
template <class T>
|
||||
constexpr bool copyable_impl<T, enable_if_t<conjunction_v<
|
||||
bool_constant<copy_constructible<T>>,
|
||||
bool_constant<movable<T>>,
|
||||
bool_constant<assignable_from<T&, T&>>,
|
||||
bool_constant<assignable_from<T&, const T&>>,
|
||||
bool_constant<assignable_from<T&, const T>>
|
||||
>>> = true;
|
||||
}
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
// copyable
|
||||
template<class T>
|
||||
/*concept*/ constexpr bool copyable = Internal::copyable_impl<T>;
|
||||
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/concepts/concepts_assignable.h>
|
||||
#include <AzCore/std/concepts/concepts_constructible.h>
|
||||
#include <AzCore/std/typetraits/integral_constant.h>
|
||||
#include <AzCore/std/ranges/swap.h>
|
||||
#include <AzCore/std/typetraits/conjunction.h>
|
||||
#include <AzCore/std/typetraits/is_object.h>
|
||||
|
||||
namespace AZStd::Internal
|
||||
{
|
||||
template <class T, class = void>
|
||||
constexpr bool movable_impl = false;
|
||||
template <class T>
|
||||
constexpr bool movable_impl<T, enable_if_t<conjunction_v<
|
||||
is_object<T>,
|
||||
bool_constant<move_constructible<T>>,
|
||||
bool_constant<assignable_from<T&, T>>,
|
||||
bool_constant<swappable<T>>
|
||||
>>> = true;
|
||||
}
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
// movable
|
||||
template <class T>
|
||||
/*concept*/ constexpr bool movable = Internal::movable_impl<T>;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/ranges/owning_view.h>
|
||||
#include <AzCore/std/ranges/ref_view.h>
|
||||
|
||||
namespace AZStd::ranges::views
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
template<class T, class = void>
|
||||
constexpr bool convertible_to_ref_view = false;
|
||||
template<class T>
|
||||
constexpr bool convertible_to_ref_view<T, void_t<decltype(ranges::ref_view(declval<T>()))>> = true;
|
||||
|
||||
struct all_fn
|
||||
: Internal::range_adaptor_closure<all_fn>
|
||||
{
|
||||
template<class View>
|
||||
constexpr auto operator()(View&& t) const noexcept(noexcept(static_cast<decay_t<View>>(AZStd::forward<View>(t))))
|
||||
->enable_if_t<ranges::view<decay_t<View>>, decltype(static_cast<decay_t<View>>(AZStd::forward<View>(t)))>
|
||||
{
|
||||
return static_cast<decay_t<View>>(AZStd::forward<View>(t));
|
||||
}
|
||||
template<class View>
|
||||
constexpr auto operator()(View&& t) const noexcept(noexcept(ranges::ref_view(AZStd::forward<View>(t))))
|
||||
->enable_if_t<conjunction_v<
|
||||
bool_constant<!ranges::view<decay_t<View>>>,
|
||||
bool_constant<convertible_to_ref_view<View>>>, decltype(ranges::ref_view(AZStd::forward<View>(t)))>
|
||||
{
|
||||
return ranges::ref_view(AZStd::forward<View>(t));
|
||||
}
|
||||
template<class View>
|
||||
constexpr auto operator()(View&& t) const noexcept(noexcept(ranges::owning_view(AZStd::forward<View>(t))))
|
||||
->enable_if_t<conjunction_v<
|
||||
bool_constant<!ranges::view<decay_t<View>>>,
|
||||
bool_constant<!convertible_to_ref_view<View>>>, decltype(ranges::owning_view(AZStd::forward<View>(t)))>
|
||||
{
|
||||
return ranges::owning_view(AZStd::forward<View>(t));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::all_fn all{};
|
||||
}
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
template <class R, class = void>
|
||||
struct all_t {};
|
||||
template <class R>
|
||||
struct all_t<R, enable_if_t<ranges::viewable_range<R>>>
|
||||
{
|
||||
using type = decltype(views::all(declval<R>()));
|
||||
};
|
||||
}
|
||||
template<class R>
|
||||
using all_t = typename Internal::all_t<R>::type;
|
||||
}
|
||||
@ -0,0 +1,475 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/ranges/all_view.h>
|
||||
#include <AzCore/std/ranges/ranges_adaptor.h>
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
template<class T, size_t N, class = void>
|
||||
/*concept*/ constexpr bool has_tuple_element = false;
|
||||
|
||||
template<class T, size_t N>
|
||||
/*concept*/ constexpr bool has_tuple_element<T, N, enable_if_t<conjunction_v<
|
||||
sfinae_trigger<typename tuple_size<T>::type>,
|
||||
bool_constant<(N < tuple_size_v<T>)>,
|
||||
sfinae_trigger<tuple_element_t<N, T>>,
|
||||
bool_constant<convertible_to<decltype(AZStd::get<N>(declval<T>())), const tuple_element_t<N, T>&>> >
|
||||
>> = true;
|
||||
|
||||
template<class T, size_t N, class = void>
|
||||
/*concept*/ constexpr bool returnable_element = false;
|
||||
|
||||
template<class T, size_t N>
|
||||
/*concept*/ constexpr bool returnable_element<T, N, enable_if_t<
|
||||
is_reference_v<T>> > = true;
|
||||
|
||||
template<class T, size_t N>
|
||||
/*concept*/ constexpr bool returnable_element<T, N, enable_if_t<
|
||||
move_constructible<tuple_element_t<N, T>>> > = true;
|
||||
}
|
||||
|
||||
template<class View, size_t N, class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<View>>,
|
||||
bool_constant<view<View>>,
|
||||
bool_constant<Internal::has_tuple_element<range_value_t<View>, N>>,
|
||||
bool_constant<Internal::has_tuple_element<remove_reference_t<range_reference_t<View>>, N>>,
|
||||
bool_constant<Internal::returnable_element<range_reference_t<View>, N>> >
|
||||
>>
|
||||
class elements_view;
|
||||
|
||||
// Alias for elements_view which is useful for extracting keys from associative containers
|
||||
template<class View>
|
||||
using keys_view = elements_view<View, 0>;
|
||||
// Alias for elements_view which is useful for extracting values from associative containers
|
||||
template<class View>
|
||||
using values_view = elements_view<View, 1>;
|
||||
|
||||
template<class T, size_t N>
|
||||
inline constexpr bool enable_borrowed_range<elements_view<T, N>> = enable_borrowed_range<T>;
|
||||
|
||||
// views::elements customization point
|
||||
namespace views
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
template<size_t N>
|
||||
struct elements_fn
|
||||
: Internal::range_adaptor_closure<elements_fn<N>>
|
||||
{
|
||||
template <class View, class = enable_if_t<conjunction_v<
|
||||
bool_constant<viewable_range<View>>
|
||||
>>>
|
||||
constexpr auto operator()(View&& view) const
|
||||
{
|
||||
return elements_view<views::all_t<View>, N>(AZStd::forward<View>(view));
|
||||
}
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
template<size_t N>
|
||||
constexpr Internal::elements_fn<N> elements{};
|
||||
|
||||
constexpr auto keys = elements<0>;
|
||||
constexpr auto values = elements<1>;
|
||||
}
|
||||
}
|
||||
|
||||
template<class View, size_t N, class>
|
||||
class elements_view
|
||||
: public view_interface<elements_view<View, N>>
|
||||
{
|
||||
template<bool>
|
||||
struct iterator;
|
||||
template<bool>
|
||||
struct sentinel;
|
||||
|
||||
public:
|
||||
template <bool Enable = default_initializable<View>,
|
||||
class = enable_if_t<Enable>>
|
||||
elements_view() {}
|
||||
|
||||
explicit constexpr elements_view(View base)
|
||||
: m_base(AZStd::move(base))
|
||||
{
|
||||
}
|
||||
|
||||
template <bool Enable = copy_constructible<View>, class = enable_if_t<Enable>>
|
||||
constexpr View base() const&
|
||||
{
|
||||
return m_base;
|
||||
}
|
||||
constexpr View base()&&
|
||||
{
|
||||
return AZStd::move(m_base);
|
||||
}
|
||||
|
||||
template<bool Enable = !Internal::simple_view<View>, class = enable_if_t<Enable>>
|
||||
constexpr auto begin()
|
||||
{
|
||||
return iterator<false>{ ranges::begin(m_base) };
|
||||
}
|
||||
|
||||
template<bool Enable = range<const View>, class = enable_if_t<Enable>>
|
||||
constexpr auto begin() const
|
||||
{
|
||||
return iterator<true>{ ranges::begin(m_base) };
|
||||
}
|
||||
|
||||
template<bool Enable = !Internal::simple_view<View>, class = enable_if_t<Enable>>
|
||||
constexpr auto end()
|
||||
{
|
||||
if constexpr (!common_range<View>)
|
||||
{
|
||||
return sentinel<false>{ ranges::end(m_base) };
|
||||
}
|
||||
else
|
||||
{
|
||||
return iterator<false>{ ranges::end(m_base) };
|
||||
}
|
||||
}
|
||||
|
||||
template<bool Enable = range<const View>>
|
||||
constexpr auto end() const
|
||||
{
|
||||
if constexpr (!common_range<const View>)
|
||||
{
|
||||
return sentinel<true>{ ranges::end(m_base) };
|
||||
}
|
||||
else
|
||||
{
|
||||
return iterator<true>{ ranges::end(m_base) };
|
||||
}
|
||||
}
|
||||
|
||||
template<bool Enable = sized_range<View>, class = enable_if_t<Enable>>
|
||||
constexpr auto size()
|
||||
{
|
||||
return ranges::size(m_base);
|
||||
}
|
||||
|
||||
template<bool Enable = sized_range<const View>, class = enable_if_t<Enable>>
|
||||
constexpr auto size() const
|
||||
{
|
||||
return ranges::size(m_base);
|
||||
}
|
||||
|
||||
private:
|
||||
View m_base{};
|
||||
};
|
||||
|
||||
template<class View, size_t N, bool Const, class = void>
|
||||
struct elements_view_iterator_category {};
|
||||
|
||||
template<class View, size_t N, bool Const>
|
||||
struct elements_view_iterator_category<View, N, Const, enable_if_t<forward_range<Internal::maybe_const<Const, View>> >>
|
||||
{
|
||||
private:
|
||||
using Base = Internal::maybe_const<Const, View>;
|
||||
using IterCategory = typename iterator_traits<iterator_t<Base>>::iterator_category;
|
||||
public:
|
||||
using iterator_category = conditional_t<
|
||||
!is_lvalue_reference_v<decltype(AZStd::get<N>(*declval<iterator_t<Base>>()))>,
|
||||
input_iterator_tag,
|
||||
conditional_t<derived_from<IterCategory, random_access_iterator_tag>,
|
||||
random_access_iterator_tag,
|
||||
IterCategory>>;
|
||||
};
|
||||
|
||||
template<class View, size_t N, class ViewEnable>
|
||||
template<bool Const>
|
||||
struct elements_view<View, N, ViewEnable>::iterator
|
||||
: enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<View>>,
|
||||
bool_constant<view<View>>,
|
||||
bool_constant<Internal::has_tuple_element<range_value_t<View>, N>>,
|
||||
bool_constant<Internal::has_tuple_element<remove_reference_t<range_reference_t<View>>, N>>,
|
||||
bool_constant<Internal::returnable_element<range_reference_t<View>, N>> >
|
||||
, elements_view_iterator_category<View, N, Const>
|
||||
>
|
||||
{
|
||||
private:
|
||||
template <bool>
|
||||
friend struct sentinel;
|
||||
|
||||
using Base = Internal::maybe_const<Const, View>;
|
||||
public:
|
||||
|
||||
using iterator_concept = conditional_t<random_access_range<Base>,
|
||||
random_access_iterator_tag,
|
||||
conditional_t<bidirectional_range<Base>,
|
||||
bidirectional_iterator_tag,
|
||||
conditional_t<forward_range<Base>,
|
||||
forward_iterator_tag,
|
||||
input_iterator_tag>>>;
|
||||
|
||||
using value_type = remove_cvref_t<tuple_element_t<N, range_value_t<Base>>>;
|
||||
using difference_type = range_difference_t<Base>;
|
||||
|
||||
template<class BaseIter = iterator_t<Base>, class = enable_if_t<default_initializable<BaseIter>>>
|
||||
iterator() {}
|
||||
|
||||
constexpr iterator(iterator_t<Base> current)
|
||||
: m_current(AZStd::move(current))
|
||||
{
|
||||
}
|
||||
template<class ViewIter = iterator_t<View>, class BaseIter = iterator_t<Base>,
|
||||
class = enable_if_t<Const && convertible_to<ViewIter, BaseIter>>>
|
||||
iterator(iterator<!Const> i)
|
||||
: m_current(i.m_current)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr iterator_t<View> base() const& noexcept
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
constexpr iterator_t<View> base() &&
|
||||
{
|
||||
return AZStd::move(m_current);
|
||||
}
|
||||
|
||||
constexpr decltype(auto) operator*() const
|
||||
{
|
||||
return get_element(m_current);
|
||||
}
|
||||
|
||||
constexpr iterator& operator++()
|
||||
{
|
||||
++m_current;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr decltype(auto) operator++(int)
|
||||
{
|
||||
if constexpr (!forward_range<Base>)
|
||||
{
|
||||
++m_current;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool Enable = bidirectional_range<Base>, class = enable_if_t<Enable>>
|
||||
constexpr iterator& operator--() const
|
||||
{
|
||||
--m_current;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<bool Enable = bidirectional_range<Base>, class = enable_if_t<Enable>>
|
||||
constexpr iterator operator--(int) const
|
||||
{
|
||||
auto tmp = *this;
|
||||
--(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
|
||||
constexpr iterator& operator+=(difference_type n)
|
||||
{
|
||||
m_current += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
|
||||
constexpr iterator& operator-=(difference_type n)
|
||||
{
|
||||
m_current -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
|
||||
constexpr decltype(auto) operator[](difference_type n) const
|
||||
{
|
||||
return get_element(m_current + n);
|
||||
}
|
||||
|
||||
// equality_comparable
|
||||
template<class BaseIter = iterator_t<Base>, class = enable_if_t<equality_comparable<BaseIter>>>
|
||||
friend constexpr bool operator==(const iterator& x, const iterator& y)
|
||||
{
|
||||
return x.m_current == y.m_current;
|
||||
}
|
||||
friend constexpr bool operator!=(const iterator& y, const iterator& x)
|
||||
{
|
||||
return !operator==(x, y);
|
||||
}
|
||||
|
||||
// strict_weak_order
|
||||
template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
|
||||
friend constexpr bool operator<(const iterator& x, const iterator& y)
|
||||
{
|
||||
return x.m_current < y.m_current;
|
||||
}
|
||||
template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
|
||||
friend constexpr bool operator>(const iterator& x, const iterator& y)
|
||||
{
|
||||
return y < x;
|
||||
}
|
||||
template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
|
||||
friend constexpr bool operator<=(const iterator& x, const iterator& y)
|
||||
{
|
||||
return !(y < x);
|
||||
}
|
||||
template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
|
||||
friend constexpr bool operator>=(const iterator& x, const iterator& y)
|
||||
{
|
||||
return !(x < y);
|
||||
}
|
||||
|
||||
template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
|
||||
friend constexpr iterator operator+(const iterator& x, difference_type n)
|
||||
{
|
||||
iterator iterCopy(x);
|
||||
iterCopy += n;
|
||||
return iterCopy;
|
||||
}
|
||||
|
||||
template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
|
||||
friend constexpr iterator operator+(difference_type n, const iterator& x)
|
||||
{
|
||||
return n + x;
|
||||
}
|
||||
|
||||
template<bool Enable = random_access_range<Base>, class = enable_if_t<Enable>>
|
||||
friend constexpr iterator operator-(const iterator& x, difference_type n)
|
||||
{
|
||||
iterator iterCopy(x);
|
||||
iterCopy -= n;
|
||||
return iterCopy;
|
||||
}
|
||||
|
||||
template<class BaseIter = iterator_t<Base>, class = enable_if_t<sized_sentinel_for<BaseIter, BaseIter>>>
|
||||
friend constexpr difference_type operator-(const iterator& x, const iterator& y)
|
||||
{
|
||||
return x.m_current - y.m_current;
|
||||
}
|
||||
private:
|
||||
static constexpr decltype(auto) get_element(const iterator_t<Base>& i)
|
||||
{
|
||||
if constexpr (is_reference_v<range_reference_t<Base>>)
|
||||
{
|
||||
// Return a reference to the element of the tuple like type
|
||||
return AZStd::get<N>(*i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cast the result of calling AZStd::get on value type reference
|
||||
using E = remove_cv_t<tuple_element_t<N, range_reference_t<Base>>>;
|
||||
return static_cast<E>(AZStd::get<N>(*i));
|
||||
}
|
||||
}
|
||||
|
||||
//! iterator to range being viewed
|
||||
iterator_t<Base> m_current{};
|
||||
};
|
||||
|
||||
namespace ElementsViewInternal
|
||||
{
|
||||
struct requirements_fulfilled {};
|
||||
}
|
||||
|
||||
template<class View, size_t N, class ViewEnable>
|
||||
template<bool Const>
|
||||
struct elements_view<View, N, ViewEnable>::sentinel
|
||||
: enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<View>>,
|
||||
bool_constant<view<View>>,
|
||||
bool_constant<Internal::has_tuple_element<range_value_t<View>, N>>,
|
||||
bool_constant<Internal::has_tuple_element<remove_reference_t<range_reference_t<View>>, N>>,
|
||||
bool_constant<Internal::returnable_element<range_reference_t<View>, N>> >
|
||||
, ElementsViewInternal::requirements_fulfilled>
|
||||
{
|
||||
private:
|
||||
using Base = Internal::maybe_const<Const, View>;
|
||||
|
||||
public:
|
||||
sentinel() = default;
|
||||
|
||||
explicit constexpr sentinel(sentinel_t<View> end)
|
||||
: m_end(end)
|
||||
{
|
||||
}
|
||||
template<class SentinelView = sentinel_t<View>, class SentinelBase = sentinel_t<Base>,
|
||||
class = enable_if_t<Const && convertible_to<SentinelView, SentinelBase>>>
|
||||
constexpr sentinel(sentinel<!Const> s)
|
||||
: m_end(AZStd::move(s.m_end))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr sentinel_t<View> base() const
|
||||
{
|
||||
return m_end;
|
||||
}
|
||||
|
||||
// comparison operators
|
||||
template<bool OtherConst, class = enable_if_t<
|
||||
sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
|
||||
>>
|
||||
friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y)
|
||||
{
|
||||
return iterator_accessor(x) == y.m_end;
|
||||
}
|
||||
template<bool OtherConst>
|
||||
friend constexpr bool operator==(const sentinel& y, const iterator<OtherConst>& x)
|
||||
{
|
||||
return operator==(x, y);
|
||||
}
|
||||
template<bool OtherConst>
|
||||
friend constexpr bool operator!=(const iterator<OtherConst>& x, const sentinel& y)
|
||||
{
|
||||
return !operator==(x, y);
|
||||
}
|
||||
template<bool OtherConst>
|
||||
friend constexpr bool operator!=(const sentinel& y, const iterator<OtherConst>& x)
|
||||
{
|
||||
return !operator==(x, y);
|
||||
}
|
||||
|
||||
// difference operator
|
||||
template<bool OtherConst, class = enable_if_t<
|
||||
sized_sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
|
||||
>>
|
||||
friend constexpr range_difference_t<Internal::maybe_const<OtherConst, Base >>
|
||||
operator-(const iterator<OtherConst>& x, const sentinel& y)
|
||||
{
|
||||
return iterator_accessor(x) - y.m_end;
|
||||
}
|
||||
|
||||
template<bool OtherConst, class = enable_if_t<
|
||||
sized_sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
|
||||
>>
|
||||
friend constexpr range_difference_t<Internal::maybe_const<OtherConst, Base>>
|
||||
operator-(const sentinel& x, const iterator<OtherConst>& y)
|
||||
{
|
||||
return x.m_end - iterator_accessor(y);
|
||||
}
|
||||
private:
|
||||
// On MSVC The friend functions are can only access the sentinel struct members
|
||||
// The iterator struct which is a friend of the sentinel struct is NOT a friend
|
||||
// of the friend functions
|
||||
// So a shim is added to provide access to the iterator m_current member
|
||||
template<bool OtherConst>
|
||||
static constexpr auto iterator_accessor(const iterator<OtherConst>& it)
|
||||
{
|
||||
return it.m_current;
|
||||
}
|
||||
|
||||
sentinel_t<Base> m_end{};
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/ranges/ranges.h>
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
template<class T, class = void>
|
||||
class empty_view;
|
||||
|
||||
template<class T>
|
||||
class empty_view<T, enable_if_t<is_object_v<T>>>
|
||||
: public view_interface<empty_view<T>>
|
||||
{
|
||||
public:
|
||||
static constexpr T* begin() noexcept { return nullptr; }
|
||||
static constexpr T* end() noexcept { return nullptr; }
|
||||
static constexpr T* data() noexcept { return nullptr; }
|
||||
static constexpr size_t size() noexcept { return 0; }
|
||||
static constexpr bool empty() noexcept { return true; }
|
||||
};
|
||||
|
||||
namespace views
|
||||
{
|
||||
template<class T>
|
||||
constexpr empty_view<T> empty{};
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline constexpr bool enable_borrowed_range<empty_view<T>> = true;
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/base.h>
|
||||
|
||||
#include <AzCore/std/iterator/iterator_primitives.h>
|
||||
#include <AzCore/std/ranges/swap.h>
|
||||
#include <AzCore/std/typetraits/conjunction.h>
|
||||
#include <AzCore/std/typetraits/disjunction.h>
|
||||
#include <AzCore/std/typetraits/extent.h>
|
||||
#include <AzCore/std/typetraits/integral_constant.h>
|
||||
#include <AzCore/std/typetraits/is_class.h>
|
||||
#include <AzCore/std/typetraits/is_enum.h>
|
||||
#include <AzCore/std/typetraits/is_void.h>
|
||||
#include <AzCore/std/typetraits/remove_cvref.h>
|
||||
#include <AzCore/std/typetraits/void_t.h>
|
||||
#include <AzCore/std/utility/declval.h>
|
||||
|
||||
|
||||
namespace AZStd::ranges::Internal
|
||||
{
|
||||
template<class I1, class I2>
|
||||
void iter_swap(I1, I2) = delete;
|
||||
|
||||
template <class I1, class I2, class = void>
|
||||
constexpr bool is_class_or_enum_with_iter_swap_adl = false;
|
||||
|
||||
template <class I1, class I2>
|
||||
constexpr bool is_class_or_enum_with_iter_swap_adl<I1, I2, enable_if_t<conjunction_v<
|
||||
disjunction<
|
||||
disjunction<is_class<remove_cvref_t<I1>>, is_enum<remove_cvref_t<I1>>>,
|
||||
disjunction<is_class<remove_cvref_t<I2>>, is_enum<remove_cvref_t<I2>>>>,
|
||||
is_void<void_t<decltype(iter_swap(declval<I1>(), declval<I2>()))>>
|
||||
>>> = true;
|
||||
|
||||
struct iter_swap_fn
|
||||
{
|
||||
template <class I1, class I2>
|
||||
constexpr auto operator()(I1&& i1, I2&& i2) const
|
||||
->enable_if_t<is_class_or_enum_with_iter_swap_adl<I1, I2>
|
||||
>
|
||||
{
|
||||
iter_swap(AZStd::forward<I1>(i1), AZStd::forward<I1>(i2));
|
||||
}
|
||||
template <class I1, class I2>
|
||||
constexpr auto operator()(I1&& i1, I2&& i2) const
|
||||
->enable_if_t<conjunction_v<bool_constant<!is_class_or_enum_with_iter_swap_adl<I1, I2>>,
|
||||
bool_constant<indirectly_readable<I1>>,
|
||||
bool_constant<indirectly_readable<I2>>,
|
||||
bool_constant<swappable_with<iter_reference_t<I1>, iter_reference_t<I2>>>
|
||||
>>
|
||||
{
|
||||
ranges::swap(*i1, *i2);
|
||||
}
|
||||
|
||||
template <class I1, class I2>
|
||||
constexpr auto operator()(I1&& i1, I2&& i2) const
|
||||
->enable_if_t<conjunction_v<bool_constant<!is_class_or_enum_with_iter_swap_adl<I1, I2>>,
|
||||
bool_constant<!swappable_with<iter_reference_t<I1>, iter_reference_t<I2>>>,
|
||||
bool_constant<indirectly_movable_storable<I1, I2>>,
|
||||
bool_constant<indirectly_movable_storable<I2, I1>>
|
||||
>>
|
||||
{
|
||||
*AZStd::forward<I1>(i1) = iter_exchange_move(AZStd::forward<I2>(i2), AZStd::forward<I1>(i1));
|
||||
}
|
||||
|
||||
private:
|
||||
template<class X, class Y>
|
||||
static constexpr iter_value_t<X> iter_exchange_move(X&& x, Y&& y)
|
||||
noexcept(noexcept(iter_value_t<X>(iter_move(x))) && noexcept(*x = iter_move(y)))
|
||||
{
|
||||
iter_value_t<X> old_value(iter_move(x));
|
||||
*x = iter_move(y);
|
||||
return old_value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
// Workaround for clang bug https://bugs.llvm.org/show_bug.cgi?id=37556
|
||||
// Using a placeholder namespace to wrap the customization point object
|
||||
// inline namespce and then bring that placeholder namespace into scope
|
||||
namespace workaround
|
||||
{
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
inline constexpr Internal::iter_swap_fn iter_swap{};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace AZStd::Internal
|
||||
{
|
||||
template <class I1, class I2, class = void>
|
||||
constexpr bool indirectly_swappable_impl = false;
|
||||
template <class I1, class I2>
|
||||
constexpr bool indirectly_swappable_impl<I1, I2, enable_if_t<conjunction_v<
|
||||
bool_constant<indirectly_readable<I1>>,
|
||||
bool_constant<indirectly_readable<I2>>,
|
||||
is_void<void_t<
|
||||
decltype(AZStd::ranges::iter_swap(declval<I1>(), declval<I1>())),
|
||||
decltype(AZStd::ranges::iter_swap(declval<I2>(), declval<I2>())),
|
||||
decltype(AZStd::ranges::iter_swap(declval<I1>(), declval<I2>())),
|
||||
decltype(AZStd::ranges::iter_swap(declval<I2>(), declval<I1>()))>>
|
||||
>>> = true;
|
||||
}
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
template<class I1, class I2 = I1>
|
||||
/*concept*/ constexpr bool indirectly_swappable = Internal::indirectly_swappable_impl<I1, I2>;
|
||||
}
|
||||
|
||||
@ -0,0 +1,430 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/ranges/all_view.h>
|
||||
#include <AzCore/std/ranges/ranges_adaptor.h>
|
||||
#include <AzCore/std/ranges/ranges_functional.h>
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
template<class View, class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<View>>,
|
||||
bool_constant<view<View>>,
|
||||
bool_constant<input_range<range_reference_t<View>>>
|
||||
>>>
|
||||
class join_view;
|
||||
|
||||
// views::join customization point
|
||||
namespace views
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
struct join_view_fn
|
||||
: Internal::range_adaptor_closure<join_view_fn>
|
||||
{
|
||||
template <class View, class = enable_if_t<conjunction_v<
|
||||
bool_constant<viewable_range<View>>
|
||||
>>>
|
||||
constexpr auto operator()(View&& view) const
|
||||
{
|
||||
return join_view(views::all(AZStd::forward<View>(view)));
|
||||
}
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::join_view_fn join{};
|
||||
}
|
||||
}
|
||||
|
||||
template<class View, class>
|
||||
class join_view
|
||||
: public view_interface<join_view<View>>
|
||||
{
|
||||
template<bool>
|
||||
struct iterator;
|
||||
template<bool>
|
||||
struct sentinel;
|
||||
|
||||
public:
|
||||
template <bool Enable = default_initializable<View>,
|
||||
class = enable_if_t<Enable>>
|
||||
join_view() {}
|
||||
|
||||
explicit constexpr join_view(View base)
|
||||
: m_base(AZStd::move(base))
|
||||
{}
|
||||
|
||||
|
||||
template <bool Enable = copy_constructible<View>, class = enable_if_t<Enable>>
|
||||
constexpr View base() const&
|
||||
{
|
||||
return m_base;
|
||||
}
|
||||
constexpr View base() &&
|
||||
{
|
||||
return AZStd::move(m_base);
|
||||
}
|
||||
|
||||
constexpr auto begin()
|
||||
{
|
||||
constexpr bool UseConst = Internal::simple_view<View> && is_reference_v<range_reference_t<View>>;
|
||||
return iterator<UseConst>{ *this, ranges::begin(m_base) };
|
||||
}
|
||||
|
||||
template<class ConstView = const View,
|
||||
class = enable_if_t<input_range<ConstView> && is_reference_v<range_reference_t<ConstView>>>>
|
||||
constexpr auto begin() const
|
||||
{
|
||||
return iterator<true>{ *this, ranges::begin(m_base) };
|
||||
}
|
||||
|
||||
constexpr decltype(auto) end()
|
||||
{
|
||||
if constexpr (forward_range<View> && is_reference_v<InnerRange> &&
|
||||
forward_range<InnerRange> && common_range<View> && common_range<InnerRange>)
|
||||
{
|
||||
return iterator<Internal::simple_view<View>>{ *this, ranges::end(m_base) };
|
||||
}
|
||||
else
|
||||
{
|
||||
return sentinel<Internal::simple_view<View>>{ *this };
|
||||
}
|
||||
}
|
||||
template<class ConstView = const View,
|
||||
class = enable_if_t<input_range<ConstView> && is_reference_v<range_reference_t<ConstView>>>>
|
||||
constexpr auto end() const
|
||||
{
|
||||
if constexpr (forward_range<const View> && forward_range<range_reference_t<const View>> &&
|
||||
common_range<const View> && common_range<range_reference_t<const View>>)
|
||||
{
|
||||
return iterator<true>{ *this, ranges::end(m_base) };
|
||||
}
|
||||
else
|
||||
{
|
||||
return sentinel<true>{ *this };
|
||||
}
|
||||
}
|
||||
private:
|
||||
View m_base{};
|
||||
|
||||
using InnerRange = range_reference_t<View>;
|
||||
// When the inner range for the view is a reference it doesn't need to be stored
|
||||
struct InnerRangeIsReference {};
|
||||
using InnerRange_t = conditional_t<is_reference_v<InnerRange>, InnerRangeIsReference,
|
||||
Internal::non_propagating_cache<remove_cv_t<InnerRange>>>;
|
||||
AZ_NO_UNIQUE_ADDRESS InnerRange_t m_inner{};
|
||||
};
|
||||
|
||||
template<class View, bool Const, class = void>
|
||||
struct join_view_iterator_category {};
|
||||
template<class View, bool Const>
|
||||
struct join_view_iterator_category<View, Const, enable_if_t<conjunction_v<
|
||||
is_reference<range_reference_t<Internal::maybe_const<Const, View>>>,
|
||||
bool_constant<forward_range<Internal::maybe_const<Const, View>>>,
|
||||
bool_constant<forward_range<range_reference_t<Internal::maybe_const<Const, View>>>>
|
||||
>>>
|
||||
{
|
||||
private:
|
||||
using Base = Internal::maybe_const<Const, View>;
|
||||
using OuterC = typename iterator_traits<iterator_t<Base>>::iterator_category;
|
||||
using InnerC = typename iterator_traits<iterator_t<range_reference_t<Base>>>::iterator_category;
|
||||
public:
|
||||
using iterator_category = conditional_t<conjunction_v<
|
||||
bool_constant<derived_from<OuterC, bidirectional_iterator_tag>>,
|
||||
bool_constant<derived_from<InnerC, bidirectional_iterator_tag>>,
|
||||
bool_constant<common_range<range_reference_t<Base>>> >,
|
||||
bidirectional_iterator_tag,
|
||||
conditional_t<conjunction_v<
|
||||
bool_constant<derived_from<OuterC, forward_iterator_tag>>,
|
||||
bool_constant<derived_from<InnerC, forward_iterator_tag>> >,
|
||||
forward_iterator_tag,
|
||||
input_iterator_tag>>;
|
||||
};
|
||||
|
||||
|
||||
template<class R>
|
||||
join_view(R&&) -> join_view<views::all_t<R>>;
|
||||
|
||||
template<class View, class ViewEnable>
|
||||
template<bool Const>
|
||||
struct join_view<View, ViewEnable>::iterator
|
||||
: enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<View>>,
|
||||
bool_constant<view<View>>,
|
||||
bool_constant<input_range<range_reference_t<View>>>
|
||||
>, join_view_iterator_category<View, Const>
|
||||
>
|
||||
{
|
||||
private:
|
||||
template<bool>
|
||||
friend struct sentinel;
|
||||
|
||||
using Parent = Internal::maybe_const<Const, join_view>;
|
||||
using Base = Internal::maybe_const<Const, View>;
|
||||
using OuterIter = iterator_t<Base>;
|
||||
using InnerIter = iterator_t<range_reference_t<Base>>;
|
||||
static constexpr bool ref_is_glvalue = is_reference_v<range_reference_t<Base>>;
|
||||
public:
|
||||
|
||||
using iterator_concept = conditional_t<ref_is_glvalue && bidirectional_range<Base>
|
||||
&& common_range<Base>, bidirectional_iterator_tag,
|
||||
conditional_t<ref_is_glvalue && forward_range<Base> && forward_range<range_reference_t<Base>>,
|
||||
forward_iterator_tag,
|
||||
input_iterator_tag>>;
|
||||
|
||||
using value_type = range_value_t<range_reference_t<Base>>;
|
||||
using difference_type = common_type_t<range_difference_t<Base>,
|
||||
range_difference_t<range_reference_t<Base>>>;
|
||||
|
||||
template<bool Enable = default_initializable<OuterIter> && default_initializable<InnerIter>,
|
||||
class = enable_if_t<Enable>>
|
||||
iterator() {}
|
||||
|
||||
constexpr iterator(join_view& parent, OuterIter outer)
|
||||
: m_parent(addressof(parent))
|
||||
, m_outer(AZStd::move(outer))
|
||||
{
|
||||
satisfy();
|
||||
}
|
||||
template<bool Enable = convertible_to<iterator_t<View>, OuterIter> &&
|
||||
convertible_to<iterator_t<InnerRange>, InnerIter>,
|
||||
class = enable_if_t<Enable>>
|
||||
iterator(iterator<!Const> i)
|
||||
: m_parent(i.m_parent)
|
||||
, m_outer(i.m_outer)
|
||||
, m_inner(i.m_inner)
|
||||
{}
|
||||
|
||||
constexpr iterator_t<View> base() const
|
||||
{
|
||||
return m_inner;
|
||||
}
|
||||
|
||||
constexpr decltype(auto) operator*() const
|
||||
{
|
||||
return *m_inner;
|
||||
}
|
||||
|
||||
template<bool Enable = Internal::has_arrow<InnerIter> && copyable<InnerIter>,
|
||||
class = enable_if_t<Enable>>
|
||||
constexpr InnerIter operator->() const
|
||||
{
|
||||
return m_inner;
|
||||
}
|
||||
|
||||
constexpr iterator& operator++()
|
||||
{
|
||||
if constexpr (ref_is_glvalue)
|
||||
{
|
||||
auto&& innerRange = *m_outer;
|
||||
if (++m_inner == ranges::end(innerRange))
|
||||
{
|
||||
++m_outer;
|
||||
satisfy();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto&& innerRange = *m_parent->m_inner;
|
||||
if (++m_inner == ranges::end(innerRange))
|
||||
{
|
||||
++m_outer;
|
||||
satisfy();
|
||||
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr void operator++(int)
|
||||
{
|
||||
++(*this);
|
||||
}
|
||||
|
||||
template<bool Enable = ref_is_glvalue && bidirectional_range<Base> && bidirectional_range<range_reference_t<Base>>,
|
||||
class = enable_if_t<Enable && common_range<range_reference_t<Base>>>>
|
||||
constexpr iterator& operator--() const
|
||||
{
|
||||
if (m_outer == ranges::end(m_parent->m_base))
|
||||
{
|
||||
m_inner = ranges::end(*--m_outer);
|
||||
}
|
||||
// Move inner iterator backwards to the last element of the first non-empty outer iterator
|
||||
// by iterating backwards
|
||||
while (m_inner == ranges::begin(*m_outer))
|
||||
{
|
||||
m_inner = ranges::end(*--m_outer);
|
||||
--m_inner;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<bool Enable = ref_is_glvalue && bidirectional_range<Base> && bidirectional_range<range_reference_t<Base>>,
|
||||
class = enable_if_t<Enable && common_range<range_reference_t<Base>>>>
|
||||
constexpr iterator operator--(int) const
|
||||
{
|
||||
auto tmp = *this;
|
||||
--(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template<class OtherBase = Base,
|
||||
class = enable_if_t<ref_is_glvalue && equality_comparable<iterator_t<OtherBase>> &&
|
||||
equality_comparable<iterator_t<range_reference_t<OtherBase>>>>>
|
||||
friend constexpr bool operator==(const iterator& x, const iterator& y)
|
||||
{
|
||||
return x.m_outer == y.m_outer && x.m_inner == y.m_inner;
|
||||
}
|
||||
template<class OtherBase = Base,
|
||||
class = enable_if_t<ref_is_glvalue && equality_comparable<iterator_t<OtherBase>>&&
|
||||
equality_comparable<iterator_t<range_reference_t<OtherBase>>>>>
|
||||
friend constexpr bool operator!=(const iterator& x, const iterator& y)
|
||||
{
|
||||
return !operator==(x, y);
|
||||
}
|
||||
|
||||
// customization of iter_move and iter_swap
|
||||
|
||||
friend constexpr decltype(auto) iter_move(
|
||||
iterator& i)
|
||||
noexcept(noexcept(ranges::iter_move(i.m_inner)))
|
||||
{
|
||||
return ranges::iter_move(i.m_inner);
|
||||
}
|
||||
|
||||
|
||||
friend constexpr void iter_swap(
|
||||
iterator& x,
|
||||
iterator& y)
|
||||
noexcept(noexcept(ranges::iter_swap(x.m_inner, y.m_inner)))
|
||||
{
|
||||
return ranges::iter_swap(x.m_inner, y.m_inner);
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr void satisfy()
|
||||
{
|
||||
// dereference the outer iterator if the inner iterato is a reference
|
||||
// or make a copy of deref ference of outer iterator
|
||||
auto update_inner = [this](const iterator_t<Base>& x) constexpr -> auto&&
|
||||
{
|
||||
if constexpr (ref_is_glvalue) // *x is a reference
|
||||
{
|
||||
// workaround clang 9.0.0 bug where this is unused because
|
||||
// of not being used in one block of the if constexpr
|
||||
(void)this;
|
||||
return *x;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_parent->m_inner.emplace_deref(x);
|
||||
}
|
||||
};
|
||||
|
||||
for (; m_outer != ranges::end(m_parent->m_base); ++m_outer)
|
||||
{
|
||||
|
||||
auto&& inner = update_inner(m_outer);
|
||||
m_inner = ranges::begin(inner);
|
||||
// m_inner is end then the inner range is empty
|
||||
// and the next outer element is iterated over
|
||||
if (m_inner != ranges::end(inner))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if constexpr (ref_is_glvalue)
|
||||
{
|
||||
m_inner = InnerIter();
|
||||
}
|
||||
}
|
||||
|
||||
//! iterator to the outer view element of the view wrapped by the join view
|
||||
OuterIter m_outer{};
|
||||
//! iterator to the actually range element which is wrapped by the view
|
||||
InnerIter m_inner{};
|
||||
//! reference to parent join_view
|
||||
join_view<View>* m_parent{};
|
||||
};
|
||||
|
||||
|
||||
// sentinel type for iterator
|
||||
namespace JoinViewInternal
|
||||
{
|
||||
struct requirements_fulfilled {};
|
||||
}
|
||||
|
||||
template<class View, class ViewEnable>
|
||||
template<bool Const>
|
||||
struct join_view<View, ViewEnable>::sentinel
|
||||
: enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<View>>,
|
||||
bool_constant<view<View>>,
|
||||
bool_constant<input_range<range_reference_t<View>>>
|
||||
>, JoinViewInternal::requirements_fulfilled>
|
||||
{
|
||||
private:
|
||||
using Parent = Internal::maybe_const<Const, join_view>;
|
||||
using Base = Internal::maybe_const<Const, View>;
|
||||
|
||||
public:
|
||||
sentinel() = default;
|
||||
explicit constexpr sentinel(Parent& parent)
|
||||
: m_end(ranges::end(parent.m_base))
|
||||
{}
|
||||
template<bool Enable = Const,
|
||||
class = enable_if_t<Enable && convertible_to<sentinel_t<View>, sentinel_t<Base>>>>
|
||||
constexpr sentinel(sentinel<!Const> s)
|
||||
: m_end(AZStd::move(s.m_end))
|
||||
{
|
||||
}
|
||||
|
||||
// comparison operators
|
||||
template<bool OtherConst, class = enable_if_t<
|
||||
sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
|
||||
>>
|
||||
friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y)
|
||||
{
|
||||
return iterator_accessor(x) == y.m_end;
|
||||
}
|
||||
template<bool OtherConst, class = enable_if_t<
|
||||
sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
|
||||
>>
|
||||
friend constexpr bool operator==(const sentinel& y, const iterator<OtherConst>& x)
|
||||
{
|
||||
return operator==(x, y);
|
||||
}
|
||||
template<bool OtherConst, class = enable_if_t<
|
||||
sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
|
||||
>>
|
||||
friend constexpr bool operator!=(const iterator<OtherConst>& x, const sentinel& y)
|
||||
{
|
||||
return !operator==(x, y);
|
||||
}
|
||||
template<bool OtherConst, class = enable_if_t<
|
||||
sentinel_for<sentinel_t<Base>, iterator_t<Internal::maybe_const<OtherConst, Base>>>
|
||||
>>
|
||||
friend constexpr bool operator!=(const sentinel& y, const iterator<OtherConst>& x)
|
||||
{
|
||||
return !operator==(x, y);
|
||||
}
|
||||
private:
|
||||
// On MSVC The friend functions are can only access the sentinel struct members
|
||||
// The iterator struct which is a friend of the sentinel struct is NOT a friend
|
||||
// of the friend functions
|
||||
// So a shim is added to provide access to the iterator m_current member
|
||||
template<bool OtherConst>
|
||||
static constexpr auto iterator_accessor(const iterator<OtherConst>& it)
|
||||
{
|
||||
return it.m_outer;
|
||||
}
|
||||
sentinel_t<Base> m_end{};
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/ranges/ranges_adaptor.h>
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
template<class R, class = void>
|
||||
class owning_view;
|
||||
|
||||
template<class R>
|
||||
class owning_view<R, enable_if_t<ranges::range<R> && movable<R> &&
|
||||
!Internal::is_initializer_list<remove_cvref_t<R>> >>
|
||||
: public ranges::view_interface<owning_view<R>>
|
||||
{
|
||||
public:
|
||||
|
||||
template<class T = R, enable_if_t<default_initializable<T>>>
|
||||
constexpr owning_view() {}
|
||||
|
||||
constexpr owning_view(R&& t)
|
||||
: m_range{ AZStd::move(t) }
|
||||
{}
|
||||
|
||||
constexpr owning_view(owning_view&&) = default;
|
||||
constexpr owning_view& operator=(owning_view&&) = default;
|
||||
|
||||
constexpr R& base() & noexcept
|
||||
{
|
||||
return m_range;
|
||||
}
|
||||
constexpr const R& base() const& noexcept
|
||||
{
|
||||
return m_range;
|
||||
}
|
||||
constexpr R&& base() && noexcept
|
||||
{
|
||||
return AZStd::move(m_range);
|
||||
}
|
||||
constexpr const R&& base() const&& noexcept
|
||||
{
|
||||
return AZStd::move(m_range);
|
||||
}
|
||||
|
||||
constexpr iterator_t<R> begin()
|
||||
{
|
||||
return ranges::begin(m_range);
|
||||
}
|
||||
constexpr sentinel_t<R> end()
|
||||
{
|
||||
return ranges::end(m_range);
|
||||
}
|
||||
|
||||
template<class Rn = R>
|
||||
constexpr auto begin() const -> enable_if_t<range<const R>, decltype(ranges::begin(declval<Rn>()))>
|
||||
{
|
||||
return ranges::begin(m_range);
|
||||
}
|
||||
template<class Rn = R>
|
||||
constexpr auto end() const -> enable_if_t<range<const R>, decltype(ranges::end(declval<Rn>()))>
|
||||
{
|
||||
return ranges::end(m_range);
|
||||
}
|
||||
|
||||
template<class Rn = R>
|
||||
constexpr auto empty() -> enable_if_t<Internal::sfinae_trigger_v<decltype(ranges::empty(declval<Rn>()))>, bool>
|
||||
{
|
||||
return ranges::empty(m_range);
|
||||
}
|
||||
template<class Rn = R>
|
||||
constexpr auto empty() const -> enable_if_t<Internal::sfinae_trigger_v<decltype(ranges::empty(declval<Rn>()))>, bool>
|
||||
{
|
||||
return ranges::empty(m_range);
|
||||
}
|
||||
|
||||
template<class Rn = R>
|
||||
constexpr auto size() -> enable_if_t<sized_range<R>, decltype(ranges::size(declval<Rn>()))>
|
||||
{
|
||||
return ranges::size(m_range);
|
||||
}
|
||||
template<class Rn = R>
|
||||
constexpr auto size() const -> enable_if_t<sized_range<const R>, decltype(ranges::size(declval<Rn>()))>
|
||||
{
|
||||
return ranges::size(m_range);
|
||||
}
|
||||
|
||||
template<class Rn = R>
|
||||
constexpr auto data() -> enable_if_t<contiguous_range<R>, decltype(ranges::data(declval<Rn>()))>
|
||||
{
|
||||
return ranges::data(m_range);
|
||||
}
|
||||
template<class Rn = R>
|
||||
constexpr auto data() const -> enable_if_t<contiguous_range<const R>, decltype(ranges::data(declval<Rn>()))>
|
||||
{
|
||||
return ranges::data(m_range);
|
||||
}
|
||||
|
||||
private:
|
||||
R m_range;
|
||||
};
|
||||
|
||||
//! deduction guides
|
||||
template<class R>
|
||||
owning_view(R&&) -> owning_view<R>;
|
||||
|
||||
template<class T>
|
||||
inline constexpr bool enable_borrowed_range<owning_view<T>> = enable_borrowed_range<T>;
|
||||
}
|
||||
@ -0,0 +1,333 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/ranges/ranges.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
|
||||
namespace AZStd::ranges::views::Internal
|
||||
{
|
||||
// Call wrapping that performs perfecting forwarding of arguments
|
||||
// and take const and ref function qualifiers into account(rvalue functions will move)
|
||||
template<class Outer, class Inner>
|
||||
struct perfect_forwarding_call_wrapper
|
||||
{
|
||||
perfect_forwarding_call_wrapper(Outer&& outer, Inner&& inner)
|
||||
: m_outer{ AZStd::forward<Outer>(outer) }
|
||||
, m_inner{ AZStd::forward<Inner>(inner) }
|
||||
{}
|
||||
template<class... Args>
|
||||
constexpr decltype(auto) operator()(Args&&... args) &
|
||||
noexcept(noexcept(AZStd::invoke(m_outer, AZStd::invoke(m_inner, AZStd::forward<Args>(args)...))))
|
||||
{
|
||||
return AZStd::invoke(m_outer, AZStd::invoke(m_inner, AZStd::forward<Args>(args)...));
|
||||
}
|
||||
template<class... Args>
|
||||
constexpr decltype(auto) operator()(Args&&... args) const&
|
||||
noexcept(noexcept(AZStd::invoke(m_outer, AZStd::invoke(m_inner, AZStd::forward<Args>(args)...))))
|
||||
{
|
||||
return AZStd::invoke(m_outer, AZStd::invoke(m_inner, AZStd::forward<Args>(args)...));
|
||||
}
|
||||
//
|
||||
template<class... Args>
|
||||
constexpr decltype(auto) operator()(Args&&... args) &&
|
||||
noexcept(noexcept(AZStd::invoke(AZStd::move(m_outer),
|
||||
AZStd::invoke(AZStd::move(m_inner), AZStd::forward<Args>(args)...))))
|
||||
{
|
||||
return AZStd::invoke(AZStd::move(m_outer), AZStd::invoke(AZStd::move(m_inner), AZStd::forward<Args>(args)...));
|
||||
}
|
||||
template<class... Args>
|
||||
constexpr decltype(auto) operator()(Args&&... args) const&&
|
||||
noexcept(noexcept(AZStd::invoke(AZStd::move(m_outer),
|
||||
AZStd::invoke(AZStd::move(m_inner), AZStd::forward<Args>(args)...))))
|
||||
{
|
||||
return AZStd::invoke(AZStd::move(m_outer), AZStd::invoke(AZStd::move(m_inner), AZStd::forward<Args>(args)...));
|
||||
}
|
||||
private:
|
||||
Outer m_outer;
|
||||
Inner m_inner;
|
||||
};
|
||||
|
||||
// base class which provides the bitwise OR operator to
|
||||
// any derived classes
|
||||
// This can be used by inherited by range adaptor classes
|
||||
// to implement their to allow for changing
|
||||
// https://eel.is/c++draft/ranges#range.adaptor.object-1.3
|
||||
template<class RangeAdaptor>
|
||||
struct range_adaptor_closure;
|
||||
|
||||
// Return a closure new in which the outer range adaptor invokes the inner range adaptor
|
||||
template<class RangeAdaptorFunctor>
|
||||
struct range_adaptor_closure_forwarder
|
||||
: RangeAdaptorFunctor
|
||||
, range_adaptor_closure_forwarder<range_adaptor_closure<RangeAdaptorFunctor>>
|
||||
{
|
||||
constexpr explicit range_adaptor_closure_forwarder(RangeAdaptorFunctor&& closure)
|
||||
: RangeAdaptorFunctor{ AZStd::forward<RangeAdaptorFunctor>(closure) }
|
||||
{}
|
||||
};
|
||||
|
||||
template<class RangeAdaptor>
|
||||
using is_range_closure_t = bool_constant<derived_from<remove_cvref_t<RangeAdaptor>,
|
||||
range_adaptor_closure<remove_cvref_t<RangeAdaptor>>>>;
|
||||
|
||||
template<class T>
|
||||
struct range_adaptor_closure
|
||||
{
|
||||
template<class View, class U>
|
||||
friend constexpr auto operator|(View&& view, U&& closure) noexcept(is_nothrow_invocable_v<View, U>)
|
||||
-> enable_if_t<conjunction_v<
|
||||
bool_constant<viewable_range<View>>,
|
||||
is_range_closure_t<U>,
|
||||
bool_constant<same_as<T, remove_cvref_t<U>>>,
|
||||
bool_constant<invocable<U, View>>>,
|
||||
decltype(AZStd::invoke(AZStd::forward<U>(closure), AZStd::forward<View>(view)))>
|
||||
{
|
||||
return AZStd::invoke(AZStd::forward<U>(closure), AZStd::forward<View>(view));
|
||||
}
|
||||
|
||||
template<class U, class Target>
|
||||
friend constexpr auto operator|(U&& closure, Target&& outerClosure)
|
||||
noexcept(is_nothrow_constructible_v<remove_cvref_t<U>> && is_nothrow_constructible_v<remove_cvref_t<Target>>)
|
||||
->enable_if_t<conjunction_v<
|
||||
is_range_closure_t<U>,
|
||||
is_range_closure_t<Target>,
|
||||
bool_constant<same_as<T, remove_cvref_t<U>>>,
|
||||
bool_constant<constructible_from<decay_t<U>, U>>,
|
||||
bool_constant<constructible_from<decay_t<Target>, Target>>>,
|
||||
decltype(range_adaptor_closure_forwarder{
|
||||
perfect_forwarding_call_wrapper{AZStd::forward<Target>(outerClosure), AZStd::forward<U>(closure) }
|
||||
})>
|
||||
{
|
||||
// Create a perfect_forwarding_wrapper that wraps the outer adaptor around the inner adaptor
|
||||
// and then pass that to the range_adaptor_closure_forward struct which inherits from
|
||||
// range_adaptor_closure class template so that it is_range_closure_t template succeeds
|
||||
return range_adaptor_closure_forwarder{
|
||||
perfect_forwarding_call_wrapper{AZStd::forward<Target>(outerClosure), AZStd::forward<U>(closure) }
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace AZStd::ranges::views::Internal
|
||||
|
||||
namespace AZStd::ranges::Internal
|
||||
{
|
||||
// Copyable Wrapper - https://eel.is/c++draft/ranges#range.copy.wrap
|
||||
// Used to wrap a class that copy constructible, but not necessarily copy assignable
|
||||
// and implements a copy assignment operator using the optional emplace function
|
||||
// to construct in place
|
||||
template<class T, class = void>
|
||||
class copyable_box;
|
||||
|
||||
template<class T>
|
||||
class copyable_box<T, enable_if_t<is_copy_constructible_v<T>&& is_object_v<T>>>
|
||||
{
|
||||
public:
|
||||
template<class U = T, class = enable_if_t<default_initializable<U>>>
|
||||
constexpr copyable_box() noexcept(is_nothrow_constructible_v<T>)
|
||||
: copyable_box{ in_place }
|
||||
{}
|
||||
|
||||
template<class... Args>
|
||||
explicit constexpr copyable_box(in_place_t, Args&&... args) noexcept(is_nothrow_constructible_v<T>)
|
||||
: m_copyable_wrapper{ in_place, AZStd::forward<Args>(args)... }
|
||||
{}
|
||||
|
||||
constexpr copyable_box(const copyable_box&) = default;
|
||||
constexpr copyable_box(copyable_box&&) = default;
|
||||
|
||||
constexpr copyable_box& operator=(const copyable_box& other) noexcept(is_nothrow_copy_constructible_v<T>)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
if constexpr (copyable<T>)
|
||||
{
|
||||
m_copyable_wrapper = other.m_copyable_wrapper;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (other)
|
||||
{
|
||||
emplace(other.m_copyable_wrapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr copyable_box& operator=(copyable_box&& other) noexcept(is_nothrow_move_constructible_v<T>)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
if constexpr (movable<T>)
|
||||
{
|
||||
m_copyable_wrapper = AZStd::move(other.m_copyable_wrapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (other)
|
||||
{
|
||||
emplace(AZStd::move(other.m_copyable_wrapper));
|
||||
}
|
||||
else
|
||||
{
|
||||
reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
constexpr T& operator*() & noexcept
|
||||
{
|
||||
return *m_copyable_wrapper;
|
||||
}
|
||||
constexpr const T& operator*() const& noexcept
|
||||
{
|
||||
return *m_copyable_wrapper;
|
||||
}
|
||||
constexpr T&& operator*() && noexcept
|
||||
{
|
||||
return AZStd::move(*m_copyable_wrapper);
|
||||
}
|
||||
constexpr const T&& operator*() const&& noexcept
|
||||
{
|
||||
return AZStd::move(*m_copyable_wrapper);
|
||||
}
|
||||
|
||||
constexpr T* operator->() noexcept
|
||||
{
|
||||
return m_copyable_wrapper.operator->();
|
||||
}
|
||||
constexpr const T* operator->() const noexcept
|
||||
{
|
||||
return m_copyable_wrapper.operator->();
|
||||
}
|
||||
|
||||
constexpr explicit operator bool()
|
||||
{
|
||||
return m_copyable_wrapper.has_value();
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
constexpr T& emplace(Args&&... args)
|
||||
{
|
||||
return m_copyable_wrapper.emplace(AZStd::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
constexpr void reset() noexcept
|
||||
{
|
||||
m_copyable_wrapper.reset();
|
||||
}
|
||||
private:
|
||||
optional<T> m_copyable_wrapper;
|
||||
};
|
||||
|
||||
// Non-propagating cache - https://eel.is/c++draft/ranges#range.nonprop.cache
|
||||
// Enables an input view to temporarily cache values over as it is iterated over.
|
||||
// For copy construction, a current instance is default initialized
|
||||
// For move construction, the input object is reset
|
||||
// On copy assignment, the current instance is reset
|
||||
// Onmove assignment, both the current and input are reset
|
||||
|
||||
template<class T, class = void>
|
||||
class non_propagating_cache;
|
||||
|
||||
template<class T>
|
||||
class non_propagating_cache<T, enable_if_t<is_object_v<T>>>
|
||||
{
|
||||
public:
|
||||
template<class U = T, class = enable_if_t<default_initializable<U>>>
|
||||
constexpr non_propagating_cache() noexcept(is_nothrow_constructible_v<T>)
|
||||
: non_propagating_cache{ in_place }
|
||||
{}
|
||||
|
||||
template<class... Args>
|
||||
explicit constexpr non_propagating_cache(in_place_t, Args&&... args) noexcept
|
||||
: m_cache{ in_place, AZStd::forward<Args>(args)... }
|
||||
{}
|
||||
|
||||
// Purposely does not initialize the wrapper
|
||||
constexpr non_propagating_cache(const non_propagating_cache&)
|
||||
{}
|
||||
constexpr non_propagating_cache(non_propagating_cache&& other)
|
||||
{
|
||||
other.reset();
|
||||
}
|
||||
|
||||
constexpr auto operator=(const non_propagating_cache& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto operator=(non_propagating_cache&& other) noexcept
|
||||
{
|
||||
reset();
|
||||
other.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr T& operator*() & noexcept
|
||||
{
|
||||
return *m_cache;
|
||||
}
|
||||
constexpr const T& operator*() const& noexcept
|
||||
{
|
||||
return *m_cache;
|
||||
}
|
||||
constexpr T&& operator*() && noexcept
|
||||
{
|
||||
return AZStd::move(*m_cache);
|
||||
}
|
||||
constexpr const T&& operator*() const&& noexcept
|
||||
{
|
||||
return AZStd::move(*m_cache);
|
||||
}
|
||||
|
||||
constexpr T* operator->() noexcept
|
||||
{
|
||||
return m_cache.operator->();
|
||||
}
|
||||
constexpr const T* operator->() const noexcept
|
||||
{
|
||||
return m_cache.operator->();
|
||||
}
|
||||
|
||||
constexpr explicit operator bool()
|
||||
{
|
||||
return m_cache.has_value();
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
constexpr T& emplace(Args&&... args)
|
||||
{
|
||||
return m_cache.emplace(AZStd::forward<Args>(args)...);
|
||||
}
|
||||
template<class I>
|
||||
constexpr auto emplace_deref(const I& i) -> enable_if_t<constructible_from<T, decltype(*i)>, T&>
|
||||
{
|
||||
return m_cache.emplace(*i);
|
||||
}
|
||||
|
||||
constexpr void reset() noexcept
|
||||
{
|
||||
m_cache.reset();
|
||||
}
|
||||
private:
|
||||
optional<T> m_cache;
|
||||
};
|
||||
} // namespace AZStd::ranges::Internal
|
||||
@ -0,0 +1,996 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/function/identity.h>
|
||||
#include <AzCore/std/ranges/ranges.h>
|
||||
#include <AzCore/std/ranges/ranges_functional.h>
|
||||
#include <AzCore/std/ranges/subrange.h>
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
template<class T>
|
||||
struct min_max_result
|
||||
{
|
||||
AZ_NO_UNIQUE_ADDRESS T min;
|
||||
AZ_NO_UNIQUE_ADDRESS T max;
|
||||
|
||||
template<class T2, class = enable_if_t<convertible_to<const T&, T2>>>
|
||||
constexpr operator min_max_result<T2>() const&
|
||||
{
|
||||
return { min, max };
|
||||
}
|
||||
|
||||
template<class T2, class = enable_if_t<convertible_to<T, T2>>>
|
||||
constexpr operator min_max_result<T2>()&&
|
||||
{
|
||||
return { std::move(min), std::move(max) };
|
||||
}
|
||||
};
|
||||
template<class T>
|
||||
using minmax_result = min_max_result<T>;
|
||||
|
||||
template<class I>
|
||||
using minmax_element_result = min_max_result<I>;
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
struct min_fn
|
||||
{
|
||||
template<class T, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(const T& a, const T& b, Comp comp = {}, Proj proj = {}) const
|
||||
-> enable_if_t<indirect_strict_weak_order<Comp, projected<const T*, Proj>>, const T&>
|
||||
{
|
||||
return AZStd::invoke(comp, AZStd::invoke(proj, b), AZStd::invoke(proj, a)) ? b : a;
|
||||
}
|
||||
|
||||
template<class T, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(initializer_list<T> r, Comp comp = {}, Proj proj = {}) const
|
||||
->enable_if_t<conjunction_v<
|
||||
bool_constant<copyable<T>>,
|
||||
bool_constant<indirect_strict_weak_order<Comp, projected<const T*, Proj>>>>, T>
|
||||
{
|
||||
AZ_Assert(r.size() > 0, "ranges::min cannot be invoked with an empty initializer_list");
|
||||
auto it = r.begin();
|
||||
auto last = r.end();
|
||||
T result{ *it };
|
||||
for (; it != last; ++it)
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *it), AZStd::invoke(proj, result)))
|
||||
{
|
||||
result = *it;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class R, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(R&& r, Comp comp = {}, Proj proj = {}) const
|
||||
->enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<R>>,
|
||||
bool_constant<indirect_strict_weak_order<Comp, projected<iterator_t<R>, Proj>>>,
|
||||
bool_constant<indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*>>>, range_value_t<R>>
|
||||
{
|
||||
AZ_Assert(ranges::distance(r) > 0, "ranges::min cannot be invoked with an empty range");
|
||||
auto it = ranges::begin(r);
|
||||
auto last = ranges::end(r);
|
||||
range_value_t<R> result{ *it };
|
||||
for (; it != last; ++it)
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *it), AZStd::invoke(proj, result)))
|
||||
{
|
||||
result = *it;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
} // namespace Internal
|
||||
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::min_fn min{};
|
||||
}
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
struct max_fn
|
||||
{
|
||||
template<class T, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(const T& a, const T& b, Comp comp = {}, Proj proj = {}) const
|
||||
-> enable_if_t<indirect_strict_weak_order<Comp, projected<const T*, Proj>>, const T&>
|
||||
{
|
||||
return AZStd::invoke(comp, AZStd::invoke(proj, a), AZStd::invoke(proj, b)) ? b : a;
|
||||
}
|
||||
|
||||
template<class T, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(initializer_list<T> r, Comp comp = {}, Proj proj = {}) const
|
||||
->enable_if_t<conjunction_v<
|
||||
bool_constant<copyable<T>>,
|
||||
bool_constant<indirect_strict_weak_order<Comp, projected<const T*, Proj>>>>, T>
|
||||
{
|
||||
AZ_Assert(r.size() > 0, "ranges::max cannot be invoked with an empty initializer_list");
|
||||
auto it = r.begin();
|
||||
auto last = r.end();
|
||||
T result{ *it };
|
||||
for (; it != last; ++it)
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, result), AZStd::invoke(proj, *it)))
|
||||
{
|
||||
result = *it;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class R, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(R&& r, Comp comp = {}, Proj proj = {}) const
|
||||
->enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<R>>,
|
||||
bool_constant<indirect_strict_weak_order<Comp, projected<iterator_t<R>, Proj>>>,
|
||||
bool_constant<indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*>>>, range_value_t<R>>
|
||||
{
|
||||
AZ_Assert(ranges::distance(r) > 0, "ranges::max cannot be invoked with an empty range");
|
||||
auto it = ranges::begin(r);
|
||||
auto last = ranges::end(r);
|
||||
range_value_t<R> result{ *it };
|
||||
for (; it != last; ++it)
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, result), AZStd::invoke(proj, *it)))
|
||||
{
|
||||
result = *it;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
} // namespace Internal
|
||||
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::max_fn max{};
|
||||
}
|
||||
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
struct minmax_fn
|
||||
{
|
||||
template<class T, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(const T& a, const T& b, Comp comp = {}, Proj proj = {}) const
|
||||
-> enable_if_t<indirect_strict_weak_order<Comp, projected<const T*, Proj>>, minmax_result<const T&>>
|
||||
{
|
||||
return AZStd::invoke(comp, AZStd::invoke(proj, b), AZStd::invoke(proj, a)) ?
|
||||
minmax_result<const T&>{b, a} : minmax_result<const T&>{a, b};
|
||||
}
|
||||
|
||||
template<class T, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(initializer_list<T> r, Comp comp = {}, Proj proj = {}) const
|
||||
->enable_if_t<conjunction_v<
|
||||
bool_constant<copyable<T>>,
|
||||
bool_constant<indirect_strict_weak_order<Comp, projected<const T*, Proj>>>>,
|
||||
minmax_result<T>>
|
||||
{
|
||||
AZ_Assert(r.size() > 0, "ranges::minmax cannot be invoked with an empty initializer_list");
|
||||
auto first = r.begin();
|
||||
minmax_result<T> result{ *first, *first };
|
||||
auto last = r.end();
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
// Algorithm requires at most (3/2 * ranges::distance(r)) comparisons
|
||||
if (auto prevIt = first++; first == last)
|
||||
{
|
||||
// Find the leftmost smallest element
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, result.min)))
|
||||
{
|
||||
result.min = *prevIt;
|
||||
}
|
||||
// Find the rightmost largest element
|
||||
else if (!AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, result.max)))
|
||||
{
|
||||
result.max = *prevIt;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For every two elements performs a transitive comparisons to calculate either new
|
||||
// leftmost min or rightmost max using at most three comparisons
|
||||
// (*first < *prevIt) && (*first < result.min), then *first = leftmost min
|
||||
// (*first < *prevIt) && (*prevIt >= result.max), then *prevIt = rightmost max
|
||||
// else
|
||||
// (*first >= *prevIt) && (*prevIt < result.min), then *prevIt = leftmost min
|
||||
// (*first >= *prevIt) && (*first >= result.max), then *first = rightmost max
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *first), AZStd::invoke(proj, *prevIt)))
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *first), AZStd::invoke(proj, result.min)))
|
||||
{
|
||||
result.min = *first;
|
||||
}
|
||||
if (!AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, result.max)))
|
||||
{
|
||||
result.max = *prevIt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, result.min)))
|
||||
{
|
||||
result.min = *prevIt;
|
||||
}
|
||||
if (!AZStd::invoke(comp, AZStd::invoke(proj, *first), AZStd::invoke(proj, result.max)))
|
||||
{
|
||||
result.max = *first;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
template<class R, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(R&& r, Comp comp = {}, Proj proj = {}) const
|
||||
-> enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<R>>,
|
||||
bool_constant<indirect_strict_weak_order<Comp, projected<iterator_t<R>, Proj>>>,
|
||||
bool_constant<indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*>>>,
|
||||
minmax_result<range_value_t<R>>>
|
||||
{
|
||||
AZ_Assert(ranges::distance(r) > 0, "ranges::minmax cannot be invoked with an empty range");
|
||||
auto first = ranges::begin(r);
|
||||
minmax_result<range_value_t<R>> result{ *first, *first };
|
||||
auto last = ranges::end(r);
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
// Algorithm requires at most (3/2 * ranges::distance(r)) comparisons
|
||||
if (auto prevIt = first++; first == last)
|
||||
{
|
||||
// Find the leftmost smallest element
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, result.min)))
|
||||
{
|
||||
result.min = *prevIt;
|
||||
}
|
||||
// Find the rightmost largest element
|
||||
else if (!AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, result.max)))
|
||||
{
|
||||
result.max = *prevIt;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For every two elements performs a transitive comparisons to calculate either new
|
||||
// leftmost min or rightmost max using at most three comparisons
|
||||
// (*first < *prevIt) && (*first < result.min), then *first = leftmost min
|
||||
// (*first < *prevIt) && (*prevIt >= result.max), then *prevIt = rightmost max
|
||||
// else
|
||||
// (*first >= *prevIt) && (*prevIt < result.min), then *prevIt = leftmost min
|
||||
// (*first >= *prevIt) && (*first >= result.max), then *first = rightmost max
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *first), AZStd::invoke(proj, *prevIt)))
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *first), AZStd::invoke(proj, result.min)))
|
||||
{
|
||||
result.min = *first;
|
||||
}
|
||||
if (!AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, result.max)))
|
||||
{
|
||||
result.max = *prevIt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, result.min)))
|
||||
{
|
||||
result.min = *prevIt;
|
||||
}
|
||||
if (!AZStd::invoke(comp, AZStd::invoke(proj, *first), AZStd::invoke(proj, result.max)))
|
||||
{
|
||||
result.max = *first;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::minmax_fn minmax{};
|
||||
}
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
struct min_element_fn
|
||||
{
|
||||
template<class I, class S, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(I first, S last, Comp comp = {}, Proj proj = {}) const
|
||||
-> enable_if_t<conjunction_v<
|
||||
bool_constant<forward_iterator<I>>,
|
||||
bool_constant<sentinel_for<S, I>>,
|
||||
bool_constant<indirect_strict_weak_order<Comp, projected<I, Proj>>>
|
||||
>, I>
|
||||
{
|
||||
I result{ first };
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *first), AZStd::invoke(proj, *result)))
|
||||
{
|
||||
result = first;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
template<class R, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(R&& r, Comp comp = {}, Proj proj = {}) const
|
||||
->enable_if_t<conjunction_v<
|
||||
bool_constant<forward_range<R>>,
|
||||
bool_constant<indirect_strict_weak_order<Comp, projected<iterator_t<R>, Proj>>>
|
||||
>, borrowed_iterator_t<R>>
|
||||
{
|
||||
auto it = ranges::begin(r);
|
||||
auto last = ranges::end(r);
|
||||
borrowed_iterator_t<R> result{ it };
|
||||
for (; it != last; ++it)
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *it), AZStd::invoke(proj, *result)))
|
||||
{
|
||||
result = it;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::min_element_fn min_element{};
|
||||
}
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
struct max_element_fn
|
||||
{
|
||||
template<class I, class S, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(I first, S last, Comp comp = {}, Proj proj = {}) const
|
||||
-> enable_if_t<conjunction_v<
|
||||
bool_constant<forward_iterator<I>>,
|
||||
bool_constant<sentinel_for<S, I>>,
|
||||
bool_constant<indirect_strict_weak_order<Comp, projected<I, Proj>>>
|
||||
>, I>
|
||||
{
|
||||
I result{ first };
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *result), AZStd::invoke(proj, *first)))
|
||||
{
|
||||
result = first;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
template<class R, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(R&& r, Comp comp = {}, Proj proj = {}) const
|
||||
->enable_if_t<conjunction_v<
|
||||
bool_constant<forward_range<R>>,
|
||||
bool_constant<indirect_strict_weak_order<Comp, projected<iterator_t<R>, Proj>>>
|
||||
>, borrowed_iterator_t<R>>
|
||||
{
|
||||
auto first = ranges::begin(r);
|
||||
auto last = ranges::end(r);
|
||||
borrowed_iterator_t<R> result{ first };
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *result), AZStd::invoke(proj, *first)))
|
||||
{
|
||||
result = first;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::max_element_fn max_element{};
|
||||
}
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
// C++ Standard footnote
|
||||
// minmax_element returns the rightmost iterator with the largest value
|
||||
// and is intentionally different from max_element which returns
|
||||
// the leftmost iterator with the largest value
|
||||
// https://eel.is/c++draft/algorithms#footnoteref-225
|
||||
struct minmax_element_fn
|
||||
{
|
||||
template<
|
||||
class I,
|
||||
class S,
|
||||
class Proj = identity,
|
||||
class Comp = ranges::less>
|
||||
constexpr auto operator()(I first, S last, Comp comp = {}, Proj proj = {}) const
|
||||
->enable_if_t<conjunction_v<
|
||||
bool_constant<forward_iterator<I>>,
|
||||
bool_constant<sentinel_for<S, I>>,
|
||||
bool_constant<indirect_strict_weak_order<Comp, projected<I, Proj>>>
|
||||
>, minmax_element_result<I>>
|
||||
{
|
||||
minmax_element_result<I> result{ first, first };
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
// Algorithm requires at most (3/2 * ranges::distance(r)) comparisons
|
||||
if (auto prevIt = first++; first == last)
|
||||
{
|
||||
// Find the leftmost smallest element
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, *result.min)))
|
||||
{
|
||||
result.min = prevIt;
|
||||
}
|
||||
// Find the rightmost largest element
|
||||
else if (!AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, *result.max)))
|
||||
{
|
||||
result.max = prevIt;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For every two elements performs a transitive comparisons to calculate either new
|
||||
// leftmost min or rightmost max using at most three comparisons
|
||||
// (*first < *prevIt) && (*first < result.min), then *first = leftmost min
|
||||
// (*first < *prevIt) && (*prevIt >= result.max), then *prevIt = rightmost max
|
||||
// else
|
||||
// (*first >= *prevIt) && (*prevIt < result.min), then *prevIt = leftmost min
|
||||
// (*first >= *prevIt) && (*first >= result.max), then *first = rightmost max
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *first), AZStd::invoke(proj, *prevIt)))
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *first), AZStd::invoke(proj, *result.min)))
|
||||
{
|
||||
result.min = first;
|
||||
}
|
||||
if (!AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, *result.max)))
|
||||
{
|
||||
result.max = prevIt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, *result.min)))
|
||||
{
|
||||
result.min = prevIt;
|
||||
}
|
||||
if (!AZStd::invoke(comp, AZStd::invoke(proj, *first), AZStd::invoke(proj, *result.max)))
|
||||
{
|
||||
result.max = first;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
template<class R, class Proj = identity, class Comp = ranges::less>
|
||||
constexpr auto operator()(R&& r, Comp comp = {}, Proj proj = {}) const
|
||||
->enable_if_t<conjunction_v<
|
||||
bool_constant<forward_range<R>>,
|
||||
bool_constant<indirect_strict_weak_order<Comp, projected<iterator_t<R>, Proj>>>
|
||||
>, minmax_element_result<borrowed_iterator_t<R>>>
|
||||
{
|
||||
auto first = ranges::begin(r);
|
||||
minmax_element_result<borrowed_iterator_t<R>> result{ first, first };
|
||||
auto last = ranges::end(r);
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
// Algorithm requires at most (3/2 * ranges::distance(r)) comparisons
|
||||
if (auto prevIt = first++; first == last)
|
||||
{
|
||||
// Find the leftmost smallest element
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, *result.min)))
|
||||
{
|
||||
result.min = prevIt;
|
||||
}
|
||||
// Find the rightmost largest element
|
||||
else if (!AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, *result.max)))
|
||||
{
|
||||
result.max = prevIt;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For every two elements performs a transitive comparisons to calculate either new
|
||||
// leftmost min or rightmost max using at most three comparisons
|
||||
// (*first < *prevIt) && (*first < result.min), then *first = leftmost min
|
||||
// (*first < *prevIt) && (*prevIt >= result.max), then *prevIt = rightmost max
|
||||
// else
|
||||
// (*first >= *prevIt) && (*prevIt < result.min), then *prevIt = leftmost min
|
||||
// (*first >= *prevIt) && (*first >= result.max), then *first = rightmost max
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *first), AZStd::invoke(proj, *prevIt)))
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *first), AZStd::invoke(proj, *result.min)))
|
||||
{
|
||||
result.min = first;
|
||||
}
|
||||
if (!AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, *result.max)))
|
||||
{
|
||||
result.max = prevIt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AZStd::invoke(comp, AZStd::invoke(proj, *prevIt), AZStd::invoke(proj, *result.min)))
|
||||
{
|
||||
result.min = prevIt;
|
||||
}
|
||||
if (!AZStd::invoke(comp, AZStd::invoke(proj, *first), AZStd::invoke(proj, *result.max)))
|
||||
{
|
||||
result.max = first;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::minmax_element_fn minmax_element{};
|
||||
}
|
||||
|
||||
// find algorithms
|
||||
namespace Internal
|
||||
{
|
||||
struct find_fn
|
||||
{
|
||||
template<class I, class S, class T, class Proj = identity, class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_iterator<I>>,
|
||||
bool_constant<sentinel_for<S, I>>,
|
||||
bool_constant<indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*>>
|
||||
>>>
|
||||
constexpr I operator()(I first, S last, const T& value, Proj proj = {}) const
|
||||
{
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
if (AZStd::invoke(proj, *first) == value)
|
||||
{
|
||||
return first;
|
||||
}
|
||||
}
|
||||
|
||||
return first;
|
||||
}
|
||||
template<class R, class T, class Proj = identity, class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<R>>,
|
||||
bool_constant<indirect_binary_predicate<equal_to, projected<iterator_t<R>, Proj>, const T*>>
|
||||
>>>
|
||||
constexpr borrowed_iterator_t<R> operator()(R&& r, const T& value, Proj proj = {}) const
|
||||
{
|
||||
return operator()(ranges::begin(r), ranges::end(r),
|
||||
value, AZStd::move(proj));
|
||||
}
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::find_fn find{};
|
||||
}
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
struct find_if_fn
|
||||
{
|
||||
template<class I, class S, class Proj = identity, class Pred, class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_iterator<I>>,
|
||||
bool_constant<sentinel_for<S, I>>,
|
||||
bool_constant<indirect_unary_predicate<Pred, projected<I, Proj>>>
|
||||
>>>
|
||||
constexpr I operator()(I first, S last, Pred pred, Proj proj = {}) const
|
||||
{
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
if (AZStd::invoke(pred, AZStd::invoke(proj, *first)))
|
||||
{
|
||||
return first;
|
||||
}
|
||||
}
|
||||
|
||||
return first;
|
||||
}
|
||||
template<class R, class Proj = identity, class Pred, class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<R>>,
|
||||
bool_constant<indirect_unary_predicate<Pred, projected<iterator_t<R>, Proj>>>
|
||||
>>>
|
||||
constexpr borrowed_iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const
|
||||
{
|
||||
return operator()(ranges::begin(r), ranges::end(r),
|
||||
AZStd::move(pred), AZStd::move(proj));
|
||||
}
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::find_if_fn find_if{};
|
||||
}
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
struct find_if_not_fn
|
||||
{
|
||||
template<class I, class S, class Proj = identity, class Pred, class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_iterator<I>>,
|
||||
bool_constant<sentinel_for<S, I>>,
|
||||
bool_constant<indirect_unary_predicate<Pred, projected<I, Proj>>>
|
||||
>>>
|
||||
constexpr I operator()(I first, S last, Pred pred, Proj proj = {}) const
|
||||
{
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
if (!AZStd::invoke(pred, AZStd::invoke(proj, *first)))
|
||||
{
|
||||
return first;
|
||||
}
|
||||
}
|
||||
|
||||
return first;
|
||||
}
|
||||
template<class R, class Proj = identity, class Pred, class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<R>>,
|
||||
bool_constant<indirect_unary_predicate<Pred, projected<iterator_t<R>, Proj>>>
|
||||
>>>
|
||||
constexpr borrowed_iterator_t<R>
|
||||
operator()(R&& r, Pred pred, Proj proj = {}) const
|
||||
{
|
||||
return operator()(ranges::begin(r), ranges::end(r),
|
||||
AZStd::move(pred), AZStd::move(proj));
|
||||
}
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::find_if_not_fn find_if_not{};
|
||||
}
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
struct find_first_of_fn
|
||||
{
|
||||
template<class I1, class S1, class I2, class S2, class Pred = equal_to, class Proj1 = identity, class Proj2 = identity,
|
||||
class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_iterator<I1>>,
|
||||
bool_constant<sentinel_for<S1, I1>>,
|
||||
bool_constant<forward_iterator<I2>>,
|
||||
bool_constant<sentinel_for<S2, I2>>,
|
||||
bool_constant<indirectly_comparable<I1, I2, Pred, Proj1, Proj2>>
|
||||
>>>
|
||||
constexpr I1 operator()(I1 first1, S1 last1, I2 first2, S2 last2,
|
||||
Pred pred = {},
|
||||
Proj1 proj1 = {}, Proj2 proj2 = {}) const
|
||||
{
|
||||
for (; first1 != last1; ++first1)
|
||||
{
|
||||
for (I2 elementIt = first2; elementIt != last2; ++elementIt)
|
||||
{
|
||||
if (AZStd::invoke(pred, AZStd::invoke(proj1, *first1), AZStd::invoke(proj2, *elementIt)))
|
||||
{
|
||||
return first1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return first1;
|
||||
}
|
||||
template<class R1, class R2,class Pred = equal_to, class Proj1 = identity, class Proj2 = identity,
|
||||
class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<R1>>,
|
||||
bool_constant<forward_range<R2>>,
|
||||
bool_constant<indirectly_comparable<iterator_t<R1>, iterator_t<R2>, Pred, Proj1, Proj2>>
|
||||
>>>
|
||||
constexpr borrowed_iterator_t<R1> operator()(R1&& r1, R2&& r2,
|
||||
Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const
|
||||
{
|
||||
return operator()(ranges::begin(r1), ranges::end(r1),
|
||||
ranges::begin(r2), ranges::end(r2),
|
||||
AZStd::move(pred), AZStd::move(proj1), AZStd::move(proj2));
|
||||
}
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::find_first_of_fn find_first_of{};
|
||||
}
|
||||
|
||||
// ranges::mismatch
|
||||
template<class I1, class I2>
|
||||
using mismatch_result = in_in_result<I1, I2>;
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
struct mismatch_fn
|
||||
{
|
||||
template<class I1, class S1, class I2, class S2, class Pred = equal_to, class Proj1 = identity, class Proj2 = identity,
|
||||
class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_iterator<I1>>,
|
||||
bool_constant<sentinel_for<S1, I1>>,
|
||||
bool_constant<input_iterator<I2>>,
|
||||
bool_constant<sentinel_for<S2, I2>>,
|
||||
bool_constant<indirectly_comparable<I1, I2, Pred, Proj1, Proj2>>
|
||||
>>>
|
||||
constexpr mismatch_result<I1, I2> operator()(I1 first1, S1 last1, I2 first2, S2 last2,
|
||||
Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const
|
||||
{
|
||||
for (; first1 != last1 && first2 != last2; ++first1, ++first2)
|
||||
{
|
||||
if (!AZStd::invoke(pred, AZStd::invoke(proj1, *first1), AZStd::invoke(proj2, *first2)))
|
||||
{
|
||||
return { first1, first2 };
|
||||
}
|
||||
}
|
||||
|
||||
return { first1, first2 };
|
||||
}
|
||||
|
||||
template<class R1, class R2, class Pred = equal_to, class Proj1 = identity, class Proj2 = identity,
|
||||
class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<R1>>,
|
||||
bool_constant<input_range<R2>>,
|
||||
bool_constant<indirectly_comparable<iterator_t<R1>, iterator_t<R2>, Pred, Proj1, Proj2>>
|
||||
>>>
|
||||
constexpr mismatch_result<borrowed_iterator_t<R1>, borrowed_iterator_t<R2>> operator()(R1&& r1, R2&& r2,
|
||||
Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const
|
||||
{
|
||||
return operator()(ranges::begin(r1), ranges::end(r1),
|
||||
ranges::begin(r2), ranges::end(r2),
|
||||
AZStd::move(pred), AZStd::move(proj1), AZStd::move(proj2));
|
||||
}
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::mismatch_fn mismatch{};
|
||||
}
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
struct equal_fn
|
||||
{
|
||||
template<class I1, class S1, class I2, class S2, class Pred = equal_to, class Proj1 = identity, class Proj2 = identity,
|
||||
class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_iterator<I1>>,
|
||||
bool_constant<sentinel_for<S1, I1>>,
|
||||
bool_constant<input_iterator<I2>>,
|
||||
bool_constant<sentinel_for<S2, I2>>,
|
||||
bool_constant<indirectly_comparable<I1, I2, Pred, Proj1, Proj2>>
|
||||
>>>
|
||||
constexpr bool operator()(I1 first1, S1 last1, I2 first2, S2 last2,
|
||||
Pred pred = {},
|
||||
Proj1 proj1 = {}, Proj2 proj2 = {}) const
|
||||
{
|
||||
if constexpr (sized_sentinel_for<S1, I1> && sized_sentinel_for<S2, I2>)
|
||||
{
|
||||
if (ranges::distance(first1, last1) != ranges::distance(first2, last2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (; first1 != last1 && first2 != last2; ++first1, ++first2)
|
||||
{
|
||||
if (!AZStd::invoke(pred, AZStd::invoke(proj1, *first1), AZStd::invoke(proj2, *first2)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return first1 == last1 && first2 == last2;
|
||||
}
|
||||
|
||||
template<class R1, class R2, class Pred = equal_to, class Proj1 = identity, class Proj2 = identity,
|
||||
class = enable_if_t<conjunction_v<
|
||||
bool_constant<input_range<R1>>,
|
||||
bool_constant<input_range<R2>>,
|
||||
bool_constant<indirectly_comparable<iterator_t<R1>, iterator_t<R2>, Pred, Proj1, Proj2>>
|
||||
>>>
|
||||
constexpr bool operator()(R1&& r1, R2&& r2, Pred pred = {},
|
||||
Proj1 proj1 = {}, Proj2 proj2 = {}) const
|
||||
{
|
||||
return operator()(ranges::begin(r1), ranges::end(r1),
|
||||
ranges::begin(r2), ranges::end(r2),
|
||||
AZStd::move(pred), AZStd::move(proj1), AZStd::move(proj2));
|
||||
}
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::equal_fn equal{};
|
||||
}
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
struct search_fn
|
||||
{
|
||||
template<class I1, class S1, class I2, class S2, class Pred = equal_to, class Proj1 = identity, class Proj2 = identity,
|
||||
class = enable_if_t<conjunction_v<
|
||||
bool_constant<forward_iterator<I1>>,
|
||||
bool_constant<sentinel_for<S1, I1>>,
|
||||
bool_constant<forward_iterator<I2>>,
|
||||
bool_constant<sentinel_for<S2, I2>>,
|
||||
bool_constant<indirectly_comparable<I1, I2, Pred, Proj1, Proj2>>
|
||||
>>>
|
||||
constexpr subrange<I1> operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {},
|
||||
Proj1 proj1 = {}, Proj2 proj2 = {}) const
|
||||
{
|
||||
do
|
||||
{
|
||||
I1 it1 = first1;
|
||||
I2 it2 = first2;
|
||||
for (;; ++it1, ++it2)
|
||||
{
|
||||
if (it2 == last2)
|
||||
{
|
||||
// Reached the end of the second iteator sequence
|
||||
// Therefore the search has succeeded. return the matching range from the first sequence
|
||||
return { first1, it1 };
|
||||
}
|
||||
if (it1 == last1)
|
||||
{
|
||||
// The search has failed to find the second iterator sequence within the first
|
||||
return { last1, last1 };
|
||||
}
|
||||
if (!AZStd::invoke(pred, AZStd::invoke(proj1, *it1), AZStd::invoke(proj2, *it2)))
|
||||
{
|
||||
// Mismatch found break out of iteration of loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Increment to the next element in the range of [first1, last1) and restart the search
|
||||
++first1;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
template<class R1, class R2, class Pred = equal_to, class Proj1 = identity, class Proj2 = identity,
|
||||
class = enable_if_t<conjunction_v<
|
||||
bool_constant<forward_range<R1>>,
|
||||
bool_constant<forward_range<R2>>,
|
||||
bool_constant<indirectly_comparable<iterator_t<R1>, iterator_t<R2>, Pred, Proj1, Proj2>>
|
||||
>>>
|
||||
constexpr borrowed_subrange_t<R1>
|
||||
operator()(R1&& r1, R2&& r2, Pred pred = {},
|
||||
Proj1 proj1 = {}, Proj2 proj2 = {}) const
|
||||
{
|
||||
return operator()(ranges::begin(r1), ranges::end(r1),
|
||||
ranges::begin(r2), ranges::end(r2),
|
||||
AZStd::move(pred), AZStd::move(proj1), AZStd::move(proj2));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::search_fn search{};
|
||||
}
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
struct search_n_fn
|
||||
{
|
||||
template<class I, class S, class T, class Pred = equal_to, class Proj = identity,
|
||||
class = enable_if_t<conjunction_v<
|
||||
bool_constant<forward_iterator<I>>,
|
||||
bool_constant<sentinel_for<S, I>>,
|
||||
bool_constant<indirectly_comparable<I, const T*, Pred, Proj>>
|
||||
>>>
|
||||
constexpr subrange<I> operator()(I first, S last, iter_difference_t<I> count,
|
||||
const T& value, Pred pred = {}, Proj proj = {}) const
|
||||
{
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
iter_difference_t<I> foundCount{};
|
||||
I searchFirst = first;
|
||||
for (; foundCount != count && first != last; ++first, ++foundCount)
|
||||
{
|
||||
if (!AZStd::invoke(pred, AZStd::invoke(proj, *first), value))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundCount == count)
|
||||
{
|
||||
// count consecutive elements matching value have been found
|
||||
return { searchFirst, first };
|
||||
}
|
||||
if (first == last)
|
||||
{
|
||||
// search has failed to find count matching elements over the range.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return { last, last };
|
||||
}
|
||||
template<class R, class T, class Pred = equal_to, class Proj = identity,
|
||||
class = enable_if_t<conjunction_v<
|
||||
bool_constant<forward_range<R>>,
|
||||
bool_constant<indirectly_comparable<iterator_t<R>, const T*, Pred, Proj>>
|
||||
>>>
|
||||
constexpr borrowed_subrange_t<R> operator()(R&& r, range_difference_t<R> count,
|
||||
const T& value, Pred pred = {}, Proj proj = {}) const
|
||||
{
|
||||
return operator()(ranges::begin(r), ranges::end(r),
|
||||
AZStd::move(count), value, AZStd::move(pred), AZStd::move(proj));
|
||||
}
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::search_n_fn search_n{};
|
||||
}
|
||||
|
||||
// ranges::find_end_fn
|
||||
// use ranges::search
|
||||
namespace Internal
|
||||
{
|
||||
struct find_end_fn
|
||||
{
|
||||
template<class I1, class S1, class I2, class S2, class Pred = equal_to, class Proj1 = identity, class Proj2 = identity,
|
||||
class = enable_if_t<conjunction_v<
|
||||
bool_constant<forward_iterator<I1>>,
|
||||
bool_constant<sentinel_for<S1, I1>>,
|
||||
bool_constant<forward_iterator<I2>>,
|
||||
bool_constant<sentinel_for<S2, I2>>,
|
||||
bool_constant<indirectly_comparable<I1, I2, Pred, Proj1, Proj2>>
|
||||
>>>
|
||||
constexpr subrange<I1> operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {},
|
||||
Proj1 proj1 = {}, Proj2 proj2 = {}) const
|
||||
{
|
||||
if (first2 == last2)
|
||||
{
|
||||
return { last1, last1 };
|
||||
}
|
||||
auto foundSubrange = ranges::search(first1, last1, first2, last2, pred, proj1, proj2);
|
||||
if (foundSubrange.empty())
|
||||
{
|
||||
return foundSubrange;
|
||||
}
|
||||
do
|
||||
{
|
||||
auto nextIt = ranges::next(foundSubrange.begin());
|
||||
auto nextSubrange = ranges::search(nextIt, last1, first2, last2, pred, proj1, proj2);
|
||||
if (nextSubrange.empty())
|
||||
{
|
||||
return foundSubrange;
|
||||
}
|
||||
|
||||
foundSubrange = AZStd::move(nextSubrange);
|
||||
|
||||
} while (true);
|
||||
}
|
||||
template<class R1, class R2, class Pred = equal_to, class Proj1 = identity, class Proj2 = identity,
|
||||
class = enable_if_t<conjunction_v<
|
||||
bool_constant<forward_range<R1>>,
|
||||
bool_constant<forward_range<R2>>,
|
||||
bool_constant<indirectly_comparable<iterator_t<R1>, iterator_t<R2>, Pred, Proj1, Proj2>>
|
||||
>>>
|
||||
constexpr borrowed_subrange_t<R1> operator()(R1&& r1, R2&& r2, Pred pred = {},
|
||||
Proj1 proj1 = {}, Proj2 proj2 = {}) const
|
||||
{
|
||||
return operator()(ranges::begin(r1), ranges::end(r1),
|
||||
ranges::begin(r2), ranges::end(r2),
|
||||
AZStd::move(pred), AZStd::move(proj1), AZStd::move(proj2));
|
||||
}
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::find_end_fn find_end{};
|
||||
}
|
||||
} // namespace AZStd::ranges
|
||||
|
||||
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/function/identity.h>
|
||||
#include <AzCore/std/ranges/ranges.h>
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
struct equal_to
|
||||
{
|
||||
template<class T, class U>
|
||||
constexpr auto operator()(T&& t, U&& u) const ->
|
||||
enable_if_t<equality_comparable_with<T, U>, decltype(AZStd::forward<T>(t) == AZStd::forward<U>(u))>
|
||||
{
|
||||
return AZStd::forward<T>(t) == AZStd::forward<U>(u);
|
||||
}
|
||||
using is_transparent = void;
|
||||
};
|
||||
|
||||
struct not_equal_to
|
||||
{
|
||||
template<class T, class U>
|
||||
constexpr auto operator()(T&& t, U&& u) const ->
|
||||
enable_if_t<equality_comparable_with<T, U>, decltype(!ranges::equal_to{}(AZStd::forward<T>(t), AZStd::forward<U>(u)))>
|
||||
{
|
||||
return !ranges::equal_to{}(AZStd::forward<T>(t), AZStd::forward<U>(u));
|
||||
}
|
||||
using is_transparent = void;
|
||||
};
|
||||
|
||||
struct less
|
||||
{
|
||||
template<class T, class U>
|
||||
constexpr auto operator()(T&& t, U&& u) const ->
|
||||
enable_if_t<totally_ordered_with<T, U>, decltype(AZStd::forward<T>(t) < AZStd::forward<U>(u))>
|
||||
{
|
||||
return AZStd::forward<T>(t) < AZStd::forward<U>(u);
|
||||
}
|
||||
using is_transparent = void;
|
||||
};
|
||||
|
||||
struct greater
|
||||
{
|
||||
template<class T, class U>
|
||||
constexpr auto operator()(T&& t, U&& u) const ->
|
||||
enable_if_t<totally_ordered_with<T, U>, decltype(ranges::less{}(AZStd::forward<T>(t) < AZStd::forward<U>(u)))>
|
||||
{
|
||||
return ranges::less{}(AZStd::forward<U>(u) < AZStd::forward<T>(t));
|
||||
}
|
||||
using is_transparent = void;
|
||||
};
|
||||
|
||||
struct greater_equal
|
||||
{
|
||||
template<class T, class U>
|
||||
constexpr auto operator()(T&& t, U&& u) const ->
|
||||
enable_if_t<totally_ordered_with<T, U>, decltype(!ranges::less{}(AZStd::forward<T>(t), AZStd::forward<U>(u)))>
|
||||
{
|
||||
return !ranges::less{}(AZStd::forward<T>(t), AZStd::forward<U>(u));
|
||||
}
|
||||
using is_transparent = void;
|
||||
};
|
||||
|
||||
struct less_equal
|
||||
{
|
||||
template<class T, class U>
|
||||
constexpr auto operator()(T&& t, U&& u) const ->
|
||||
enable_if_t<totally_ordered_with<T, U>, decltype(!ranges::less{}(AZStd::forward<U>(u), AZStd::forward<T>(t)))>
|
||||
{
|
||||
return !ranges::less{}(AZStd::forward<U>(u), AZStd::forward<T>(t));
|
||||
}
|
||||
using is_transparent = void;
|
||||
};
|
||||
} // namespace AZStd::ranges
|
||||
|
||||
|
||||
// Iterator Common algorithm requirement concepts
|
||||
// https://eel.is/c++draft/alg.req
|
||||
namespace AZStd
|
||||
{
|
||||
template<class I1, class I2, class R, class P1 = identity, class P2 = identity, class = void>
|
||||
/*concept*/ constexpr bool indirectly_comparable = false;
|
||||
|
||||
template<class I1, class I2, class R, class P1, class P2>
|
||||
/*concept*/ constexpr bool indirectly_comparable<I1, I2, R, P1, P2, enable_if_t<
|
||||
indirect_binary_predicate<R, projected<I1, P1>, projected<I2, P2>>>> = true;
|
||||
|
||||
template<class I, class = void>
|
||||
/*concept*/ constexpr bool permutable = false;
|
||||
|
||||
template<class I>
|
||||
/*concept*/ constexpr bool permutable<I, enable_if_t<conjunction_v<
|
||||
bool_constant<forward_iterator<I>>,
|
||||
bool_constant<indirectly_movable_storable<I, I>>,
|
||||
bool_constant<indirectly_swappable<I, I>> >>> = true;
|
||||
|
||||
template<class I1, class I2, class Out, class R = ranges::less, class P1 = identity, class P2 = identity, class = void>
|
||||
/*concept*/ constexpr bool mergeable = false;
|
||||
|
||||
template<class I1, class I2, class Out, class R, class P1, class P2>
|
||||
/*concept*/ constexpr bool mergeable<I1, I2, Out, R, P1, P2, enable_if_t<conjunction_v<
|
||||
bool_constant<input_iterator<I1>>,
|
||||
bool_constant<input_iterator<I2>>,
|
||||
bool_constant<weakly_incrementable<Out>>,
|
||||
bool_constant<indirectly_copyable<I1, Out>>,
|
||||
bool_constant<indirectly_copyable<I2, Out>>,
|
||||
bool_constant<indirect_strict_weak_order<R, projected<I1, P1>, projected<I2, P2>>> >>> = true;
|
||||
|
||||
template<class I, class R = ranges::less, class P = identity, class = void>
|
||||
/*concept*/ constexpr bool sortable = false;
|
||||
template<class I, class R, class P>
|
||||
/*concept*/ constexpr bool sortable<I, R, P, enable_if_t<conjunction_v<
|
||||
bool_constant<permutable<I>>,
|
||||
bool_constant<indirect_strict_weak_order<R, projected<I, P>>> >>> = true;
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/ranges/ranges.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
template<class R, class = void>
|
||||
class ref_view;
|
||||
|
||||
template<class R>
|
||||
class ref_view<R, enable_if_t<ranges::range<R> && is_object_v<R>>>
|
||||
: public ranges::view_interface<ref_view<R>>
|
||||
{
|
||||
private:
|
||||
static void bindable_to_range(R&);
|
||||
static void bindable_to_range(R&&) = delete;
|
||||
public:
|
||||
template<class T, class = enable_if_t<conjunction_v<
|
||||
bool_constant<Internal::different_from<T, ref_view>>,
|
||||
bool_constant<convertible_to<T, R&>>,
|
||||
Internal::sfinae_trigger<decltype(bindable_to_range(declval<T>()))>
|
||||
>>>
|
||||
constexpr ref_view(T&& t)
|
||||
: m_range{ addressof(static_cast<R&>(AZStd::forward<T>(t))) }
|
||||
{}
|
||||
|
||||
constexpr R& base() const
|
||||
{
|
||||
return *m_range;
|
||||
}
|
||||
|
||||
constexpr iterator_t<R> begin() const
|
||||
{
|
||||
return ranges::begin(*m_range);
|
||||
}
|
||||
constexpr sentinel_t<R> end() const
|
||||
{
|
||||
return ranges::end(*m_range);
|
||||
}
|
||||
|
||||
template<class Rn = R>
|
||||
constexpr auto empty() const -> enable_if_t<Internal::sfinae_trigger_v<decltype(ranges::empty(*declval<Rn*>()))>, bool>
|
||||
{
|
||||
return ranges::empty(*m_range);
|
||||
}
|
||||
|
||||
template<class Rn = R>
|
||||
constexpr auto size() const -> enable_if_t<sized_range<R>, decltype(ranges::size(*declval<Rn*>()))>
|
||||
{
|
||||
return ranges::size(*m_range);
|
||||
}
|
||||
|
||||
template<class Rn = R>
|
||||
constexpr auto data() const -> enable_if_t<contiguous_range<R>, decltype(ranges::data(*declval<Rn*>()))>
|
||||
{
|
||||
return ranges::data(*m_range);
|
||||
}
|
||||
|
||||
private:
|
||||
R* m_range{};
|
||||
};
|
||||
|
||||
//! deduction guides
|
||||
template<class R>
|
||||
ref_view(R&) -> ref_view<R>;
|
||||
|
||||
template<class T>
|
||||
inline constexpr bool enable_borrowed_range<ref_view<T>> = true;
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/ranges/ranges_adaptor.h>
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
template<class T, class = void>
|
||||
class single_view;
|
||||
|
||||
namespace views
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
struct single_view_fn
|
||||
{
|
||||
template <class View>
|
||||
constexpr decltype(auto) operator()(View&& view) const
|
||||
{
|
||||
return single_view<decay_t<View>>(AZStd::forward<View>(view));
|
||||
}
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::single_view_fn single{};
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
class single_view<T, enable_if_t<copy_constructible<T>&& is_object_v<T>>>
|
||||
: public view_interface<single_view<T>>
|
||||
{
|
||||
public:
|
||||
|
||||
template<class T2 = T, class = enable_if_t<default_initializable<T2>>>
|
||||
single_view() {}
|
||||
constexpr explicit single_view(const T& t)
|
||||
: m_value(in_place, t)
|
||||
{}
|
||||
constexpr explicit single_view(T&& t)
|
||||
: m_value(in_place, AZStd::move(t))
|
||||
{}
|
||||
template<class... Args, enable_if_t<constructible_from<T, Args...>>* = nullptr>
|
||||
constexpr explicit single_view(in_place_t, Args&&... args)
|
||||
: m_value{ in_place, AZStd::forward<Args>(args)... }
|
||||
{}
|
||||
|
||||
constexpr T* begin() noexcept
|
||||
{
|
||||
return data();
|
||||
}
|
||||
constexpr const T* begin() const noexcept
|
||||
{
|
||||
return data();
|
||||
}
|
||||
constexpr T* end() noexcept
|
||||
{
|
||||
return data() + 1;
|
||||
}
|
||||
constexpr const T* end() const noexcept
|
||||
{
|
||||
return data() + 1;
|
||||
}
|
||||
static constexpr size_t size() noexcept
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
constexpr T* data() noexcept
|
||||
{
|
||||
return m_value.operator->();
|
||||
}
|
||||
constexpr const T* data() const noexcept
|
||||
{
|
||||
return m_value.operator->();
|
||||
}
|
||||
private:
|
||||
Internal::copyable_box<T> m_value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
single_view(T) -> single_view<T>;
|
||||
}
|
||||
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/ranges/all_view.h>
|
||||
#include <AzCore/std/ranges/ranges_adaptor.h>
|
||||
#include <AzCore/std/ranges/ranges_algorithm.h>
|
||||
#include <AzCore/std/ranges/ranges_functional.h>
|
||||
#include <AzCore/std/ranges/single_view.h>
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
template<class View, class Pattern, class = enable_if_t<conjunction_v<
|
||||
bool_constant<forward_range<View>>,
|
||||
bool_constant<forward_range<Pattern>>,
|
||||
bool_constant<view<View>>,
|
||||
bool_constant<view<Pattern>>,
|
||||
bool_constant<indirectly_comparable<iterator_t<View>, iterator_t<Pattern>, ranges::equal_to>>
|
||||
>>>
|
||||
class split_view;
|
||||
|
||||
// views::split customization point
|
||||
namespace views
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
struct split_view_fn
|
||||
: Internal::range_adaptor_closure<split_view_fn>
|
||||
{
|
||||
template <class View, class Pattern, class = enable_if_t<conjunction_v<
|
||||
bool_constant<viewable_range<View>>
|
||||
>>>
|
||||
constexpr auto operator()(View&& view, Pattern&& pattern) const
|
||||
{
|
||||
return split_view(AZStd::forward<View>(view), AZStd::forward<Pattern>(pattern));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::split_view_fn split{};
|
||||
}
|
||||
}
|
||||
|
||||
template<class View, class Pattern, class>
|
||||
class split_view
|
||||
: public view_interface<split_view<View, Pattern>>
|
||||
{
|
||||
struct iterator;
|
||||
struct sentinel;
|
||||
public:
|
||||
template <bool Enable = default_initializable<View> && default_initializable<Pattern>,
|
||||
class = enable_if_t<Enable>>
|
||||
split_view() {}
|
||||
|
||||
constexpr split_view(View base, Pattern pattern)
|
||||
: m_base(AZStd::move(base))
|
||||
, m_pattern(AZStd::move(pattern))
|
||||
{}
|
||||
|
||||
template<class R, class = enable_if_t<conjunction_v<
|
||||
bool_constant<forward_range<R>>,
|
||||
bool_constant<constructible_from<View, views::all_t<R>>>,
|
||||
bool_constant<constructible_from<Pattern, single_view<range_value_t<R>>>>
|
||||
>>>
|
||||
constexpr split_view(R&& r, range_value_t<R> e)
|
||||
: m_base{ views::all(AZStd::forward<R>(r)) }
|
||||
, m_pattern{ views::single(AZStd::move(e)) }
|
||||
{
|
||||
}
|
||||
|
||||
template <bool Enable = copy_constructible<View>, class = enable_if_t<Enable>>
|
||||
constexpr View base() const&
|
||||
{
|
||||
return m_base;
|
||||
}
|
||||
constexpr View base()&&
|
||||
{
|
||||
return AZStd::move(m_base);
|
||||
}
|
||||
|
||||
constexpr iterator begin()
|
||||
{
|
||||
return { *this, ranges::begin(m_base), find_next(ranges::begin(m_base)) };
|
||||
}
|
||||
|
||||
constexpr auto end()
|
||||
{
|
||||
if constexpr (common_range<View>)
|
||||
{
|
||||
return iterator{ *this, ranges::end(m_base), {} };
|
||||
}
|
||||
else
|
||||
{
|
||||
return sentinel{ *this };
|
||||
}
|
||||
}
|
||||
|
||||
constexpr subrange<iterator_t<View>> find_next(iterator_t<View> it)
|
||||
{
|
||||
auto [first, last] = ranges::search(subrange(it, ranges::end(m_base)), m_pattern);
|
||||
if (first != ranges::end(m_base) && ranges::empty(m_pattern))
|
||||
{
|
||||
++first;
|
||||
++last;
|
||||
}
|
||||
|
||||
return { first, last };
|
||||
}
|
||||
private:
|
||||
View m_base;
|
||||
Pattern m_pattern;
|
||||
};
|
||||
|
||||
template<class R, class P>
|
||||
split_view(R&&, P&&)->split_view<views::all_t<R>, views::all_t<P>>;
|
||||
|
||||
template<class R, class = enable_if_t<forward_range<R>>>
|
||||
split_view(R&&, range_value_t<R>)
|
||||
-> split_view<views::all_t<R>, single_view<range_value_t<R>>>;
|
||||
|
||||
template<class View, class Pattern, class Enable>
|
||||
struct split_view<View, Pattern, Enable>::iterator
|
||||
{
|
||||
using iterator_concept = forward_iterator_tag;
|
||||
using iterator_category = input_iterator_tag;
|
||||
using value_type = subrange<iterator_t<View>>;
|
||||
using difference_type = range_difference_t<View>;
|
||||
|
||||
iterator() = default;
|
||||
|
||||
constexpr iterator(split_view& parent, iterator_t<View> current, subrange<iterator_t<View>> patternSubrange)
|
||||
: m_parent(&parent)
|
||||
, m_current(AZStd::move(current))
|
||||
, m_next(AZStd::move(patternSubrange))
|
||||
{
|
||||
}
|
||||
constexpr iterator_t<View> base() const
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
constexpr value_type operator*() const
|
||||
{
|
||||
return { m_current, m_next.begin() };
|
||||
}
|
||||
|
||||
constexpr iterator& operator++()
|
||||
{
|
||||
m_current = m_next.begin();
|
||||
if (m_current != ranges::end(m_parent->m_base))
|
||||
{
|
||||
m_current = m_next.end();
|
||||
if (m_current == ranges::end(m_parent->m_base))
|
||||
{
|
||||
m_trailing_empty = true;
|
||||
m_next = { m_current, m_current };
|
||||
}
|
||||
else
|
||||
{
|
||||
m_next = m_parent->find_next(m_current);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_trailing_empty = false;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr iterator operator++(int)
|
||||
{
|
||||
auto tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(const iterator& x, const iterator& y)
|
||||
{
|
||||
return x.m_current == y.m_current && x.m_trailing_empty == y.m_trailing_empty;
|
||||
}
|
||||
friend constexpr bool operator!=(const iterator& y, const iterator& x)
|
||||
{
|
||||
return !operator==(x, y);
|
||||
}
|
||||
private:
|
||||
//! reference to parent split_view
|
||||
split_view<View, Pattern>* m_parent{};
|
||||
//! iterator of the current split content of the view
|
||||
iterator_t<View> m_current{};
|
||||
//! The next subrange within the view that matches the pattern
|
||||
subrange<iterator_t<View>> m_next{};
|
||||
//! Set to true to indicate an iterator
|
||||
//! pointing to the empty after the last pattern if the pattern is at the end of the view
|
||||
//! For a View="a,b,c," and Pattern=",", the last iterator will point to the empty element
|
||||
//! after the last comma. This iteator will not match the sentinel iterator to allow users
|
||||
//! to know between the end of the View and an empty element after the last pattern
|
||||
bool m_trailing_empty{};
|
||||
};
|
||||
|
||||
template<class View, class Pattern, class Enable>
|
||||
struct split_view<View, Pattern, Enable>::sentinel
|
||||
{
|
||||
sentinel() = default;
|
||||
explicit constexpr sentinel(split_view& parent)
|
||||
: m_end{ ranges::end(parent.m_base) }
|
||||
{}
|
||||
|
||||
friend constexpr bool operator==(const iterator& x, const sentinel& y)
|
||||
{
|
||||
return x.m_current == y.m_end && !x.m_trailing_empty;
|
||||
}
|
||||
friend constexpr bool operator==(const sentinel& y, const iterator& x)
|
||||
{
|
||||
return operator==(x, y);
|
||||
}
|
||||
friend constexpr bool operator!=(const iterator& x, const sentinel& y)
|
||||
{
|
||||
return !operator==(x, y);
|
||||
}
|
||||
friend constexpr bool operator!=(const sentinel& y, const iterator& x)
|
||||
{
|
||||
return !operator==(x, y);
|
||||
}
|
||||
private:
|
||||
sentinel_t<View> m_end;
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,290 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/ranges/ranges_adaptor.h>
|
||||
#include <AzCore/std/tuple.h>
|
||||
|
||||
// Specializing tuple in std:: namespace since tuple_size and tuple_element structs
|
||||
// are alias templates inside of the AZStd:: namespace
|
||||
namespace std
|
||||
{
|
||||
AZ_PUSH_DISABLE_WARNING(, "-Wmismatched-tags")
|
||||
template<class I, class S, AZStd::ranges::subrange_kind K>
|
||||
struct tuple_size<AZStd::ranges::subrange<I, S, K>>
|
||||
: integral_constant<size_t, 2> {};
|
||||
template<class I, class S, AZStd::ranges::subrange_kind K>
|
||||
struct tuple_element<0, AZStd::ranges::subrange<I, S, K>>
|
||||
{
|
||||
using type = I;
|
||||
};
|
||||
template<class I, class S, AZStd::ranges::subrange_kind K>
|
||||
struct tuple_element<1, AZStd::ranges::subrange<I, S, K>>
|
||||
{
|
||||
using type = S;
|
||||
};
|
||||
template<class I, class S, AZStd::ranges::subrange_kind K>
|
||||
struct tuple_element<0, const AZStd::ranges::subrange<I, S, K>>
|
||||
{
|
||||
using type = I;
|
||||
};
|
||||
template<class I, class S, AZStd::ranges::subrange_kind K>
|
||||
struct tuple_element<1, const AZStd::ranges::subrange<I, S, K>>
|
||||
{
|
||||
using type = S;
|
||||
};
|
||||
AZ_POP_DISABLE_WARNING
|
||||
}
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
template <typename From, typename To>
|
||||
/*concept*/ constexpr bool uses_nonqualification_pointer_conversion =
|
||||
is_pointer_v<From> && is_pointer_v<To> &&
|
||||
!convertible_to<remove_pointer_t<From>(*)[], remove_pointer_t<To>(*)[]>;
|
||||
|
||||
template<class From, class To>
|
||||
/*concept*/ constexpr bool convertible_to_non_slicing =
|
||||
convertible_to<From, To> &&
|
||||
!uses_nonqualification_pointer_conversion<decay_t<From>, decay_t<To>>;
|
||||
|
||||
template<class T, class = void>
|
||||
/*concept*/ constexpr bool pair_like = false;
|
||||
|
||||
template<class T>
|
||||
/*concept*/ constexpr bool pair_like<T, enable_if_t<conjunction_v<
|
||||
bool_constant<!is_reference_v<T>>,
|
||||
sfinae_trigger<typename tuple_size<T>::type>,
|
||||
bool_constant<derived_from<tuple_size<T>, integral_constant<size_t, 2>>>,
|
||||
sfinae_trigger<
|
||||
tuple_element_t<0, remove_const_t<T>>,
|
||||
tuple_element_t<1, remove_const_t<T>>>,
|
||||
bool_constant<convertible_to<decltype(AZStd::get<0>(declval<T>())), const tuple_element_t<0, T>&>>,
|
||||
bool_constant<convertible_to<decltype(AZStd::get<1>(declval<T>())), const tuple_element_t<1, T>&>>>
|
||||
>> = true;
|
||||
|
||||
template<class T, class U, class V, class = void>
|
||||
/*concept*/ constexpr bool pair_like_convertible_from = false;
|
||||
template<class T, class U, class V>
|
||||
/*concept*/ constexpr bool pair_like_convertible_from<T, U, V, enable_if_t<
|
||||
!range<T> && pair_like<T> && constructible_from<T, U, V>>> =
|
||||
convertible_to_non_slicing<U, tuple_element_t<0, T>> &&
|
||||
convertible_to<V, tuple_element_t<1, T>>;
|
||||
}
|
||||
|
||||
template<class I, class S, subrange_kind K>
|
||||
class subrange<I, S, K, enable_if_t<conjunction_v<
|
||||
bool_constant<input_or_output_iterator<I>>,
|
||||
bool_constant<sentinel_for<S, I>>,
|
||||
bool_constant<(K == subrange_kind::sized || !sized_sentinel_for<S, I>)>>
|
||||
>>
|
||||
: public view_interface<subrange<I, S, K>>
|
||||
{
|
||||
static constexpr bool StoreSize = K == subrange_kind::sized && !sized_sentinel_for<S, I>;
|
||||
|
||||
public:
|
||||
template<class I2 = I, class = enable_if_t<default_initializable<I2>>>
|
||||
constexpr subrange() {}
|
||||
|
||||
template<class I2, class = enable_if_t<Internal::convertible_to_non_slicing<I2, I> && !StoreSize>>
|
||||
constexpr subrange(I2 i, S s)
|
||||
: m_begin(AZStd::move(i))
|
||||
, m_end(s)
|
||||
{
|
||||
}
|
||||
template<class I2, class = enable_if_t<Internal::convertible_to_non_slicing<I2, I> && K == subrange_kind::sized>>
|
||||
constexpr subrange(I2 i, S s, make_unsigned_t<iter_difference_t<I>> n)
|
||||
: m_begin(AZStd::move(i))
|
||||
, m_end(s)
|
||||
{
|
||||
if constexpr (StoreSize)
|
||||
{
|
||||
m_size = n;
|
||||
}
|
||||
}
|
||||
|
||||
template<class R, class = enable_if_t<conjunction_v<
|
||||
bool_constant<Internal::different_from<R, subrange>>,
|
||||
bool_constant<borrowed_range<R>>,
|
||||
bool_constant<Internal::convertible_to_non_slicing<iterator_t<R>, I>>,
|
||||
bool_constant<convertible_to<sentinel_t<R>, S>>,
|
||||
bool_constant<(!StoreSize || sized_range<R>)>>
|
||||
>>
|
||||
constexpr subrange(R&& r)
|
||||
: m_begin{ ranges::begin(AZStd::forward<R>(r)) }
|
||||
, m_end{ ranges::end(AZStd::forward<R>(r)) }
|
||||
{
|
||||
if constexpr (StoreSize)
|
||||
{
|
||||
m_size = ranges::size(AZStd::forward<R>(r));
|
||||
}
|
||||
}
|
||||
|
||||
template<class R, class = enable_if_t<conjunction_v<
|
||||
bool_constant<borrowed_range<R>>,
|
||||
bool_constant<Internal::convertible_to_non_slicing<iterator_t<R>, I>>,
|
||||
bool_constant<convertible_to<sentinel_t<R>, S>>,
|
||||
bool_constant<(K == subrange_kind::sized)>>
|
||||
>>
|
||||
constexpr subrange(R&& r, make_unsigned_t<iter_difference_t<I>> n)
|
||||
: subrange{ ranges::begin(r), ranges::end(r), n }
|
||||
{
|
||||
}
|
||||
|
||||
template<class PairLike, class = enable_if_t<conjunction_v<
|
||||
bool_constant<Internal::different_from<PairLike, subrange>>,
|
||||
bool_constant<Internal::pair_like_convertible_from<PairLike, const I&, const S&>>>
|
||||
>>
|
||||
constexpr operator PairLike() const
|
||||
{
|
||||
return PairLike{ m_begin, m_end };
|
||||
}
|
||||
|
||||
template<bool Enable = copyable<I>, class = enable_if_t<Enable>>
|
||||
constexpr I begin() const
|
||||
{
|
||||
return m_begin;
|
||||
}
|
||||
template<bool Enable = !copyable<I>, class = enable_if_t<Enable>>
|
||||
[[nodiscard]] constexpr I begin()
|
||||
{
|
||||
return AZStd::move(m_begin);
|
||||
}
|
||||
constexpr S end() const
|
||||
{
|
||||
return m_end;
|
||||
}
|
||||
|
||||
constexpr bool empty() const
|
||||
{
|
||||
return m_begin == m_end;
|
||||
}
|
||||
template<bool Enable = (K == subrange_kind::sized), class = enable_if_t<Enable>>
|
||||
constexpr make_unsigned_t<iter_difference_t<I>> size() const
|
||||
{
|
||||
if constexpr (StoreSize)
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
using unsigned_difference_type = make_unsigned_t<iter_difference_t<I>>;
|
||||
return static_cast<unsigned_difference_type>(m_end - m_begin);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool Enable = forward_iterator<I>, class = enable_if_t<Enable>>
|
||||
[[nodiscard]] constexpr subrange next(iter_difference_t<I> n = 1) const&
|
||||
{
|
||||
auto tmp = *this;
|
||||
tmp.advance(n);
|
||||
return tmp;
|
||||
}
|
||||
[[nodiscard]] constexpr subrange next(iter_difference_t<I> n = 1)&&
|
||||
{
|
||||
advance(n);
|
||||
return AZStd::move(*this);
|
||||
}
|
||||
template<bool Enable = bidirectional_iterator<I>, class = enable_if_t<Enable>>
|
||||
[[nodiscard]] constexpr subrange prev(iter_difference_t<I> n = 1) const
|
||||
{
|
||||
auto tmp = *this;
|
||||
tmp.advance(-n);
|
||||
return tmp;
|
||||
}
|
||||
constexpr subrange& advance(iter_difference_t<I> n)
|
||||
{
|
||||
using unsigned_difference_type = AZStd::make_unsigned_t<iter_difference_t<I>>;
|
||||
if constexpr (bidirectional_iterator<I>)
|
||||
{
|
||||
if (n < 0)
|
||||
{
|
||||
ranges::advance(m_begin, n);
|
||||
if constexpr (StoreSize)
|
||||
{
|
||||
m_size += static_cast<unsigned_difference_type>(-n);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
auto actualAdvanceDist = n - ranges::advance(m_begin, n, m_end);
|
||||
if constexpr (StoreSize)
|
||||
{
|
||||
m_size -= static_cast<unsigned_difference_type>(actualAdvanceDist);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
I m_begin{};
|
||||
S m_end{};
|
||||
|
||||
// Size member will not count against the size of the class when using no_unique_address
|
||||
struct SizeNotStored
|
||||
{
|
||||
constexpr SizeNotStored() noexcept = default;
|
||||
};
|
||||
using size_type = AZStd::conditional_t<StoreSize, make_unsigned_t<iter_difference_t<I>>, SizeNotStored>;
|
||||
AZ_NO_UNIQUE_ADDRESS size_type m_size{};
|
||||
};
|
||||
|
||||
template<class I, class S, class = enable_if_t<input_or_output_iterator<I> && sentinel_for<S, I>>>
|
||||
subrange(I, S) -> subrange<I, S>;
|
||||
|
||||
template<class I, class S, class = enable_if_t<input_or_output_iterator<I>&& sentinel_for<S, I>>>
|
||||
subrange(I, S, make_unsigned_t<iter_difference_t<I>>) -> subrange<I, S, subrange_kind::sized>;
|
||||
|
||||
template<class R, class = enable_if_t<borrowed_range<R>> >
|
||||
subrange(R&&) -> subrange<iterator_t<R>, sentinel_t<R>,
|
||||
(sized_range<R> || sized_sentinel_for<sentinel_t<R>, iterator_t<R>>)
|
||||
? subrange_kind::sized : subrange_kind::unsized>;
|
||||
|
||||
template<class R, class = enable_if_t<borrowed_range<R>> >
|
||||
subrange(R&&, make_unsigned_t<range_difference_t<R>>) ->
|
||||
subrange<iterator_t<R>, sentinel_t<R>, subrange_kind::sized>;
|
||||
|
||||
template<size_t N, class I, class S, subrange_kind K, class = enable_if_t<((N == 0 && copyable<I>) || N == 1)>>
|
||||
constexpr auto get(const subrange<I, S, K>& r)
|
||||
{
|
||||
if constexpr (N == 0)
|
||||
{
|
||||
return r.begin();
|
||||
}
|
||||
else
|
||||
{
|
||||
return r.end();
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t N, class I, class S, subrange_kind K, class = enable_if_t<(N < 2)>>
|
||||
constexpr auto get(subrange<I, S, K>&& r)
|
||||
{
|
||||
if constexpr (N == 0)
|
||||
{
|
||||
return r.begin();
|
||||
}
|
||||
else
|
||||
{
|
||||
return r.end();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class I, class S, subrange_kind K>
|
||||
inline constexpr bool enable_borrowed_range<subrange<I, S, K>> = true;
|
||||
|
||||
}
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
using ranges::get;
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/base.h>
|
||||
|
||||
#include <AzCore/std/concepts/concepts_assignable.h>
|
||||
#include <AzCore/std/concepts/concepts_constructible.h>
|
||||
#include <AzCore/std/typetraits/conjunction.h>
|
||||
#include <AzCore/std/typetraits/disjunction.h>
|
||||
#include <AzCore/std/typetraits/extent.h>
|
||||
#include <AzCore/std/typetraits/integral_constant.h>
|
||||
#include <AzCore/std/typetraits/is_assignable.h>
|
||||
#include <AzCore/std/typetraits/is_array.h>
|
||||
#include <AzCore/std/typetraits/is_class.h>
|
||||
#include <AzCore/std/typetraits/is_enum.h>
|
||||
#include <AzCore/std/typetraits/is_void.h>
|
||||
#include <AzCore/std/typetraits/remove_cvref.h>
|
||||
#include <AzCore/std/typetraits/void_t.h>
|
||||
#include <AzCore/std/utility/move.h>
|
||||
#include <AzCore/std/utility/declval.h>
|
||||
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
// Bring std utility functions into AZStd namespace
|
||||
using std::forward;
|
||||
}
|
||||
|
||||
namespace AZStd::ranges::Internal
|
||||
{
|
||||
template <class T, class U, class = void>
|
||||
constexpr bool is_class_or_enum_with_swap_adl = false;
|
||||
template <class T, class U>
|
||||
constexpr bool is_class_or_enum_with_swap_adl<T, U, enable_if_t<conjunction_v<
|
||||
disjunction<
|
||||
disjunction<is_class<remove_cvref_t<T>>, is_enum<remove_cvref_t<T>>>,
|
||||
disjunction<is_class<remove_cvref_t<U>>, is_enum<remove_cvref_t<U>>>>,
|
||||
is_void<void_t<decltype(swap(declval<T&>(), declval<U&>()))>>
|
||||
>>> = true;
|
||||
|
||||
template <class T>
|
||||
void swap(T&, T&) = delete;
|
||||
|
||||
struct swap_fn
|
||||
{
|
||||
template <class T, class U>
|
||||
constexpr auto operator()(T&& t, U&& u) const noexcept(noexcept(swap(AZStd::forward<T>(t), AZStd::forward<U>(u))))
|
||||
->enable_if_t<is_class_or_enum_with_swap_adl<T, U>>
|
||||
{
|
||||
swap(AZStd::forward<T>(t), AZStd::forward<U>(u));
|
||||
}
|
||||
|
||||
// ranges::swap customization point https://eel.is/c++draft/concepts#concept.swappable-2.2
|
||||
// Implemented in ranges.h as to prevent circular dependency.
|
||||
// ranges::swap_ranges depends on the range concepts that can't be defined here
|
||||
template <class T, class U>
|
||||
constexpr auto operator()(T&& t, U&& u) const noexcept(noexcept((*this)(*t, *u)))
|
||||
->enable_if_t<conjunction_v<
|
||||
bool_constant<!is_class_or_enum_with_swap_adl<T, U>>,
|
||||
is_array<T>,
|
||||
is_array<U>,
|
||||
bool_constant<extent_v<T> == extent_v<U>>
|
||||
>>;
|
||||
|
||||
template <class T>
|
||||
constexpr auto operator()(T& t1, T& t2) const noexcept(noexcept(is_nothrow_move_constructible_v<T> && is_nothrow_move_assignable_v<T>))
|
||||
->enable_if_t<conjunction_v<bool_constant<move_constructible<T>>, bool_constant<assignable_from<T&, T>> >>
|
||||
{
|
||||
auto temp(AZStd::move(t1));
|
||||
t1 = AZStd::move(t2);
|
||||
t2 = AZStd::move(temp);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
inline constexpr auto swap = Internal::swap_fn{};
|
||||
}
|
||||
}
|
||||
|
||||
namespace AZStd::Internal
|
||||
{
|
||||
template <class T, class = void>
|
||||
constexpr bool swappable_impl = false;
|
||||
template <class T>
|
||||
constexpr bool swappable_impl<T, void_t<decltype(AZStd::ranges::swap(declval<T&>(), declval<T&>()))>> = true;
|
||||
|
||||
template <class T, class U, class = void>
|
||||
constexpr bool swappable_with_impl = false;
|
||||
template <class T, class U>
|
||||
constexpr bool swappable_with_impl<T, U, enable_if_t<conjunction_v<
|
||||
bool_constant<common_reference_with<T, U>>,
|
||||
is_void<void_t<
|
||||
decltype(AZStd::ranges::swap(declval<T&>(), declval<T&>())),
|
||||
decltype(AZStd::ranges::swap(declval<U&>(), declval<U&>())),
|
||||
decltype(AZStd::ranges::swap(declval<T&>(), declval<U&>())),
|
||||
decltype(AZStd::ranges::swap(declval<U&>(), declval<T&>()))>> >>> = true;
|
||||
}
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
template<class T>
|
||||
/*concept*/ constexpr bool swappable = Internal::swappable_impl<T>;
|
||||
|
||||
template<class T, class U>
|
||||
/*concept*/ constexpr bool swappable_with = Internal::swappable_with_impl<T, U>;
|
||||
}
|
||||
@ -0,0 +1,395 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/ranges/all_view.h>
|
||||
#include <AzCore/std/ranges/empty_view.h>
|
||||
#include <AzCore/std/ranges/ranges_algorithm.h>
|
||||
#include <AzCore/std/tuple.h>
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
template<class... Views>
|
||||
class zip_view;
|
||||
|
||||
template<class... Views>
|
||||
inline constexpr bool enable_borrowed_range<zip_view<Views...>> = (enable_borrowed_range<Views> && ...);
|
||||
|
||||
// views::zip customization point
|
||||
namespace views
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
struct zip_view_fn
|
||||
{
|
||||
template <class... Views>
|
||||
constexpr auto operator()(Views&&... views) const
|
||||
{
|
||||
if constexpr (sizeof...(Views) == 0)
|
||||
{
|
||||
return views::empty<tuple<>>;
|
||||
}
|
||||
else
|
||||
{
|
||||
return zip_view<views::all_t<decltype((declval<Views>()))>...>(AZStd::forward<Views>(views)...);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
inline namespace customization_point_object
|
||||
{
|
||||
constexpr Internal::zip_view_fn zip{};
|
||||
}
|
||||
}
|
||||
|
||||
namespace ZipViewInternal
|
||||
{
|
||||
template<class... Rs>
|
||||
/*concept*/ constexpr bool zip_is_common = (sizeof...(Rs) == 1 && (common_range<Rs> && ...)) ||
|
||||
(!(bidirectional_range<Rs> && ...) && (common_range<Rs> && ...)) ||
|
||||
((random_access_range<Rs> && ...) && (sized_range<Rs> && ...));
|
||||
|
||||
template<class... Ts>
|
||||
using tuple_or_pair = tuple<Ts...>;
|
||||
|
||||
template<class F, class Tuple>
|
||||
constexpr auto tuple_transform(F&& f, Tuple&& tuple)
|
||||
{
|
||||
auto TransformToResultTuple = [&](auto&&... elements)
|
||||
{
|
||||
return tuple_or_pair<invoke_result_t<F&, decltype(elements)>...>(
|
||||
AZStd::invoke(f, AZStd::forward<decltype(elements)>(elements))...);
|
||||
};
|
||||
return AZStd::apply(AZStd::move(TransformToResultTuple), AZStd::forward<Tuple>(tuple));
|
||||
}
|
||||
|
||||
template<class F, class Tuple>
|
||||
constexpr void tuple_for_each(F&& f, Tuple&& tuple)
|
||||
{
|
||||
auto InvokeTupleElement = [&](auto&&... elements)
|
||||
{
|
||||
(AZStd::invoke(f, AZStd::forward<decltype(elements)>(elements)), ...);
|
||||
};
|
||||
AZStd::apply(AZStd::move(InvokeTupleElement), AZStd::forward<Tuple>(tuple));
|
||||
}
|
||||
|
||||
template<class F, class Tuple1, class Tuple2, size_t... Indices>
|
||||
constexpr decltype(auto) tuple_zip(F&& f, AZStd::index_sequence<Indices...>,
|
||||
Tuple1&& tuple1, Tuple2&& tuple2)
|
||||
{
|
||||
(AZStd::invoke(AZStd::forward<F>(f), AZStd::get<Indices>(tuple1), AZStd::get<Indices>(tuple2)), ...);
|
||||
}
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
class zip_view
|
||||
: public enable_if_t<conjunction_v<
|
||||
bool_constant<(sizeof...(Views) > 0)>,
|
||||
bool_constant<input_range<Views>>...,
|
||||
bool_constant<view<Views>>...
|
||||
>, view_interface<zip_view<Views...>>>
|
||||
{
|
||||
// [range.zip.iterator], class template zip_view::iterator
|
||||
template<bool>
|
||||
class iterator;
|
||||
|
||||
// [range.zip.sentinel], class template zip_view::sentinel
|
||||
template<bool>
|
||||
class sentinel; // exposition only
|
||||
|
||||
public:
|
||||
zip_view() = default;
|
||||
constexpr explicit zip_view(Views... views);
|
||||
|
||||
template<bool Enable = !conjunction_v<bool_constant<Internal::simple_view<Views>>...>, class = enable_if_t<Enable>>
|
||||
constexpr auto begin();
|
||||
template<bool Enable = conjunction_v<bool_constant<range<const Views>>...>, class = enable_if_t<Enable>>
|
||||
constexpr auto begin() const;
|
||||
|
||||
template<bool Enable = !conjunction_v<bool_constant<Internal::simple_view<Views>>...>, class = enable_if_t<Enable>>
|
||||
constexpr auto end();
|
||||
template<bool Enable = conjunction_v<bool_constant<range<const Views>>...>, class = enable_if_t<Enable>>
|
||||
constexpr auto end() const;
|
||||
|
||||
template<bool Enable = conjunction_v<bool_constant<sized_range<Views>>...>, class = enable_if_t<Enable>>
|
||||
constexpr auto size();
|
||||
template<bool Enable = conjunction_v<bool_constant<sized_range<const Views>>...>, class = enable_if_t<Enable>>
|
||||
constexpr auto size() const;
|
||||
|
||||
private:
|
||||
tuple<Views...> m_views;
|
||||
};
|
||||
|
||||
template<class... Rs>
|
||||
zip_view(Rs&&...)->zip_view<views::all_t<Rs>...>;
|
||||
|
||||
namespace ZipViewInternal
|
||||
{
|
||||
template<bool Const, class... Views>
|
||||
/*concept*/ constexpr bool all_random_access = (random_access_range<::AZStd::ranges::Internal::maybe_const<Const, Views>>&&...);
|
||||
template<bool Const, class... Views>
|
||||
/*concept*/ constexpr bool all_bidirectional = (bidirectional_range<::AZStd::ranges::Internal::maybe_const<Const, Views>>&&...);
|
||||
template<bool Const, class... Views>
|
||||
/*concept*/ constexpr bool all_forward = (forward_range<::AZStd::ranges::Internal::maybe_const<Const, Views>>&&...);
|
||||
|
||||
struct requirements_fulfilled {};
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
class zip_view<Views...>::iterator
|
||||
: public enable_if_t<(conjunction_v<
|
||||
bool_constant<(sizeof...(Views) > 0)>,
|
||||
bool_constant<input_range<Views>>...,
|
||||
bool_constant<view<Views>>...>),
|
||||
ZipViewInternal::requirements_fulfilled>
|
||||
{
|
||||
friend class zip_view<Views...>;
|
||||
template<bool>
|
||||
friend class zip_view<Views...>::sentinel;
|
||||
constexpr explicit iterator(ZipViewInternal::tuple_or_pair<iterator_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>...>);
|
||||
public:
|
||||
using iterator_category = input_iterator_tag; // not always present
|
||||
using iterator_concept = conditional_t<ZipViewInternal::all_random_access<Const, Views...>, random_access_iterator_tag,
|
||||
conditional_t<ZipViewInternal::all_bidirectional<Const, Views...>, bidirectional_iterator_tag,
|
||||
conditional_t<ZipViewInternal::all_forward<Const, Views...>, forward_iterator_tag, input_iterator_tag>>>;
|
||||
using value_type = ZipViewInternal::tuple_or_pair<range_value_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>...>;
|
||||
using difference_type = common_type_t<range_difference_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>...>;
|
||||
|
||||
private:
|
||||
constexpr auto view_iterator_to_value_tuple(difference_type n) const;
|
||||
template<size_t... Indices>
|
||||
static constexpr auto any_iterator_equal(const iterator& x, const iterator& y, AZStd::index_sequence<Indices...>);
|
||||
template<size_t... Indices>
|
||||
static constexpr auto min_distance_in_views(const iterator& x, const iterator& y, AZStd::index_sequence<Indices...>);
|
||||
public:
|
||||
|
||||
iterator() = default;
|
||||
template<bool Enable = Const && conjunction_v<
|
||||
bool_constant<convertible_to<iterator_t<Views>, iterator_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>>>...>,
|
||||
class = enable_if_t<Enable>>
|
||||
constexpr iterator(iterator<!Const> other);
|
||||
|
||||
constexpr auto operator*() const;
|
||||
constexpr iterator& operator++();
|
||||
constexpr decltype(auto) operator++(int);
|
||||
|
||||
template<bool Enable = ZipViewInternal::all_bidirectional<Const, Views...>, class = enable_if_t<Enable>>
|
||||
constexpr auto operator--()->iterator&;
|
||||
template<bool Enable = ZipViewInternal::all_bidirectional<Const, Views...>, class = enable_if_t<Enable>>
|
||||
constexpr auto operator--(int)->iterator;
|
||||
|
||||
template<bool Enable = ZipViewInternal::all_random_access<Const, Views...>, class = enable_if_t<Enable>>
|
||||
constexpr auto operator+=(difference_type x)->iterator&;
|
||||
template<bool Enable = ZipViewInternal::all_random_access<Const, Views...>, class = enable_if_t<Enable>>
|
||||
constexpr auto operator-=(difference_type x)->iterator&;
|
||||
|
||||
template<bool Enable = ZipViewInternal::all_random_access<Const, Views...>, class = enable_if_t<Enable>>
|
||||
constexpr auto operator[](difference_type n) const;
|
||||
|
||||
// Out-of-line defintions with these friend functions are possible, but quite a mess signature wise
|
||||
// that is why the defintions are inline, instead of
|
||||
// template<bool Const, class... Views>
|
||||
// constexpr auto operator==(const typename zip_views<Views...>::template iterator<Const>& x,
|
||||
// const typename zip_views<Views...>::template iterator<Const>& y){}
|
||||
template<bool Enable = conjunction_v<bool_constant<equality_comparable<iterator_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>>>...>,
|
||||
class = enable_if_t<Enable>>
|
||||
friend constexpr auto operator==(const iterator& x, const iterator& y) -> bool
|
||||
{
|
||||
if constexpr (ZipViewInternal::all_bidirectional<Const, Views...>)
|
||||
{
|
||||
return x.m_current == y.m_current;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Returns true if it at least one of the iterator views are equal
|
||||
return any_iterator_equal(x, y, AZStd::index_sequence_for<Views...>{});
|
||||
}
|
||||
}
|
||||
|
||||
template<bool Enable = conjunction_v<bool_constant<equality_comparable<iterator_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>>>...>,
|
||||
class = enable_if_t<Enable>>
|
||||
friend constexpr auto operator!=(const iterator& x, const iterator& y) -> bool
|
||||
{
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
template<bool Enable = ZipViewInternal::all_random_access<Const, Views...>, class = enable_if_t<Enable>>
|
||||
friend constexpr auto operator<(const iterator& x, const iterator& y) -> bool
|
||||
{
|
||||
return x.m_current < y.m_current;
|
||||
}
|
||||
template<bool Enable = ZipViewInternal::all_random_access<Const, Views...>, class = enable_if_t<Enable>>
|
||||
friend constexpr auto operator>(const iterator& x, const iterator& y) -> bool
|
||||
{
|
||||
return y.m_current < x.m_current;
|
||||
}
|
||||
template<bool Enable = ZipViewInternal::all_random_access<Const, Views...>, class = enable_if_t<Enable>>
|
||||
friend constexpr auto operator<=(const iterator& x, const iterator& y) -> bool
|
||||
{
|
||||
return !(y.m_current < x.m_current);
|
||||
}
|
||||
template<bool Enable = ZipViewInternal::all_random_access<Const, Views...>, class = enable_if_t<Enable>>
|
||||
friend constexpr auto operator>=(const iterator& x, const iterator& y) -> bool
|
||||
{
|
||||
return !(x.m_current < y.m_current);
|
||||
}
|
||||
/* Requires C++20 compiler support
|
||||
friend constexpr auto operator<=>(const iterator& x, const iterator& y) requires ZipViewInternal::all_random_access<Const, Views...> &&
|
||||
(three_way_comparable<iterator_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>> && ...);
|
||||
*/
|
||||
|
||||
template<bool Enable = ZipViewInternal::all_random_access<Const, Views...>, class = enable_if_t<Enable>>
|
||||
friend constexpr auto operator+(const iterator& i, difference_type n) -> iterator
|
||||
{
|
||||
auto r = i;
|
||||
r += n;
|
||||
return r;
|
||||
}
|
||||
template<bool Enable = ZipViewInternal::all_random_access<Const, Views...>, class = enable_if_t<Enable>>
|
||||
friend constexpr auto operator+(difference_type n, const iterator& i) -> iterator
|
||||
{
|
||||
auto r = i;
|
||||
r += n;
|
||||
return r;
|
||||
}
|
||||
template<bool Enable = ZipViewInternal::all_random_access<Const, Views...>, class = enable_if_t<Enable>>
|
||||
friend constexpr auto operator-(const iterator& i, difference_type n) -> iterator
|
||||
{
|
||||
auto r = i;
|
||||
r -= n;
|
||||
return r;
|
||||
}
|
||||
template<bool Enable = conjunction_v<
|
||||
bool_constant<sized_sentinel_for<
|
||||
iterator_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>,
|
||||
iterator_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>>>...
|
||||
>, class = enable_if_t<Enable>>
|
||||
friend constexpr auto operator-(const iterator& x, const iterator& y) -> difference_type
|
||||
{
|
||||
return min_distance_in_views(x, y, AZStd::index_sequence_for<Views...>{});
|
||||
}
|
||||
|
||||
// customization of iter_move and iter_swap
|
||||
friend constexpr auto iter_move(
|
||||
iterator& i) noexcept(
|
||||
noexcept(ZipViewInternal::tuple_transform(ranges::iter_move, i.m_current)))
|
||||
{
|
||||
return ZipViewInternal::tuple_transform(ranges::iter_move, i.m_current);
|
||||
}
|
||||
|
||||
friend constexpr auto iter_swap(
|
||||
iterator& l,
|
||||
iterator& r) noexcept
|
||||
{
|
||||
static_assert(!conjunction_v<
|
||||
bool_constant<indirectly_swappable<iterator_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>>>...>);
|
||||
|
||||
ZipViewInternal::tuple_zip(ranges::iter_swap, l.m_current, r.m_current,
|
||||
AZStd::index_sequence_for<Views...>{});
|
||||
}
|
||||
|
||||
private:
|
||||
ZipViewInternal::tuple_or_pair<iterator_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>...> m_current;
|
||||
};
|
||||
|
||||
// sentinel type for iterator
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
class zip_view<Views...>::sentinel
|
||||
: public enable_if_t<conjunction_v<
|
||||
bool_constant<(sizeof...(Views) > 0)>,
|
||||
bool_constant<input_range<Views>>...,
|
||||
bool_constant<view<Views>>...
|
||||
>, ZipViewInternal::requirements_fulfilled>
|
||||
{
|
||||
friend class zip_view<Views...>;
|
||||
constexpr explicit sentinel(ZipViewInternal::tuple_or_pair<sentinel_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>...> end);
|
||||
|
||||
template<bool OtherConst, size_t... Indices>
|
||||
static constexpr auto min_distance_between_view_iterators(const iterator<OtherConst>& x,
|
||||
const sentinel& y, AZStd::index_sequence<Indices...>) ->
|
||||
common_type_t<range_difference_t<::AZStd::ranges::Internal::maybe_const<OtherConst, Views>>...>;
|
||||
|
||||
// On MSVC The friend functions are can only access the sentinel struct members
|
||||
// The iterator struct which is a friend of the sentinel struct is NOT a friend
|
||||
// of the friend functions
|
||||
// So a shim is added to provide access to the iterator m_current member
|
||||
template<bool OtherConst>
|
||||
static constexpr auto iterator_accessor(const iterator<OtherConst>& it);
|
||||
public:
|
||||
sentinel() = default;
|
||||
template<bool Enable = Const && conjunction_v<
|
||||
bool_constant<convertible_to<sentinel_t<Views>, sentinel_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>>>...>,
|
||||
class = enable_if_t<Enable>>
|
||||
constexpr sentinel(sentinel<!Const> i);
|
||||
|
||||
template<bool OtherConst, class = enable_if_t<conjunction_v<
|
||||
bool_constant<sentinel_for<
|
||||
sentinel_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>,
|
||||
iterator_t<::AZStd::ranges::Internal::maybe_const<OtherConst, Views>>>>...>
|
||||
>>
|
||||
friend constexpr auto operator==(const iterator<OtherConst>&x, const sentinel & y) -> bool
|
||||
{
|
||||
// Tracks if any iterator of the Views is equal to a sentinel of the views
|
||||
bool anyIteratorEqual = false;
|
||||
auto CompareIterator = [&anyIteratorEqual](auto&& lhs, auto&& rhs)
|
||||
{
|
||||
anyIteratorEqual = anyIteratorEqual || AZStd::forward<decltype(lhs)>(lhs) == AZStd::forward<decltype(rhs)>(rhs);
|
||||
};
|
||||
ZipViewInternal::tuple_zip(AZStd::move(CompareIterator), AZStd::index_sequence_for<Views...>{},
|
||||
iterator_accessor(x), y.m_end);
|
||||
|
||||
return anyIteratorEqual;
|
||||
}
|
||||
|
||||
template<bool OtherConst>
|
||||
friend constexpr auto operator==(const sentinel & y, const iterator<OtherConst>&x) -> bool
|
||||
{
|
||||
return x == y;
|
||||
}
|
||||
template<bool OtherConst>
|
||||
friend constexpr auto operator!=(const iterator<OtherConst>&x, const sentinel & y) -> bool
|
||||
{
|
||||
return !(x == y);
|
||||
}
|
||||
template<bool OtherConst>
|
||||
friend constexpr auto operator!=(const sentinel & y, const iterator<OtherConst>&x) -> bool
|
||||
{
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
template<bool OtherConst, class = enable_if_t<conjunction_v<
|
||||
bool_constant<sentinel_for<
|
||||
sentinel_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>,
|
||||
iterator_t<::AZStd::ranges::Internal::maybe_const<OtherConst, Views>>>>...>
|
||||
>>
|
||||
friend constexpr auto operator-(const iterator<OtherConst>&x, const sentinel & y) ->
|
||||
common_type_t<range_difference_t<::AZStd::ranges::Internal::maybe_const<OtherConst, Views>>...>
|
||||
{
|
||||
return min_distance_between_view_iterators(x, y, AZStd::index_sequence_for<Views...>{});
|
||||
}
|
||||
|
||||
template<bool OtherConst, class = enable_if_t<conjunction_v<
|
||||
bool_constant<sentinel_for<
|
||||
sentinel_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>,
|
||||
iterator_t<::AZStd::ranges::Internal::maybe_const<OtherConst, Views>>>>...>
|
||||
>>
|
||||
friend constexpr auto operator-(const sentinel & y, const iterator<OtherConst>&x) ->
|
||||
common_type_t<range_difference_t<::AZStd::ranges::Internal::maybe_const<OtherConst, Views>>...>
|
||||
{
|
||||
return -(x - y);
|
||||
}
|
||||
|
||||
private:
|
||||
ZipViewInternal::tuple_or_pair<sentinel_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>...> m_end;
|
||||
};
|
||||
} // namespace AZStd::ranges
|
||||
|
||||
#include <AzCore/std/ranges/zip_view.inl>
|
||||
@ -0,0 +1,308 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/ranges/all_view.h>
|
||||
#include <AzCore/std/ranges/ranges_algorithm.h>
|
||||
#include <AzCore/std/tuple.h>
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
// public zip_view functions
|
||||
template<class... Views>
|
||||
constexpr zip_view<Views...>::zip_view(Views... views)
|
||||
: m_views{ AZStd::move(views)... }
|
||||
{
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool, class>
|
||||
constexpr auto zip_view<Views...>::begin()
|
||||
{
|
||||
return iterator<false>(ZipViewInternal::tuple_transform(ranges::begin, m_views));
|
||||
}
|
||||
template<class... Views>
|
||||
template<bool, class>
|
||||
constexpr auto zip_view<Views...>::begin() const
|
||||
{
|
||||
return iterator<true>(ZipViewInternal::tuple_transform(ranges::begin, m_views));
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool, class>
|
||||
constexpr auto zip_view<Views...>::end()
|
||||
{
|
||||
if constexpr (!ZipViewInternal::zip_is_common<Views...>)
|
||||
{
|
||||
return sentinel<false>(ZipViewInternal::tuple_transform(ranges::end, m_views));
|
||||
}
|
||||
else if constexpr ((random_access_range<Views> && ...))
|
||||
{
|
||||
return begin() + iter_difference_t<iterator<false>>(size());
|
||||
}
|
||||
else
|
||||
{
|
||||
return iterator<false>(ZipViewInternal::tuple_transform(ranges::end, m_views));
|
||||
}
|
||||
}
|
||||
template<class... Views>
|
||||
template<bool, class>
|
||||
constexpr auto zip_view<Views...>::end() const
|
||||
{
|
||||
if constexpr (!ZipViewInternal::zip_is_common<const Views...>)
|
||||
{
|
||||
return sentinel<true>(ZipViewInternal::tuple_transform(ranges::end, m_views));
|
||||
}
|
||||
else if constexpr ((random_access_range<const Views> && ...))
|
||||
{
|
||||
return begin() + iter_difference_t<iterator<true>>(size());
|
||||
}
|
||||
else
|
||||
{
|
||||
return iterator<true>(ZipViewInternal::tuple_transform(ranges::end, m_views));
|
||||
}
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool, class>
|
||||
constexpr auto zip_view<Views...>::size()
|
||||
{
|
||||
auto GetSizeForViews = [](auto... sizes)
|
||||
{
|
||||
using CommonType = make_unsigned_t<common_type_t<decltype(sizes)...>>;
|
||||
return ranges::min({ CommonType(sizes)... });
|
||||
};
|
||||
return AZStd::apply(AZStd::move(GetSizeForViews), ZipViewInternal::tuple_transform(ranges::size, m_views));
|
||||
}
|
||||
template<class... Views>
|
||||
template<bool, class>
|
||||
constexpr auto zip_view<Views...>::size() const
|
||||
{
|
||||
auto GetSizeForViews = [](auto... sizes)
|
||||
{
|
||||
using CommonType = make_unsigned_t<common_type_t<decltype(sizes)...>>;
|
||||
return ranges::min({ CommonType(sizes)... });
|
||||
};
|
||||
return AZStd::apply(AZStd::move(GetSizeForViews), ZipViewInternal::tuple_transform(ranges::size, m_views));
|
||||
}
|
||||
|
||||
|
||||
// public zip_view::iterator functions
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
template<bool, class>
|
||||
constexpr zip_view<Views...>::iterator<Const>::iterator(iterator<!Const> other)
|
||||
: m_current(AZStd::move(other.m_current))
|
||||
{
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
constexpr auto zip_view<Views...>::iterator<Const>::operator*() const
|
||||
{
|
||||
auto TransformToReference = [](auto& i) -> decltype(auto)
|
||||
{
|
||||
return *i;
|
||||
};
|
||||
return ZipViewInternal::tuple_transform(AZStd::move(TransformToReference), m_current);
|
||||
}
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
constexpr auto zip_view<Views...>::iterator<Const>::operator++() -> iterator&
|
||||
{
|
||||
auto PreIncrementIterator = [](auto& i)
|
||||
{
|
||||
++i;
|
||||
};
|
||||
ZipViewInternal::tuple_for_each(AZStd::move(PreIncrementIterator), m_current);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
constexpr decltype(auto) zip_view<Views...>::iterator<Const>::operator++(int)
|
||||
{
|
||||
if constexpr (ZipViewInternal::all_forward<Const, Views...>)
|
||||
{
|
||||
auto tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
template<bool, class>
|
||||
constexpr auto zip_view<Views...>::iterator<Const>::operator--() -> iterator&
|
||||
{
|
||||
auto PreDecrementIterator = [](auto& i)
|
||||
{
|
||||
--i;
|
||||
};
|
||||
ZipViewInternal::tuple_for_each(AZStd::move(PreDecrementIterator), m_current);
|
||||
return *this;
|
||||
}
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
template<bool, class>
|
||||
constexpr auto zip_view<Views...>::iterator<Const>::operator--(int) -> iterator
|
||||
{
|
||||
auto tmp = *this;
|
||||
--* this;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
template<bool, class>
|
||||
constexpr auto zip_view<Views...>::iterator<Const>::operator+=(difference_type x) -> iterator&
|
||||
{
|
||||
auto AddIterator = [&](auto& i)
|
||||
{
|
||||
i += iter_difference_t<decltype(i)>(x);
|
||||
};
|
||||
ZipViewInternal::tuple_for_each(AZStd::move(AddIterator), m_current);
|
||||
return *this;
|
||||
}
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
template<bool, class>
|
||||
constexpr auto zip_view<Views...>::iterator<Const>::operator-=(difference_type x) -> iterator&
|
||||
{
|
||||
auto AddIterator = [&](auto& i)
|
||||
{
|
||||
i -= iter_difference_t<decltype(i)>(x);
|
||||
};
|
||||
ZipViewInternal::tuple_for_each(AZStd::move(AddIterator), m_current);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
template<bool, class>
|
||||
constexpr auto zip_view<Views...>::iterator<Const>::operator[](difference_type n) const
|
||||
{
|
||||
return view_iterator_to_value_tuple(n);
|
||||
}
|
||||
|
||||
// private zip_view::iterator functions
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
constexpr zip_view<Views...>::iterator<Const>::iterator(
|
||||
ZipViewInternal::tuple_or_pair<iterator_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>...> current)
|
||||
: m_current(AZStd::move(current))
|
||||
{
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
constexpr auto zip_view<Views...>::iterator<Const>::view_iterator_to_value_tuple(difference_type n) const
|
||||
{
|
||||
auto TransformToValue = [&](auto& i) -> decltype(auto)
|
||||
{
|
||||
using I = decltype(i);
|
||||
return i[iter_difference_t<I>(n)];
|
||||
};
|
||||
return ZipViewInternal::tuple_transform(AZStd::move(TransformToValue), m_current);
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
template<size_t... Indices>
|
||||
constexpr auto zip_view<Views...>::iterator<Const>::any_iterator_equal(const iterator& x, const iterator& y,
|
||||
AZStd::index_sequence<Indices...>)
|
||||
{
|
||||
return (... || (AZStd::get<Indices>(x.m_current) == AZStd::get<Indices>(y.m_current)));
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
template<size_t... Indices>
|
||||
constexpr auto zip_view<Views...>::iterator<Const>::min_distance_in_views(const iterator& x, const iterator& y,
|
||||
AZStd::index_sequence<Indices...>)
|
||||
{
|
||||
AZStd::array iterDistances{
|
||||
((AZStd::get<Indices>(x.m_current) - AZStd::get<Indices>(y.m_current)), ...) };
|
||||
if (iterDistances.empty())
|
||||
{
|
||||
return difference_type{};
|
||||
}
|
||||
|
||||
auto first = iterDistances.begin();
|
||||
difference_type minDistance = *first++;
|
||||
auto last = iterDistances.end();
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
difference_type absMinDistance = minDistance < 0 ? -minDistance : minDistance;
|
||||
difference_type absDistance = *first < 0 ? -*first : *first;
|
||||
minDistance = absDistance < absMinDistance ? *first : minDistance;
|
||||
}
|
||||
|
||||
return minDistance;
|
||||
}
|
||||
|
||||
// private zip_view::sentinel functions
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
constexpr zip_view<Views...>::sentinel<Const>::sentinel(
|
||||
ZipViewInternal::tuple_or_pair<sentinel_t<::AZStd::ranges::Internal::maybe_const<Const, Views>>...> end)
|
||||
: m_end(end)
|
||||
{
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
template<bool, class>
|
||||
constexpr zip_view<Views...>::sentinel<Const>::sentinel(sentinel<!Const> other)
|
||||
: m_end(AZStd::move(other.m_end))
|
||||
{
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
template<bool OtherConst, size_t... Indices>
|
||||
constexpr auto zip_view<Views...>::sentinel<Const>::min_distance_between_view_iterators(
|
||||
const typename zip_view<Views...>::template iterator<OtherConst>& x,
|
||||
const typename zip_view<Views...>::template sentinel<Const>& y,
|
||||
AZStd::index_sequence<Indices...>) ->
|
||||
common_type_t<range_difference_t<::AZStd::ranges::Internal::maybe_const<OtherConst, Views>>...>
|
||||
{
|
||||
using difference_type = common_type_t<range_difference_t<::AZStd::ranges::Internal::maybe_const<OtherConst, Views>>...>;
|
||||
// Tracks if any iterator of the Views is equal to a sentinel of the views
|
||||
AZStd::array iterDistances{
|
||||
((AZStd::get<Indices>(x.m_current) - AZStd::get<Indices>(y.m_end)), ...) };
|
||||
if (iterDistances.empty())
|
||||
{
|
||||
return difference_type{};
|
||||
}
|
||||
|
||||
auto first = iterDistances.begin();
|
||||
difference_type minDistance = *first++;
|
||||
auto last = iterDistances.end();
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
difference_type absMinDistance = minDistance < 0 ? -minDistance : minDistance;
|
||||
difference_type absDistance = *first < 0 ? -*first : *first;
|
||||
minDistance = absDistance < absMinDistance ? *first : minDistance;
|
||||
}
|
||||
|
||||
return minDistance;
|
||||
}
|
||||
|
||||
template<class... Views>
|
||||
template<bool Const>
|
||||
template<bool OtherConst>
|
||||
constexpr auto zip_view<Views...>::sentinel<Const>::iterator_accessor(const iterator<OtherConst>& it)
|
||||
{
|
||||
return it.m_current;
|
||||
}
|
||||
} // namespace AZStd::ranges
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue