Merge branch 'development' into math_string_converters

This commit is contained in:
puvvadar
2022-02-14 10:42:48 -08:00
committed by GitHub
373 changed files with 15071 additions and 5781 deletions
@@ -162,11 +162,11 @@ def select_model_config(configname):
azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SelectModelPresetByName", configname)
def destroy_main_window():
def exit():
"""
Closes the Material Editor window
Closes the Material Editor
"""
azlmbr.atomtools.AtomToolsMainWindowFactoryRequestBus(azlmbr.bus.Broadcast, "DestroyMainWindow")
azlmbr.atomtools.general.exit()
def wait_for_condition(function, timeout_in_seconds=1.0):
@@ -214,7 +214,7 @@ def run():
(not material_editor.is_open(document1_id)) and
(not material_editor.is_open(document2_id)) and
(not material_editor.is_open(document3_id)), 2.0)
material_editor.destroy_main_window()
material_editor.exit()
if __name__ == "__main__":
@@ -61,6 +61,7 @@ class TestAutomation(TestAutomationBase):
from . import Graph_HappyPath_ZoomInZoomOut as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.xfail(reason="Test fails on nightly build builds, it needs to be fixed.")
def test_NodePalette_HappyPath_CanSelectNode(self, request, workspace, editor, launcher_platform):
from . import NodePalette_HappyPath_CanSelectNode as test_module
self._run_test(request, workspace, editor, test_module)
@@ -113,6 +114,7 @@ class TestAutomation(TestAutomationBase):
from . import Debugger_HappyPath_TargetMultipleGraphs as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.xfail(reason="Test fails on nightly build builds, it needs to be fixed.")
@pytest.mark.parametrize("level", ["tmp_level"])
def test_Debugger_HappyPath_TargetMultipleEntities(self, request, workspace, editor, launcher_platform, project, level):
def teardown():
@@ -174,6 +176,7 @@ class TestAutomation(TestAutomationBase):
from . import ScriptEvents_ReturnSetType_Successfully as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.xfail(reason="Test fails on nightly build builds, it needs to be fixed.")
def test_NodeCategory_ExpandOnClick(self, request, workspace, editor, launcher_platform):
from . import NodeCategory_ExpandOnClick as test_module
self._run_test(request, workspace, editor, test_module)
@@ -187,6 +190,7 @@ class TestAutomation(TestAutomationBase):
from . import VariableManager_UnpinVariableType_Works as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.xfail(reason="Test fails on nightly build builds, it needs to be fixed.")
def test_Node_HappyPath_DuplicateNode(self, request, workspace, editor, launcher_platform):
from . import Node_HappyPath_DuplicateNode as test_module
self._run_test(request, workspace, editor, test_module)
@@ -263,6 +267,7 @@ class TestScriptCanvasTests(object):
timeout=60,
)
@pytest.mark.xfail(reason="Test fails on nightly build builds, it needs to be fixed.")
def test_VariableManager_Default_CreateDeleteVars(self, request, editor, launcher_platform):
var_types = ["Boolean", "Color", "EntityID", "Number", "String", "Transform", "Vector2", "Vector3", "Vector4"]
expected_lines = [f"Success: {var_type} variable is created" for var_type in var_types]
@@ -38,7 +38,10 @@
},
"Component_[7874177159288365422]": {
"$type": "EditorEntitySortComponent",
"Id": 7874177159288365422
"Id": 7874177159288365422,
"Child Entity Order": [
"Entity_[471076350497]"
]
},
"Component_[8018146290632383969]": {
"$type": "EditorEntityIconComponent",
@@ -110,34 +113,14 @@
"Component_[16871442125196328877]": {
"$type": "EditorEntitySortComponent",
"Id": 16871442125196328877,
"ChildEntityOrderEntryArray": [
{
"EntityId": "Entity_[604220336673]"
},
{
"EntityId": "Entity_[599925369377]",
"SortIndex": 1
},
{
"EntityId": "Entity_[475371317793]",
"SortIndex": 2
},
{
"EntityId": "Entity_[509731056161]",
"SortIndex": 3
},
{
"EntityId": "Entity_[505436088865]",
"SortIndex": 4
},
{
"EntityId": "Entity_[539795827233]",
"SortIndex": 5
},
{
"EntityId": "Entity_[569860598305]",
"SortIndex": 6
}
"Child Entity Order": [
"Entity_[604220336673]",
"Entity_[599925369377]",
"Entity_[475371317793]",
"Entity_[509731056161]",
"Entity_[505436088865]",
"Entity_[539795827233]",
"Entity_[569860598305]"
]
},
"Component_[18389136819207633744]": {
@@ -263,10 +246,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -370,10 +353,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -477,10 +460,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -584,10 +567,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -691,10 +674,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -798,10 +781,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -905,10 +888,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -944,30 +927,13 @@
"Component_[11056805018150955063]": {
"$type": "EditorEntitySortComponent",
"Id": 11056805018150955063,
"ChildEntityOrderEntryArray": [
{
"EntityId": "Entity_[488256219681]"
},
{
"EntityId": "Entity_[483961252385]",
"SortIndex": 1
},
{
"EntityId": "Entity_[479666285089]",
"SortIndex": 2
},
{
"EntityId": "Entity_[492551186977]",
"SortIndex": 3
},
{
"EntityId": "Entity_[496846154273]",
"SortIndex": 4
},
{
"EntityId": "Entity_[501141121569]",
"SortIndex": 5
}
"Child Entity Order": [
"Entity_[488256219681]",
"Entity_[483961252385]",
"Entity_[479666285089]",
"Entity_[492551186977]",
"Entity_[496846154273]",
"Entity_[501141121569]"
]
},
"Component_[11466054095979053511]": {
@@ -1028,30 +994,13 @@
"Component_[11056805018150955063]": {
"$type": "EditorEntitySortComponent",
"Id": 11056805018150955063,
"ChildEntityOrderEntryArray": [
{
"EntityId": "Entity_[522615958049]"
},
{
"EntityId": "Entity_[518320990753]",
"SortIndex": 1
},
{
"EntityId": "Entity_[514026023457]",
"SortIndex": 2
},
{
"EntityId": "Entity_[526910925345]",
"SortIndex": 3
},
{
"EntityId": "Entity_[531205892641]",
"SortIndex": 4
},
{
"EntityId": "Entity_[535500859937]",
"SortIndex": 5
}
"Child Entity Order": [
"Entity_[522615958049]",
"Entity_[518320990753]",
"Entity_[514026023457]",
"Entity_[526910925345]",
"Entity_[531205892641]",
"Entity_[535500859937]"
]
},
"Component_[11466054095979053511]": {
@@ -1180,10 +1129,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -1287,10 +1236,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -1394,10 +1343,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -1501,10 +1450,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -1608,10 +1557,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -1715,10 +1664,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -1754,30 +1703,13 @@
"Component_[11056805018150955063]": {
"$type": "EditorEntitySortComponent",
"Id": 11056805018150955063,
"ChildEntityOrderEntryArray": [
{
"EntityId": "Entity_[552680729121]"
},
{
"EntityId": "Entity_[548385761825]",
"SortIndex": 1
},
{
"EntityId": "Entity_[544090794529]",
"SortIndex": 2
},
{
"EntityId": "Entity_[556975696417]",
"SortIndex": 3
},
{
"EntityId": "Entity_[561270663713]",
"SortIndex": 4
},
{
"EntityId": "Entity_[565565631009]",
"SortIndex": 5
}
"Child Entity Order": [
"Entity_[552680729121]",
"Entity_[548385761825]",
"Entity_[544090794529]",
"Entity_[556975696417]",
"Entity_[561270663713]",
"Entity_[565565631009]"
]
},
"Component_[11466054095979053511]": {
@@ -1906,10 +1838,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -2013,10 +1945,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -2120,10 +2052,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -2227,10 +2159,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -2334,10 +2266,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -2441,10 +2373,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -2480,30 +2412,13 @@
"Component_[11056805018150955063]": {
"$type": "EditorEntitySortComponent",
"Id": 11056805018150955063,
"ChildEntityOrderEntryArray": [
{
"EntityId": "Entity_[582745500193]"
},
{
"EntityId": "Entity_[578450532897]",
"SortIndex": 1
},
{
"EntityId": "Entity_[574155565601]",
"SortIndex": 2
},
{
"EntityId": "Entity_[587040467489]",
"SortIndex": 3
},
{
"EntityId": "Entity_[591335434785]",
"SortIndex": 4
},
{
"EntityId": "Entity_[595630402081]",
"SortIndex": 5
}
"Child Entity Order": [
"Entity_[582745500193]",
"Entity_[578450532897]",
"Entity_[574155565601]",
"Entity_[587040467489]",
"Entity_[591335434785]",
"Entity_[595630402081]"
]
},
"Component_[11466054095979053511]": {
@@ -2632,10 +2547,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -2739,10 +2654,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -2846,10 +2761,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -2953,10 +2868,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -3060,10 +2975,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
@@ -3167,10 +3082,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D0F73AAF-52B7-507C-B045-DBE2FE2D4403}",
"subId": 268677693
"guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
"subId": 281415304
},
"assetHint": "objects/shaderball_simple/shaberball_simple_1m.azmodel"
"assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
},
"LodOverride": 255
}
+4 -1
View File
@@ -709,7 +709,10 @@ namespace AZ::IO
constexpr reference operator*() const;
constexpr pointer operator->() const;
constexpr pointer operator->() const
{
return &m_stashed_elem;
}
constexpr PathIterator& operator++();
+10 -6
View File
@@ -1397,12 +1397,6 @@ namespace AZ::IO
return m_stashed_elem;
}
template <typename PathType>
constexpr auto PathIterator<PathType>::operator->() const -> pointer
{
return &m_stashed_elem;
}
template <typename PathType>
constexpr auto PathIterator<PathType>::operator++() -> PathIterator&
{
@@ -1542,3 +1536,13 @@ namespace AZ::IO
extern template bool operator!=<const FixedMaxPath>(const PathIterator<const FixedMaxPath>& lhs,
const PathIterator<const FixedMaxPath>& rhs);
}
namespace AZStd::ranges
{
// A PathView is a borrowed range, it does not own the content of the Path it is viewing
template<>
inline constexpr bool enable_borrowed_range<AZ::IO::PathView> = true;
template<>
inline constexpr bool enable_view<AZ::IO::PathView> = true;
}
@@ -13,6 +13,7 @@
#include <AzCore/Memory/OSAllocator.h> // required by certain platforms
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/parallel/lock.h>
#include <AzCore/std/containers/intrusive_list.h>
#include <AzCore/std/containers/intrusive_set.h>
#ifdef _DEBUG
@@ -56,212 +57,6 @@ namespace AZ {
// Enabled mutex per bucket
#define USE_MUTEX_PER_BUCKET
//////////////////////////////////////////////////////////////////////////
// TODO: Replace with AZStd::intrusive_list
class intrusive_list_base
{
public:
class node_base
{
node_base* mPrev;
node_base* mNext;
public:
node_base* next() const {return mNext; }
node_base* prev() const {return mPrev; }
void reset()
{
mPrev = this;
mNext = this;
}
void unlink()
{
mNext->mPrev = mPrev;
mPrev->mNext = mNext;
}
void link(node_base* node)
{
mPrev = node->mPrev;
mNext = node;
node->mPrev = this;
mPrev->mNext = this;
}
};
intrusive_list_base()
{
mHead.reset();
}
intrusive_list_base(const intrusive_list_base&)
{
mHead.reset();
}
bool empty() const {return mHead.next() == &mHead; }
void swap(intrusive_list_base& other)
{
node_base* node = &other.mHead;
if (!empty())
{
node = mHead.next();
mHead.unlink();
mHead.reset();
}
node_base* other_node = &mHead;
if (!other.empty())
{
other_node = other.mHead.next();
other.mHead.unlink();
other.mHead.reset();
}
mHead.link(other_node);
other.mHead.link(node);
}
protected:
node_base mHead;
};
//////////////////////////////////////////////////////////////////////////
// TODO: Replace with AZStd::intrusive_list
template<class T>
class intrusive_list
: public intrusive_list_base
{
intrusive_list(const intrusive_list& rhs);
intrusive_list& operator=(const intrusive_list& rhs);
public:
class node
: public node_base
{
public:
T* next() const {return static_cast<T*>(node_base::next()); }
T* prev() const {return static_cast<T*>(node_base::prev()); }
const T& data() const {return *static_cast<const T*>(this); }
T& data() {return *static_cast<T*>(this); }
};
class const_iterator;
class iterator
{
using reference = T&;
using pointer = T*;
friend class const_iterator;
T* mPtr;
public:
iterator()
: mPtr(0) {}
explicit iterator(T* ptr)
: mPtr(ptr) {}
reference operator*() const {return mPtr->data(); }
pointer operator->() const {return &mPtr->data(); }
operator pointer() const {
return &mPtr->data();
}
iterator& operator++()
{
mPtr = mPtr->next();
return *this;
}
iterator& operator--()
{
mPtr = mPtr->prev();
return *this;
}
bool operator==(const iterator& rhs) const {return mPtr == rhs.mPtr; }
bool operator!=(const iterator& rhs) const {return mPtr != rhs.mPtr; }
T* ptr() const {return mPtr; }
};
class const_iterator
{
using reference = const T &;
using pointer = const T *;
const T* mPtr;
public:
const_iterator()
: mPtr(0) {}
explicit const_iterator(const T* ptr)
: mPtr(ptr) {}
const_iterator(const iterator& it)
: mPtr(it.mPtr) {}
reference operator*() const {return mPtr->data(); }
pointer operator->() const {return &mPtr->data(); }
operator pointer() const {
return &mPtr->data();
}
const_iterator& operator++()
{
mPtr = mPtr->next();
return *this;
}
const_iterator& operator--()
{
mPtr = mPtr->prev();
return *this;
}
bool operator==(const const_iterator& rhs) const {return mPtr == rhs.mPtr; }
bool operator!=(const const_iterator& rhs) const {return mPtr != rhs.mPtr; }
const T* ptr() const {return mPtr; }
};
intrusive_list()
: intrusive_list_base() {}
~intrusive_list() {clear(); }
const_iterator begin() const {return const_iterator((const T*)mHead.next()); }
iterator begin() {return iterator((T*)mHead.next()); }
const_iterator end() const {return const_iterator((const T*)&mHead); }
iterator end() {return iterator((T*)&mHead); }
const T& front() const
{
HPPA_ASSERT(!empty());
return *begin();
}
T& front()
{
HPPA_ASSERT(!empty());
return *begin();
}
const T& back() const
{
HPPA_ASSERT(!empty());
return *(--end());
}
T& back()
{
HPPA_ASSERT(!empty());
return *(--end());
}
void push_front(T* v) {insert(this->begin(), v); }
void pop_front() {erase(this->begin()); }
void push_back(T* v) {insert(this->end(), v); }
void pop_back() {erase(--(this->end())); }
iterator insert(iterator where, T* node)
{
T* newLink = node;
newLink->link(where.ptr());
return iterator(newLink);
}
iterator erase(iterator where)
{
T* node = where.ptr();
++where;
node->unlink();
return where;
}
void erase(T* node)
{
node->unlink();
}
void clear()
{
while (!this->empty())
{
this->pop_back();
}
}
};
//////////////////////////////////////////////////////////////////////////
class HpAllocator
{
@@ -376,7 +171,7 @@ namespace AZ {
};
struct page
: public block_header_proxy /* must be first */
, public intrusive_list<page>::node
, public AZStd::list_base_hook<page>::node_type
{
page(size_t elemSize, size_t pageSize, size_t marker)
: mBucketIndex((unsigned short)bucket_spacing_function_aligned(elemSize))
@@ -415,7 +210,7 @@ namespace AZ {
void dec_ref() { HPPA_ASSERT(mUseCount > 0); mUseCount--; }
bool check_marker(size_t marker) const { return mMarker == (marker ^ ((size_t)this)); }
};
using page_list = intrusive_list<page>;
using page_list = AZStd::intrusive_list<page, AZStd::list_base_hook<page>>;
class bucket
{
page_list mPageList;
@@ -442,16 +237,17 @@ namespace AZ {
#endif
#endif
size_t marker() const {return mMarker; }
const page* page_list_begin() const {return mPageList.begin(); }
page* page_list_begin() {return mPageList.begin(); }
const page* page_list_end() const {return mPageList.end(); }
page* page_list_end() {return mPageList.end(); }
auto page_list_begin() const {return mPageList.begin(); }
auto page_list_begin() {return mPageList.begin(); }
auto page_list_end() const {return mPageList.end(); }
auto page_list_end() {return mPageList.end(); }
bool page_list_empty() const {return mPageList.empty(); }
void add_free_page(page* p) {mPageList.push_front(p); }
void add_free_page(page* p) {mPageList.push_front(*p); }
page* get_free_page();
const page* get_free_page() const;
void* alloc(page* p);
void free(page* p, void* ptr);
void unlink(page* p);
};
void* bucket_system_alloc();
void bucket_system_free(void* ptr);
@@ -1278,8 +1074,8 @@ namespace AZ {
if (!next)
{
// if full, auto sort to back
p->unlink();
mPageList.push_back(p);
mPageList.erase(*p);
mPageList.push_back(*p);
}
return (void*)free;
}
@@ -1295,11 +1091,16 @@ namespace AZ {
if (!free)
{
// if the page was previously full, auto sort to front
p->unlink();
mPageList.push_front(p);
mPageList.erase(*p);
mPageList.push_front(*p);
}
}
void HpAllocator::bucket::unlink(page* p)
{
mPageList.erase(*p);
}
void* HpAllocator::bucket_system_alloc()
{
void* ptr;
@@ -1522,8 +1323,8 @@ namespace AZ {
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
#endif
#endif
const page* pageEnd = mBuckets[i].page_list_end();
for (const page* p = mBuckets[i].page_list_begin(); p != pageEnd; )
auto pageEnd = mBuckets[i].page_list_end();
for (auto p = mBuckets[i].page_list_begin(); p != pageEnd; )
{
// early out if we reach fully occupied page (the remaining should all be full)
if (p->mFreeList == nullptr)
@@ -1537,7 +1338,7 @@ namespace AZ {
{
AZ_TracePrintf("System", "Unused Bucket %d page %p elementSize: %d available: %d elements\n", i, p, elementSize, availableMemory / elementSize);
}
p = p->next();
p = p->m_next;
}
}
return unusedMemory;
@@ -1554,21 +1355,21 @@ namespace AZ {
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
#endif
#endif
page* pageEnd = mBuckets[i].page_list_end();
for (page* p = mBuckets[i].page_list_begin(); p != pageEnd; )
auto pageEnd = mBuckets[i].page_list_end();
for (auto p = mBuckets[i].page_list_begin(); p != pageEnd; )
{
// early out if we reach fully occupied page (the remaining should all be full)
if (p->mFreeList == nullptr)
{
break;
}
page* next = p->next();
page* next = p->m_next;
if (p->empty())
{
HPPA_ASSERT(p->mFreeList);
p->unlink();
mBuckets[i].unlink(AZStd::to_address(p));
p->setInvalid();
bucket_system_free(p);
bucket_system_free(AZStd::to_address(p));
}
p = next;
}
@@ -2239,11 +2040,17 @@ namespace AZ {
size_t HpAllocator::tree_get_max_allocation() const
{
#ifdef MULTITHREADED
AZStd::lock_guard<AZStd::recursive_mutex> lock(mTreeMutex);
#endif
return mFreeTree.maximum()->get_block()->size();
}
size_t HpAllocator::tree_get_unused_memory(bool isPrint) const
{
#ifdef MULTITHREADED
AZStd::lock_guard<AZStd::recursive_mutex> lock(mTreeMutex);
#endif
size_t unusedMemory = 0;
for (free_node_tree::const_iterator it = mFreeTree.begin(); it != mFreeTree.end(); ++it)
{
+9 -2
View File
@@ -83,7 +83,7 @@
#define AZ_PUSH_DISABLE_WARNING_GCC(_gccOption)
/// Compiler specific AZ_POP_DISABLE_WARNING. This needs to be matched with the compiler specific AZ_PUSH_DISABLE_WARNINGs
#define AZ_POP_DISABLE_WARNING_CLANG
#define AZ_POP_DISABLE_WARNING_CLANG
#define AZ_POP_DISABLE_WARNING_MSVC \
__pragma(warning(pop))
#define AZ_POP_DISABLE_WARNING_GCC
@@ -176,7 +176,7 @@
#define AZ_PUSH_DISABLE_WARNING_3(_1, _2, _gccOption) AZ_PUSH_DISABLE_WARNING_GCC(_gccOption)
/// Pops the warning stack. For use matched with an AZ_PUSH_DISABLE_WARNING
#define AZ_POP_DISABLE_WARNING
#define AZ_POP_DISABLE_WARNING
_Pragma("GCC diagnostic pop")
#endif // defined(AZ_COMPILER_CLANG)
@@ -303,3 +303,10 @@
#if !defined(az_has_builtin_wmemmove)
#define az_has_builtin_wmemmove false
#endif
// no unique address attribute support in C++17
#if __has_cpp_attribute(no_unique_address)
#define AZ_NO_UNIQUE_ADDRESS [[no_unique_address]]
#else
#define AZ_NO_UNIQUE_ADDRESS
#endif
@@ -13,6 +13,7 @@
#include <AzCore/ScriptCanvas/ScriptCanvasOnDemandNames.h>
#include <AzCore/RTTI/AzStdOnDemandPrettyName.inl>
#include <AzCore/RTTI/AzStdOnDemandReflectionLuaFunctions.inl>
#include <AzCore/std/optional.h>
#ifndef AZ_USE_CUSTOM_SCRIPT_BIND
struct lua_State;
@@ -47,10 +48,6 @@ namespace AZStd
class intrusive_ptr;
template<class T>
class shared_ptr;
// Wrapper types
template <typename T>
class optional;
}
namespace AZ
+6 -8
View File
@@ -18,6 +18,7 @@
#include <AzCore/std/typetraits/has_member_function.h>
#include <AzCore/std/typetraits/void_t.h>
#include <AzCore/std/function/invoke.h>
#include <AzCore/std/optional.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/Math/Crc.h>
@@ -89,9 +90,6 @@ namespace AZStd
template<class Signature>
class function;
template<class T>
class optional;
struct monostate;
template<class... Types>
@@ -150,7 +148,7 @@ namespace AZ
template <template <typename...> class> constexpr bool false_v1 = false;
#if defined(AZ_COMPILER_MSVC)
// There is a bug with the MSVC compiler when using the 'auto' keyword here. It appears that MSVC is unable to distinguish between a template
// template argument with a type variadic pack vs a template template argument with a non-type auto variadic pack.
// template argument with a type variadic pack vs a template template argument with a non-type auto variadic pack.
template<template<AZStd::size_t...> class> constexpr bool false_v2 = false;
#else
template<template<auto...> class> constexpr bool false_v2 = false;
@@ -176,7 +174,7 @@ namespace AZ
}
#if defined(AZ_COMPILER_MSVC)
// There is a bug with the MSVC compiler when using the 'auto' keyword here. It appears that MSVC is unable to distinguish between a template
// template argument with a type variadic pack vs a template template argument with a non-type auto variadic pack.
// template argument with a type variadic pack vs a template template argument with a non-type auto variadic pack.
template<template<AZStd::size_t...> class T>
#else
template<template<auto...> class T>
@@ -326,7 +324,7 @@ namespace AZ
using TypeIdHolder = AZ::TypeId;
#endif
// Represents the "*" typeid that can be combined with non-pointer types T to form a unique T* typeid
// Represents the "*" typeid that can be combined with non-pointer types T to form a unique T* typeid
inline static const AZ::TypeId& PointerTypeId()
{
static TypeIdHolder s_uuid("{35C8A027-FE00-4769-AE36-6997CFFAF8AE}");
@@ -977,7 +975,7 @@ namespace AZ
AZ_TYPE_INFO_INTERNAL_SPECIALIZE_CV(T, const T&, "const ", "&");
AZ_TYPE_INFO_INTERNAL_SPECIALIZE_CV(T, const T&&, "const ", "&&");
AZ_TYPE_INFO_INTERNAL_SPECIALIZE_CV(T, const T, "const ", "");
AZ_TYPE_INFO_INTERNAL_SPECIALIZED_TEMPLATE_POSTFIX_UUID(AZStd::less, "{41B40AFC-68FD-4ED9-9EC7-BA9992802E1B}", AZ_TYPE_INFO_INTERNAL_TYPENAME);
AZ_TYPE_INFO_INTERNAL_SPECIALIZED_TEMPLATE_POSTFIX_UUID(AZStd::less_equal, "{91CC0BDC-FC46-4617-A405-D914EF1C1902}", AZ_TYPE_INFO_INTERNAL_TYPENAME);
AZ_TYPE_INFO_INTERNAL_SPECIALIZED_TEMPLATE_POSTFIX_UUID(AZStd::greater, "{907F012A-7A4F-4B57-AC23-48DC08D0782E}", AZ_TYPE_INFO_INTERNAL_TYPENAME);
@@ -1014,7 +1012,7 @@ namespace AZ
static constexpr const char* s_variantTypeId{ "{1E8BB1E5-410A-4367-8FAA-D43A4DE14D4B}" };
AZ_TYPE_INFO_INTERNAL_SPECIALIZED_TEMPLATE_PREFIX_UUID(AZStd::variant, "variant", s_variantTypeId, AZ_TYPE_INFO_INTERNAL_TYPENAME_VARARGS);
AZ_TYPE_INFO_INTERNAL_FUNCTION_VARIATION_SPECIALIZATION(AZStd::function, "{C9F9C644-CCC3-4F77-A792-F5B5DBCA746E}");
} // namespace AZ
@@ -11,6 +11,7 @@
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/std/containers/stack.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/tuple.h>
@@ -47,9 +48,6 @@ namespace AZStd
class intrusive_ptr;
template<class T>
class shared_ptr;
template<class T>
class optional;
}
namespace AZ
@@ -8,6 +8,7 @@
#pragma once
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/parallel/scoped_lock.h>
namespace AZ
@@ -159,6 +159,36 @@ namespace UnitTest
TeardownAllocator();
}
};
/**
* RAII wrapper around a BenchmarkEnvironmentBase to encapsulate
* the creation and destuction of the SystemAllocator
* SetUpBenchmark and TearDownBenchmark can still be used for custom
* benchmark environment setup
*/
class ScopedAllocatorBenchmarkEnvironment
: public AZ::Test::BenchmarkEnvironmentBase
{
public:
ScopedAllocatorBenchmarkEnvironment()
{
// Only create the allocator if it doesn't exist
if (!AZ::AllocatorInstance<AZ::SystemAllocator>::IsReady())
{
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
m_ownsAllocator = true;
}
}
~ScopedAllocatorBenchmarkEnvironment() override
{
if (m_ownsAllocator)
{
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
}
}
private:
bool m_ownsAllocator{};
};
#endif
class DLLTestVirtualClass
@@ -20,6 +20,10 @@ set(FILES
base.h
config.h
concepts/concepts.h
concepts/concepts_assignable.h
concepts/concepts_constructible.h
concepts/concepts_copyable.h
concepts/concepts_movable.h
createdestroy.h
docs.h
exceptions.h
@@ -34,8 +38,24 @@ set(FILES
numeric.h
math.h
optional.h
ranges/all_view.h
ranges/elements_view.h
ranges/empty_view.h
ranges/iter_move.h
ranges/iter_swap.h
ranges/join_view.h
ranges/owning_view.h
ranges/ranges.h
ranges/ranges_adaptor.h
ranges/ranges_algorithm.h
ranges/ranges_functional.h
ranges/ref_view.h
ranges/single_view.h
ranges/subrange.h
ranges/split_view.h
ranges/swap.h
ranges/zip_view.h
ranges/zip_view.inl
ratio.h
reference_wrapper.h
sort.h
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>;
}
@@ -42,20 +42,13 @@ namespace AZStd
template<class U, class Hook, class Compare>
friend class intrusive_multiset;
#ifdef AZ_DEBUG_BUILD
intrusive_multiset_node()
{
m_children[0] = nullptr;
m_children[1] = nullptr;
m_neighbours[0] = nullptr;
m_neighbours[1] = nullptr;
m_parentColorSide = nullptr;
}
intrusive_multiset_node() = default;
~intrusive_multiset_node()
{
#if defined(AZ_DEBUG_BUILD)
AZSTD_CONTAINER_ASSERT(m_children[0] == nullptr && m_children[1] == nullptr && m_parentColorSide == nullptr, "AZStd::intrusive_multiset_node - intrusive list node is being destroyed while still in a container!");
}
#endif
}
// We use the lower 2 bits of the parent pointer to store
enum Bits
{
@@ -95,9 +88,9 @@ namespace AZStd
AZ_FORCE_INLINE T* next() const { return m_neighbours[AZSTD_RBTREE_RIGHT]; }
protected:
T* m_children[2];
T* m_neighbours[2];
T* m_parentColorSide;
T* m_children[2]{};
T* m_neighbours[2]{};
T* m_parentColorSide{};
} AZ_MAY_ALIAS; // we access the object in such way that we potentially can break strict aliasing rules
/**
@@ -659,12 +652,10 @@ namespace AZStd
--m_numElements;
#ifdef AZ_DEBUG_BUILD
nodeHook->m_children[0] = nodeHook->m_children[1] = nullptr;
nodeHook->m_parentColorSide = nullptr;
#ifdef DEBUG_INTRUSIVE_MULTISET
#if defined(AZ_DEBUG_BUILD) && defined(DEBUG_INTRUSIVE_MULTISET)
validate();
#endif
#endif
}
@@ -955,8 +946,11 @@ namespace AZStd
hookNode->setParentSide(side);
}
//! pre-condition
//! @param node must be non-nullptr
inline static node_ptr_type MinOrMax(const_node_ptr_type node, SideType side)
{
AZSTD_CONTAINER_ASSERT(node != nullptr, "MinOrMax can only be called with a non-nullptr node")
const_node_ptr_type cur = node;
const_node_ptr_type minMax = cur;
while (!iterator_impl::isNil(cur))
@@ -5,8 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZSTD_ORDERED_MAP_H
#define AZSTD_ORDERED_MAP_H 1
#pragma once
#include <AzCore/std/containers/rbtree.h>
#include <AzCore/std/containers/node_handle.h>
@@ -392,6 +391,26 @@ namespace AZStd
return originalSize - container.size();
}
// deduction guides
template<class InputIterator, class Compare = less<iter_key_type<InputIterator>>,
class Allocator = allocator>
map(InputIterator, InputIterator, Compare = Compare(), Allocator = Allocator())
->map<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>, Compare, Allocator>;
template<class Key, class T, class Compare = less<Key>,
class Allocator = allocator>
map(initializer_list<pair<Key, T>>, Compare = Compare(), Allocator = Allocator())
->map<Key, T, Compare, Allocator>;
template<class InputIterator, class Allocator>
map(InputIterator, InputIterator, Allocator)
->map<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>,
less<iter_key_type<InputIterator>>, Allocator>;
template<class Key, class T, class Allocator>
map(initializer_list<pair<Key, T>>, Allocator)->map<Key, T, less<Key>, Allocator>;
/**
* Ordered multimap container is complaint with \ref C++0x (23.4.2)
* This is an associative container, key can be equivalent (multiple copies of the same key value).
@@ -684,7 +703,26 @@ namespace AZStd
return originalSize - container.size();
}
}
#endif // AZSTD_ORDERED_MAP_H
#pragma once
// deduction guides
template<class InputIterator, class Compare = less<iter_key_type<InputIterator>>,
class Allocator = allocator>
multimap(InputIterator, InputIterator, Compare = Compare(), Allocator = Allocator())
->multimap<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>,
Compare, Allocator>;
template<class Key, class T, class Compare = less<Key>,
class Allocator = allocator>
multimap(initializer_list<pair<Key, T>>, Compare = Compare(), Allocator = Allocator())
->multimap<Key, T, Compare, Allocator>;
template<class InputIterator, class Allocator>
multimap(InputIterator, InputIterator, Allocator)
->multimap<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>,
less<iter_key_type<InputIterator>>, Allocator>;
template<class Key, class T, class Allocator>
multimap(initializer_list<pair<Key, T>>, Allocator)
->multimap<Key, T, less<Key>, Allocator>;
}
@@ -7,8 +7,10 @@
*/
#pragma once
#include <AzCore/std/optional.h>
#include <AzCore/std/allocator_traits.h>
#include <AzCore/std/iterator.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/utils.h>
namespace AZStd
{
@@ -193,4 +195,23 @@ namespace AZStd
template <typename NodeTraits>
using map_node_handle = node_handle<NodeTraits, map_node_base>;
}
inline namespace AssociativeInternal
{
// deduction guide helpers
template<class InputIterator>
using iter_value_type = typename iterator_traits<InputIterator>::value_type::second_type;
template<class InputIterator>
using iter_key_type = remove_const_t<typename iterator_traits<InputIterator>::value_type::first_type>;
template<class InputIterator>
using iter_mapped_type = typename iterator_traits<InputIterator>::value_type::second_type;
template<class InputIterator>
using iter_to_alloc_type = pair<
add_const_t<typename iterator_traits<InputIterator>::value_type::first_type>,
typename iterator_traits<InputIterator>::value_type::second_type
>;
}
}
@@ -5,8 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZSTD_ORDERED_SET_H
#define AZSTD_ORDERED_SET_H 1
#pragma once
#include <AzCore/std/containers/node_handle.h>
#include <AzCore/std/containers/rbtree.h>
@@ -300,6 +299,26 @@ namespace AZStd
return originalSize - container.size();
}
// deduction guides
template<class InputIterator,
class Compare = less<iter_value_type<InputIterator>>,
class Allocator = allocator>
set(InputIterator, InputIterator,
Compare = Compare(), Allocator = Allocator())
->set<iter_value_type<InputIterator>, Compare, Allocator>;
template<class Key, class Compare = less<Key>, class Allocator = allocator>
set(initializer_list<Key>, Compare = Compare(), Allocator = Allocator())
->set<Key, Compare, Allocator>;
template<class InputIterator, class Allocator>
set(InputIterator, InputIterator, Allocator)
->set<iter_value_type<InputIterator>,
less<iter_value_type<InputIterator>>, Allocator>;
template<class Key, class Allocator>
set(initializer_list<Key>, Allocator)->set<Key, less<Key>, Allocator>;
/**
* Ordered multiset container is complaint with \ref C++0x (23.4.4)
* This is an associative container, key can be equivalent (multiple copies of the same key value).
@@ -573,7 +592,24 @@ namespace AZStd
return originalSize - container.size();
}
}
#endif // AZSTD_ORDERED_SET_H
#pragma once
// deduction guides
template<class InputIterator,
class Compare = less<iter_value_type<InputIterator>>,
class Allocator = allocator>
multiset(InputIterator, InputIterator,
Compare = Compare(), Allocator = Allocator())
->multiset<iter_value_type<InputIterator>, Compare, Allocator>;
template<class Key, class Compare = less<Key>, class Allocator = allocator>
multiset(initializer_list<Key>, Compare = Compare(), Allocator = Allocator())
->multiset<Key, Compare, Allocator>;
template<class InputIterator, class Allocator>
multiset(InputIterator, InputIterator, Allocator)
->multiset<iter_value_type<InputIterator>,
less<iter_value_type<InputIterator>>, Allocator>;
template<class Key, class Allocator>
multiset(initializer_list<Key>, Allocator)->multiset<Key, less<Key>, Allocator>;
}
@@ -5,8 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZSTD_UNORDERED_MAP_H
#define AZSTD_UNORDERED_MAP_H 1
#pragma once
#include <AzCore/std/containers/node_handle.h>
#include <AzCore/std/hash_table.h>
@@ -344,6 +343,66 @@ namespace AZStd
return originalSize - container.size();
}
// deduction guides
template<class InputIterator,
class Hash = hash<iter_key_type<InputIterator>>,
class Pred = equal_to<iter_key_type<InputIterator>>,
class Allocator = allocator>
unordered_map(InputIterator, InputIterator,
typename Internal::UnorderedMapTableTraits<iter_key_type<InputIterator>,
iter_mapped_type<InputIterator>, Hash, Pred, Allocator, false>::size_type = {},
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
->unordered_map<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>, Hash, Pred,
Allocator>;
template<class Key, class T, class Hash = hash<Key>,
class Pred = equal_to<Key>, class Allocator = allocator>
unordered_map(initializer_list<pair<Key, T>>,
typename Internal::UnorderedMapTableTraits<Key, T, Hash, Pred, Allocator, false>::size_type = {},
Hash = Hash(),
Pred = Pred(), Allocator = Allocator())
->unordered_map<Key, T, Hash, Pred, Allocator>;
template<class InputIterator, class Allocator>
unordered_map(InputIterator, InputIterator,
typename Internal::UnorderedMapTableTraits<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>,
hash<iter_key_type<InputIterator>>, equal_to<iter_key_type<InputIterator>>, Allocator, false>::size_type,
Allocator)
->unordered_map<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>,
hash<iter_key_type<InputIterator>>,
equal_to<iter_key_type<InputIterator>>, Allocator>;
template<class InputIterator, class Allocator>
unordered_map(InputIterator, InputIterator, Allocator)
->unordered_map<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>,
hash<iter_key_type<InputIterator>>,
equal_to<iter_key_type<InputIterator>>, Allocator>;
template<class InputIterator, class Hash, class Allocator>
unordered_map(InputIterator, InputIterator,
typename Internal::UnorderedMapTableTraits<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>,
Hash, equal_to<iter_key_type<InputIterator>>, Allocator, false>::size_type,
Hash, Allocator)
->unordered_map<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>, Hash,
equal_to<iter_key_type<InputIterator>>, Allocator>;
template<class Key, class T, class Allocator>
unordered_map(initializer_list<pair<Key, T>>, typename Internal::UnorderedMapTableTraits<Key, T,
hash<Key>, equal_to<Key>, Allocator, false>::size_type,
Allocator)
->unordered_map<Key, T, hash<Key>, equal_to<Key>, Allocator>;
template<class Key, class T, class Allocator>
unordered_map(initializer_list<pair<Key, T>>, Allocator)
->unordered_map<Key, T, hash<Key>, equal_to<Key>, Allocator>;
template<class Key, class T, class Hash, class Allocator>
unordered_map(initializer_list<pair<Key, T>>, typename Internal::UnorderedMapTableTraits<Key, T,
Hash, equal_to<Key>, Allocator, true>::size_type,
Hash, Allocator)
->unordered_map<Key, T, Hash, equal_to<Key>, Allocator>;
/**
* Unordered multi map container is complaint with \ref CTR1 (6.2.4.6)
* The only difference from the unordered_map is that we allow multiple entries with
@@ -560,7 +619,63 @@ namespace AZStd
return originalSize - container.size();
}
// deduction guides
template<class InputIterator,
class Hash = hash<iter_key_type<InputIterator>>,
class Pred = equal_to<iter_key_type<InputIterator>>,
class Allocator = allocator>
unordered_multimap(InputIterator, InputIterator,
typename Internal::UnorderedMapTableTraits<iter_key_type<InputIterator>,
iter_mapped_type<InputIterator>, Hash, Pred, Allocator, true>::size_type = {},
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
->unordered_multimap<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>,
Hash, Pred, Allocator>;
template<class Key, class T, class Hash = hash<Key>,
class Pred = equal_to<Key>, class Allocator = allocator>
unordered_multimap(initializer_list<pair<Key, T>>,
typename Internal::UnorderedMapTableTraits<Key, T, Hash, Pred, Allocator, true>::size_type = {},
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
->unordered_multimap<Key, T, Hash, Pred, Allocator>;
template<class InputIterator, class Allocator>
unordered_multimap(InputIterator, InputIterator,
typename Internal::UnorderedMapTableTraits<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>,
hash<iter_key_type<InputIterator>>, equal_to<iter_key_type<InputIterator>>, Allocator, true>::size_type,
Allocator)
->unordered_multimap<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>,
hash<iter_key_type<InputIterator>>,
equal_to<iter_key_type<InputIterator>>, Allocator>;
template<class InputIterator, class Allocator>
unordered_multimap(InputIterator, InputIterator, Allocator)
->unordered_multimap<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>,
hash<iter_key_type<InputIterator>>,
equal_to<iter_key_type<InputIterator>>, Allocator>;
template<class InputIterator, class Hash, class Allocator>
unordered_multimap(InputIterator, InputIterator,
typename Internal::UnorderedMapTableTraits<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>,
Hash, equal_to<iter_key_type<InputIterator>>, Allocator, true>::size_type,
Hash, Allocator)
->unordered_multimap<iter_key_type<InputIterator>, iter_mapped_type<InputIterator>, Hash,
equal_to<iter_key_type<InputIterator>>, Allocator>;
template<class Key, class T, class Allocator>
unordered_multimap(initializer_list<pair<Key, T>>, typename Internal::UnorderedMapTableTraits<Key, T,
hash<Key>, equal_to<Key>, Allocator, true>::size_type,
Allocator)
->unordered_multimap<Key, T, hash<Key>, equal_to<Key>, Allocator>;
template<class Key, class T, class Allocator>
unordered_multimap(initializer_list<pair<Key, T>>, Allocator)
->unordered_multimap<Key, T, hash<Key>, equal_to<Key>, Allocator>;
template<class Key, class T, class Hash, class Allocator>
unordered_multimap(initializer_list<pair<Key, T>>, typename Internal::UnorderedMapTableTraits<Key, T,
Hash, equal_to<Key>, Allocator, true>::size_type,
Hash, Allocator)
->unordered_multimap<Key, T, Hash, equal_to<Key>, Allocator>;
}
#endif // AZSTD_UNORDERED_MAP_H
#pragma once
@@ -5,8 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZSTD_UNORDERED_SET_H
#define AZSTD_UNORDERED_SET_H 1
#pragma once
#include <AzCore/std/containers/node_handle.h>
#include <AzCore/std/hash_table.h>
@@ -253,6 +252,55 @@ namespace AZStd
return originalSize - container.size();
}
// deduction guides
template<class InputIterator,
class Hash = hash<iter_value_type<InputIterator>>,
class Pred = equal_to<iter_value_type<InputIterator>>,
class Allocator = allocator>
unordered_set(InputIterator, InputIterator,
typename Internal::UnorderedSetTableTraits<iter_value_type<InputIterator>, Hash, Pred, Allocator, false>::size_type = {},
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
->unordered_set<iter_value_type<InputIterator>,
Hash, Pred, Allocator>;
template<class T, class Hash = hash<T>,
class Pred = equal_to<T>, class Allocator = allocator>
unordered_set(initializer_list<T>,
typename Internal::UnorderedSetTableTraits<T, Hash, Pred, Allocator, false>::size_type = {},
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
->unordered_set<T, Hash, Pred, Allocator>;
template<class InputIterator, class Allocator>
unordered_set(InputIterator, InputIterator,
typename Internal::UnorderedSetTableTraits<iter_value_type<InputIterator>, hash<iter_value_type<InputIterator>>,
equal_to<iter_value_type<InputIterator>>, Allocator, false>::size_type,
Allocator)
->unordered_set<iter_value_type<InputIterator>,
hash<iter_value_type<InputIterator>>,
equal_to<iter_value_type<InputIterator>>,
Allocator>;
template<class InputIterator, class Hash, class Allocator>
unordered_set(InputIterator, InputIterator,
typename Internal::UnorderedSetTableTraits<iter_value_type<InputIterator>, Hash,
equal_to<iter_value_type<InputIterator>>, Allocator, false>::size_type,
Hash, Allocator)
->unordered_set<iter_value_type<InputIterator>, Hash,
equal_to<iter_value_type<InputIterator>>,
Allocator>;
template<class T, class Allocator>
unordered_set(initializer_list<T>,
typename Internal::UnorderedSetTableTraits<T, hash<T>, equal_to<T>, Allocator, false>::size_type,
Allocator)
->unordered_set<T, hash<T>, equal_to<T>, Allocator>;
template<class T, class Hash, class Allocator>
unordered_set(initializer_list<T>,
typename Internal::UnorderedSetTableTraits<T, Hash, equal_to<T>, Allocator, false>::size_type,
Hash, Allocator)
->unordered_set<T, Hash, equal_to<T>, Allocator>;
/**
* Unordered multi set container is complaint with \ref CTR1 (6.2.4.5)
* The only difference from the unordered_set is that we allow multiple entries with
@@ -448,7 +496,52 @@ namespace AZStd
return originalSize - container.size();
}
}
// deduction guides
template<class InputIterator,
class Hash = hash<iter_value_type<InputIterator>>,
class Pred = equal_to<iter_value_type<InputIterator>>,
class Allocator = allocator>
unordered_multiset(InputIterator, InputIterator,
typename Internal::UnorderedSetTableTraits<iter_value_type<InputIterator>, Hash, Pred, Allocator, true>::size_type = {},
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
->unordered_multiset<iter_value_type<InputIterator>,
Hash, Pred, Allocator>;
#endif // AZSTD_UNORDERED_SET_H
#pragma once
template<class T, class Hash = hash<T>,
class Pred = equal_to<T>, class Allocator = allocator>
unordered_multiset(initializer_list<T>,
typename Internal::UnorderedSetTableTraits<T, Hash, Pred, Allocator, true>::size_type = {},
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
->unordered_multiset<T, Hash, Pred, Allocator>;
template<class InputIterator, class Allocator>
unordered_multiset(InputIterator, InputIterator,
typename Internal::UnorderedSetTableTraits<iter_value_type<InputIterator>, hash<iter_value_type<InputIterator>>,
equal_to<iter_value_type<InputIterator>>, Allocator, true>::size_type,
Allocator)
->unordered_multiset<iter_value_type<InputIterator>,
hash<iter_value_type<InputIterator>>,
equal_to<iter_value_type<InputIterator>>,
Allocator>;
template<class InputIterator, class Hash, class Allocator>
unordered_multiset(InputIterator, InputIterator,
typename Internal::UnorderedSetTableTraits<iter_value_type<InputIterator>, Hash,
equal_to<iter_value_type<InputIterator>>, Allocator, true>::size_type,
Hash, Allocator)
->unordered_multiset<iter_value_type<InputIterator>, Hash,
equal_to<iter_value_type<InputIterator>>,
Allocator>;
template<class T, class Allocator>
unordered_multiset(initializer_list<T>,
typename Internal::UnorderedSetTableTraits<T, hash<T>, equal_to<T>, Allocator, true>::size_type,
Allocator)
->unordered_multiset<T, hash<T>, equal_to<T>, Allocator>;
template<class T, class Hash, class Allocator>
unordered_multiset(initializer_list<T>,
typename Internal::UnorderedSetTableTraits<T, Hash, equal_to<T>, Allocator, true>::size_type,
Hash, Allocator)
->unordered_multiset<T, Hash, equal_to<T>, Allocator>;
}
@@ -20,7 +20,7 @@ namespace AZStd
using is_transparent = void;
template <typename T>
T&& operator()(T&& t) const
constexpr T&& operator()(T&& t) const
{
return AZStd::forward<T>(t);
}
@@ -19,12 +19,28 @@ namespace AZStd
template <class R, class Fn, class... ArgTypes>
struct is_invocable_r : Internal::invocable_r<R, Fn, ArgTypes...>::type {};
template <class Fn, class... ArgTypes>
struct is_nothrow_invocable
: bool_constant<noexcept(Internal::invocable<Fn, ArgTypes...>::value)>
{};
template <class R, class Fn, class... ArgTypes>
struct is_nothrow_invocable_r
: bool_constant<noexcept(Internal::invocable_r<R, Fn, ArgTypes...>::value)>
{};
template <class Fn, class... ArgTypes>
constexpr bool is_invocable_v = is_invocable<Fn, ArgTypes...>::value;
template <class R, class Fn, class ...ArgTypes>
constexpr bool is_invocable_r_v = is_invocable_r<R, Fn, ArgTypes...>::value;
template <class Fn, class... ArgTypes>
constexpr bool is_nothrow_invocable_v = is_nothrow_invocable<Fn, ArgTypes...>::value;
template <class R, class Fn, class ...ArgTypes>
constexpr bool is_nothrow_invocable_r_v = is_nothrow_invocable_r<R, Fn, ArgTypes...>::value;
template<class Fn, class... ArgTypes>
struct invoke_result
: AZStd::enable_if<Internal::invocable<Fn, ArgTypes...>::value, typename Internal::invocable<Fn, ArgTypes...>::result_type>
@@ -197,9 +197,5 @@ namespace AZStd
return *reinterpret_cast<Iterator*>(&iter);
}
}
using std::size;
using std::empty;
using std::data;
} // namespace AZStd
@@ -9,9 +9,14 @@
#include <AzCore/std/base.h>
#include <AzCore/std/concepts/concepts_constructible.h>
#include <AzCore/std/concepts/concepts_copyable.h>
#include <AzCore/std/concepts/concepts_movable.h>
#include <AzCore/std/ranges/iter_move.h>
#include <AzCore/std/typetraits/common_reference.h>
#include <AzCore/std/typetraits/conditional.h>
#include <AzCore/std/typetraits/conjunction.h>
#include <AzCore/std/typetraits/integral_constant.h>
#include <AzCore/std/typetraits/is_array.h>
#include <AzCore/std/typetraits/is_class.h>
#include <AzCore/std/typetraits/is_enum.h>
@@ -124,21 +129,24 @@ namespace AZStd::Internal
struct incrementable_requires {};
// iterator_traits has been specialized
template <typename T>
struct incrementable_requires<T, enable_if_t<!is_primary_template_v<iterator_traits<T>>
&& is_void_v<void_t<typename iterator_traits<T>::difference_type>> >>
struct incrementable_requires<T, enable_if_t<conjunction_v<
bool_constant<!is_primary_template_v<iterator_traits<T>>>,
bool_constant<is_void_v<void_t<typename iterator_traits<T>::difference_type>>>> >>
{
using difference_type = typename iterator_traits<T>::difference_type;
};
template <typename T>
struct incrementable_requires<T, enable_if_t<is_primary_template_v<iterator_traits<T>>
&& has_difference_type_v<T>>>
struct incrementable_requires<T, enable_if_t<conjunction_v<
bool_constant<is_primary_template_v<iterator_traits<T>>>,
bool_constant<has_difference_type_v<T>>> >>
{
using difference_type = typename T::difference_type;
};
template <typename T>
struct incrementable_requires<T, enable_if_t<is_primary_template_v<iterator_traits<T>>
&& !has_difference_type_v<T>
&& integral<decltype(declval<T>() - declval<T>())> >>
struct incrementable_requires<T, enable_if_t< conjunction_v<
bool_constant<is_primary_template_v<iterator_traits<T>>>,
bool_constant<!has_difference_type_v<T>>,
bool_constant<integral<decltype(declval<T>() - declval<T>())>>> >>
{
using difference_type = make_signed_t<decltype(declval<T>() - declval<T>())>;
};
@@ -164,7 +172,7 @@ namespace AZStd
using iter_reference_t = enable_if_t<Internal::dereferenceable<T>, decltype(*declval<T&>())>;
// incrementable_traits for iter_difference_t
template <typename T>
template <typename T, class = void>
struct incrementable_traits
: Internal::incrementable_requires<T> {};
template <typename T>
@@ -187,14 +195,109 @@ namespace AZStd
constexpr bool indirectly_readable_impl = false;
template <class In>
constexpr bool indirectly_readable_impl<In, enable_if_t<same_as<decltype(*declval<In>()), iter_reference_t<In>>
&& same_as<decltype(AZStd::ranges::iter_move(declval<In>())), iter_rvalue_reference_t<In>>
&& common_reference_with<iter_reference_t<In>&&, iter_value_t<In>&>
&& common_reference_with<iter_reference_t<In>&&, iter_rvalue_reference_t<In>&>
&& common_reference_with<iter_rvalue_reference_t<In>&&, const iter_value_t<In>&>>> = true;
constexpr bool indirectly_readable_impl<In, enable_if_t<conjunction_v<
bool_constant<same_as<decltype(*declval<In>()), iter_reference_t<In>>>,
bool_constant<same_as<decltype(AZStd::ranges::iter_move(declval<In>())), iter_rvalue_reference_t<In>>>,
bool_constant<common_reference_with<iter_reference_t<In>&&, iter_value_t<In>&>>,
bool_constant<common_reference_with<iter_reference_t<In>&&, iter_rvalue_reference_t<In>&>>,
bool_constant<common_reference_with<iter_rvalue_reference_t<In>&&, const iter_value_t<In>&>> >>> = true;
}
template <typename T>
using iter_common_reference_t = enable_if_t<Internal::indirectly_readable_impl<T>,
common_reference_t<iter_reference_t<T>, iter_value_t<T>&>>;
}
namespace AZStd
{
// indirectly readable
template <class In>
/*concept*/ constexpr bool indirectly_readable = Internal::indirectly_readable_impl<remove_cvref_t<In>>;
}
namespace AZStd::Internal
{
// model the indirectly writable concept
template <class Out, class T, class = void>
constexpr bool indirectly_writable_impl = false;
template <class Out, class T>
constexpr bool indirectly_writable_impl<Out, T, void_t<
decltype(*declval<Out&>() = declval<T>()),
decltype(*declval<Out>() = declval<T>()),
decltype(const_cast<const iter_reference_t<Out>&&>(*declval<Out&>()) = declval<T>()),
decltype(const_cast<const iter_reference_t<Out>&&>(*declval<Out>()) = declval<T>())>
> = true;
}
namespace AZStd
{
// indirectly writable
template <class Out, class T>
/*concept*/ constexpr bool indirectly_writable = Internal::indirectly_writable_impl<Out, T>;
// indirectly movable
template<class In, class Out>
/*concept*/ constexpr bool indirectly_movable = conjunction_v<bool_constant<indirectly_readable<In>>,
bool_constant<indirectly_writable<Out, iter_rvalue_reference_t<In>>>>;
}
namespace AZStd::Internal
{
template<class In, class Out, class = void>
constexpr bool indirectly_movable_storage_impl = false;
template<class In, class Out>
constexpr bool indirectly_movable_storage_impl<In, Out, enable_if_t<conjunction_v<
bool_constant<indirectly_movable<In, Out>>,
bool_constant<indirectly_writable<Out, iter_value_t<In>>>,
bool_constant<movable<iter_value_t<In>>>,
bool_constant<constructible_from<iter_value_t<In>, iter_rvalue_reference_t<In>>>,
bool_constant<assignable_from<iter_value_t<In>&, iter_rvalue_reference_t<In>>> >>> = true;
}
namespace AZStd
{
template<class In, class Out>
/*concept*/ constexpr bool indirectly_movable_storable = Internal::indirectly_movable_storage_impl<In, Out>;
}
namespace AZStd::Internal
{
template<class In, class Out, class = void>
constexpr bool indirectly_copyable_impl = false;
template<class In, class Out>
constexpr bool indirectly_copyable_impl<In, Out, enable_if_t<conjunction_v<
bool_constant<indirectly_readable<In>>,
bool_constant<indirectly_writable<Out, iter_reference_t<In>>> >>> = true;
}
namespace AZStd
{
// indirectly copyable
template<class In, class Out>
/*concept*/ constexpr bool indirectly_copyable = Internal::indirectly_copyable_impl<In, Out>;
}
namespace AZStd::Internal
{
template<class In, class Out, class = void>
constexpr bool indirectly_copyable_storable_impl = false;
template<class In, class Out>
constexpr bool indirectly_copyable_storable_impl<In, Out, enable_if_t < conjunction_v <
bool_constant<indirectly_copyable<In, Out>>,
bool_constant<indirectly_writable<Out, iter_value_t<In>&>>,
bool_constant<indirectly_writable<Out, const iter_value_t<In>&>>,
bool_constant<indirectly_writable<Out, iter_value_t<In>&&>>,
bool_constant<indirectly_writable<Out, const iter_value_t<In>&&>>,
bool_constant<copyable<iter_value_t<In>>>,
bool_constant<constructible_from<iter_value_t<In>, iter_reference_t<In>>>,
bool_constant<assignable_from<iter_value_t<In>&, iter_reference_t<In>>>
>>> = true;
}
namespace AZStd
{
template<class In, class Out>
/*concept*/ constexpr bool indirectly_copyable_storable = Internal::indirectly_copyable_storable_impl<In, Out>;
}
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;
}
@@ -74,8 +74,16 @@ namespace AZStd::ranges::Internal
namespace AZStd::ranges
{
inline namespace customization_point_object
// Workaround for clang bug https://bugs.llvm.org/show_bug.cgi?id=37556
// Adding placing the inline customization_point_object namespace
// under a regular namespace and then aliasing that into the AZStd::ranges namespace
namespace workaround
{
inline constexpr auto iter_move = Internal::iter_move_fn{};
inline namespace customization_point_object
{
inline constexpr auto iter_move = Internal::iter_move_fn{};
}
}
using namespace workaround;
}
@@ -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>;
}
+290 -139
View File
@@ -36,10 +36,12 @@ namespace AZStd::ranges
{
// Variadic template which maps types to true For SFINAE
template <class... Args>
using sfinae_trigger = true_type;
template <class... Args>
constexpr bool sfinae_trigger_v = true;
template <class T>
constexpr bool is_lvalue_or_borrowable = is_lvalue_reference_v<T> || enable_borrowed_range<remove_cv_t<T>>;
constexpr bool is_lvalue_or_borrowable = disjunction_v<is_lvalue_reference<T>, bool_constant<enable_borrowed_range<remove_cv_t<T>>>>;
//! begin
template <class T, typename = void>
@@ -50,8 +52,11 @@ namespace AZStd::ranges
template <class T, typename = void>
constexpr bool has_unqualified_begin = false;
template <class T>
constexpr bool has_unqualified_begin<T, void_t<decltype(begin(declval<T&>()))>>
= !has_member_begin<T> && AZStd::Internal::is_class_or_enum<T>;
constexpr bool has_unqualified_begin<T, enable_if_t<conjunction_v<
bool_constant<!has_member_begin<T>>,
bool_constant<AZStd::Internal::is_class_or_enum<T>>,
sfinae_trigger<decltype(begin(declval<T&>()))>
>>> = true;
template<class T>
void begin(T&) = delete;
@@ -62,7 +67,7 @@ namespace AZStd::ranges
{
template<class T>
constexpr auto operator()(T& t) const noexcept ->
enable_if_t<is_array_v<T> && sfinae_trigger_v<remove_all_extents_t<T>>,
enable_if_t<conjunction_v<is_array<T>, sfinae_trigger<remove_all_extents_t<T>>>,
decltype(t + 0)>
{
return t + 0;
@@ -70,8 +75,11 @@ namespace AZStd::ranges
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward<T>(t).begin())) ->
enable_if_t<input_or_output_iterator<decltype(t.begin())>&& is_lvalue_or_borrowable<T>
&& !is_array_v<T>&& has_member_begin<T>,
enable_if_t<conjunction_v<
bool_constant<input_or_output_iterator<decltype(AZStd::forward<T>(t).begin())>>,
bool_constant<is_lvalue_or_borrowable<T>>,
bool_constant<!is_array_v<T>>,
bool_constant<has_member_begin<T>>>,
decltype(AZStd::forward<T>(t).begin())>
{
return AZStd::forward<T>(t).begin();
@@ -79,8 +87,11 @@ namespace AZStd::ranges
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(begin(AZStd::forward<T>(t)))) ->
enable_if_t<input_or_output_iterator<decltype(begin(t))>&& is_lvalue_or_borrowable<T>
&& !is_array_v<T> && has_unqualified_begin<T>,
enable_if_t<conjunction_v<
bool_constant<input_or_output_iterator<decltype(begin(AZStd::forward<T>(t)))>>,
bool_constant<is_lvalue_or_borrowable<T>>,
bool_constant<!is_array_v<T>>,
bool_constant<has_unqualified_begin<T>>>,
decltype(begin(AZStd::forward<T>(t)))>
{
return begin(AZStd::forward<T>(t));
@@ -109,8 +120,11 @@ namespace AZStd::ranges
template <class T, typename = void>
constexpr bool has_unqualified_end = false;
template <class T>
constexpr bool has_unqualified_end<T, void_t<decltype(end(declval<T&>()))>>
= !has_member_end<T> && AZStd::Internal::is_class_or_enum<T>;
constexpr bool has_unqualified_end<T, enable_if_t<conjunction_v<
bool_constant<!has_member_end<T>>,
bool_constant<AZStd::Internal::is_class_or_enum<T>>,
sfinae_trigger<decltype(end(declval<T&>()))>
>>> = true;
template<class T>
void end(T&) = delete;
@@ -121,7 +135,7 @@ namespace AZStd::ranges
{
template<class T>
constexpr auto operator()(T& t) const noexcept ->
enable_if_t<is_array_v<T> && extent_v<T> != 0,
enable_if_t<conjunction_v<is_array<T>, bool_constant<extent_v<T> != 0>>,
decltype(t + extent_v<T>)>
{
return t + extent_v<T>;
@@ -129,8 +143,11 @@ namespace AZStd::ranges
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward<T>(t).end())) ->
enable_if_t<sentinel_for<decltype(t.end()), iterator_t<T>>&& is_lvalue_or_borrowable<T>
&& !is_array_v<T> && has_member_end<T>,
enable_if_t<conjunction_v<
bool_constant<sentinel_for<decltype(AZStd::forward<T>(t).end()), iterator_t<T>>>,
bool_constant<is_lvalue_or_borrowable<T>>,
bool_constant<!is_array_v<T>>,
bool_constant<has_member_end<T>>>,
decltype(AZStd::forward<T>(t).end())>
{
return AZStd::forward<T>(t).end();
@@ -138,8 +155,11 @@ namespace AZStd::ranges
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(end(AZStd::forward<T>(t)))) ->
enable_if_t<sentinel_for<decltype(end(t)), iterator_t<T>>&& is_lvalue_or_borrowable<T>
&& !is_array_v<T> && has_unqualified_end<T>,
enable_if_t<conjunction_v<
bool_constant<sentinel_for<decltype(end(AZStd::forward<T>(t))), iterator_t<T>>>,
bool_constant<is_lvalue_or_borrowable<T>>,
bool_constant<!is_array_v<T>>,
bool_constant<has_unqualified_end<T>>>,
decltype(end(AZStd::forward<T>(t)))>
{
return end(AZStd::forward<T>(t));
@@ -212,17 +232,22 @@ namespace AZStd::ranges
template <class T, class = void>
constexpr bool has_unqualified_rbegin = false;
template <class T>
constexpr bool has_unqualified_rbegin<T, void_t<decltype(rbegin(declval<T&>()))>>
= !has_member_rbegin<T> && AZStd::Internal::is_class_or_enum<T>;
constexpr bool has_unqualified_rbegin<T, enable_if_t<conjunction_v<
bool_constant<!has_member_rbegin<T>>,
sfinae_trigger<decltype(rbegin(declval<T&>()))>,
bool_constant<::AZStd::Internal::is_class_or_enum<T>>
>>> = true;
template <class T, class = void>
constexpr bool has_bidirectional_rbegin = false;
template <class T>
constexpr bool has_bidirectional_rbegin<T, enable_if_t<
same_as<decltype(ranges::begin(declval<T&>())), decltype(ranges::end(declval<T&>()))>
&& bidirectional_iterator<decltype(ranges::begin(declval<T&>()))>
&& bidirectional_iterator<decltype(ranges::end(declval<T&>()))>
>> = !has_member_rbegin<T> && !has_unqualified_rbegin<T>;
constexpr bool has_bidirectional_rbegin<T, enable_if_t<conjunction_v<
bool_constant<!has_member_rbegin<T>>,
bool_constant<!has_unqualified_rbegin<T>>,
bool_constant<same_as<decltype(ranges::begin(declval<T&>())), decltype(ranges::end(declval<T&>()))>>,
bool_constant<bidirectional_iterator<decltype(ranges::begin(declval<T&>()))>>,
bool_constant<bidirectional_iterator<decltype(ranges::end(declval<T&>()))>>
>>> = true;
template<class T>
@@ -234,8 +259,10 @@ namespace AZStd::ranges
{
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward<T>(t).rbegin())) ->
enable_if_t<input_or_output_iterator<decltype(t.rbegin())> && is_lvalue_or_borrowable<T>
&& has_member_rbegin<T>,
enable_if_t<conjunction_v<
bool_constant<input_or_output_iterator<decltype(AZStd::forward<T>(t).rbegin())>>,
bool_constant<is_lvalue_or_borrowable<T>>,
bool_constant<has_member_rbegin<T>>>,
decltype(AZStd::forward<T>(t).rbegin())>
{
return AZStd::forward<T>(t).rbegin();
@@ -243,8 +270,10 @@ namespace AZStd::ranges
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(rbegin(AZStd::forward<T>(t)))) ->
enable_if_t<input_or_output_iterator<decltype(rbegin(t))> && is_lvalue_or_borrowable<T>
&& has_unqualified_rbegin<T>,
enable_if_t<conjunction_v<
bool_constant<input_or_output_iterator<decltype(rbegin(AZStd::forward<T>(t)))>>,
bool_constant<is_lvalue_or_borrowable<T>>,
bool_constant<has_unqualified_rbegin<T>>>,
decltype(rbegin(AZStd::forward<T>(t)))>
{
return rbegin(AZStd::forward<T>(t));
@@ -276,17 +305,22 @@ namespace AZStd::ranges
template <class T, class = void>
constexpr bool has_unqualified_rend = false;
template <class T>
constexpr bool has_unqualified_rend<T, void_t<decltype(rend(declval<T&>()))>>
= !has_member_rend<T> && AZStd::Internal::is_class_or_enum<T>;
constexpr bool has_unqualified_rend<T, enable_if_t<conjunction_v<
bool_constant<!has_member_rend<T>>,
sfinae_trigger<decltype(rend(declval<T&>()))>,
bool_constant<::AZStd::Internal::is_class_or_enum<T>>
>>> = true;
template <class T, class = void>
constexpr bool has_bidirectional_rend = false;
template <class T>
constexpr bool has_bidirectional_rend<T, enable_if_t<
same_as<decltype(ranges::begin(declval<T&>())), decltype(ranges::end(declval<T&>()))>
&& bidirectional_iterator<decltype(ranges::begin(declval<T&>()))>
&& bidirectional_iterator<decltype(ranges::end(declval<T&>()))>
>> = !has_member_rend<T> && !has_unqualified_rend<T>;
constexpr bool has_bidirectional_rend<T, enable_if_t<conjunction_v<
bool_constant<!has_member_rend<T>>,
bool_constant<!has_unqualified_rend<T>>,
bool_constant<same_as<decltype(ranges::begin(declval<T&>())), decltype(ranges::end(declval<T&>()))>>,
bool_constant<bidirectional_iterator<decltype(ranges::begin(declval<T&>()))>>,
bool_constant<bidirectional_iterator<decltype(ranges::end(declval<T&>()))>>
>>> = true;
template<class T>
void rend(T&) = delete;
@@ -297,8 +331,10 @@ namespace AZStd::ranges
{
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward<T>(t).rend())) ->
enable_if_t<sentinel_for<decltype(t.rend()), decltype(ranges::rbegin(t))> && is_lvalue_or_borrowable<T>
&& has_member_rend<T>,
enable_if_t<conjunction_v<
bool_constant<sentinel_for<decltype(t.rend()), decltype(ranges::rbegin(AZStd::forward<T>(t)))>>,
bool_constant<is_lvalue_or_borrowable<T>>,
bool_constant<has_member_rend<T>>>,
decltype(AZStd::forward<T>(t).rend())>
{
return AZStd::forward<T>(t).rend();
@@ -306,8 +342,10 @@ namespace AZStd::ranges
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(rend(AZStd::forward<T>(t)))) ->
enable_if_t<sentinel_for<decltype(rend(t)), decltype(ranges::rbegin(t))> && is_lvalue_or_borrowable<T>
&& has_unqualified_rend<T>,
enable_if_t<conjunction_v<
bool_constant<sentinel_for<decltype(rend(t)), decltype(ranges::rbegin(AZStd::forward<T>(t)))>>,
bool_constant<is_lvalue_or_borrowable<T>>,
bool_constant<has_unqualified_rend<T>>>,
decltype(rend(AZStd::forward<T>(t)))>
{
return rend(AZStd::forward<T>(t));
@@ -389,14 +427,20 @@ namespace AZStd::ranges
template <class T, class = void>
constexpr bool has_unqualified_size = false;
template <class T>
constexpr bool has_unqualified_size<T, void_t<decltype(size(declval<T&>()))>>
= !has_member_size<T> && AZStd::Internal::is_class_or_enum<T>;
constexpr bool has_unqualified_size<T, enable_if_t<conjunction_v<
sfinae_trigger<decltype(size(declval<T&>()))>,
bool_constant<!has_member_size<T>>,
bool_constant<::AZStd::Internal::is_class_or_enum<T>>
>>> = true;
template <class T, class = void>
constexpr bool has_end_subtract_begin = false;
template <class T>
constexpr bool has_end_subtract_begin<T, void_t<decltype(ranges::end(declval<T&>()) - ranges::begin(declval<T&>()))>>
= !has_member_size<T> && !has_unqualified_size<T>;
constexpr bool has_end_subtract_begin<T, enable_if_t<conjunction_v<
sfinae_trigger<decltype(ranges::end(declval<T&>()) - ranges::begin(declval<T&>()))>,
bool_constant<!has_member_size<T>>,
bool_constant<!has_unqualified_size<T>>
>>> = true;
template<class T>
void size(T&) = delete;
@@ -407,7 +451,7 @@ namespace AZStd::ranges
{
template<class T>
constexpr auto operator()(T&) const noexcept ->
enable_if_t<is_array_v<T> && extent_v<T> != 0,
enable_if_t<conjunction_v<is_array<T>, bool_constant<extent_v<T> != 0>>,
decltype(extent_v<T>)>
{
return extent_v<T>;
@@ -415,8 +459,9 @@ namespace AZStd::ranges
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward<T>(t).size())) ->
enable_if_t<!disable_sized_range<remove_cv_t<T>> && has_member_size<T>
&& AZStd::Internal::is_integer_like<decltype(AZStd::forward<T>(t).size())>,
enable_if_t<conjunction_v<bool_constant<!disable_sized_range<remove_cv_t<T>>>,
bool_constant<has_member_size<T>>,
bool_constant<::AZStd::Internal::is_integer_like<decltype(AZStd::forward<T>(t).size())>>>,
decltype(AZStd::forward<T>(t).size())>
{
return AZStd::forward<T>(t).size();
@@ -424,9 +469,9 @@ namespace AZStd::ranges
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(size(AZStd::forward<T>(t)))) ->
enable_if_t<!disable_sized_range<remove_cv_t<T>>
&& has_unqualified_size<T>
&& AZStd::Internal::is_integer_like<decltype(size(AZStd::forward<T>(t)))>,
enable_if_t<conjunction_v<bool_constant<!disable_sized_range<remove_cv_t<T>>>,
bool_constant<has_unqualified_size<T>>,
bool_constant<::AZStd::Internal::is_integer_like<decltype(size(AZStd::forward<T>(t)))>>>,
decltype(size(AZStd::forward<T>(t)))>
{
return size(AZStd::forward<T>(t));
@@ -434,10 +479,10 @@ namespace AZStd::ranges
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::end(AZStd::forward<T>(t)) - ranges::begin(AZStd::forward<T>(t)))) ->
enable_if_t<
has_end_subtract_begin<T>
&& sized_sentinel_for<decltype(ranges::end(AZStd::forward<T>(t))), decltype(ranges::begin(AZStd::forward<T>(t)))>
&& forward_iterator<decltype(ranges::begin(AZStd::forward<T>(t)))>,
enable_if_t<conjunction_v<
bool_constant<has_end_subtract_begin<T>>,
bool_constant<sized_sentinel_for<decltype(ranges::end(AZStd::forward<T>(t))), decltype(ranges::begin(AZStd::forward<T>(t)))>>,
bool_constant<forward_iterator<decltype(ranges::begin(AZStd::forward<T>(t)))>>>,
AZStd::make_unsigned_t<decltype(ranges::end(AZStd::forward<T>(t)) - ranges::begin(AZStd::forward<T>(t)))>>
{
using size_type = AZStd::make_unsigned_t<decltype(ranges::end(AZStd::forward<T>(t)) - ranges::begin(AZStd::forward<T>(t)))>;
@@ -456,17 +501,17 @@ namespace AZStd::ranges
struct ssize_fn
{
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::size(t))) ->
enable_if_t<(sizeof(ptrdiff_t) > sizeof(make_signed_t<decltype(ranges::size(t))>)), ptrdiff_t>
constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::size(AZStd::forward<T>(t)))) ->
enable_if_t<(sizeof(ptrdiff_t) > sizeof(make_signed_t<decltype(ranges::size(AZStd::forward<T>(t)))>)), ptrdiff_t>
{
return static_cast<ptrdiff_t>(ranges::size(t));
return static_cast<ptrdiff_t>(ranges::size(AZStd::forward<T>(t)));
}
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::size(t))) ->
enable_if_t<sizeof(ptrdiff_t) <= sizeof(make_signed_t<decltype(ranges::size(t))>), make_signed_t<decltype(ranges::size(t))>>
constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::size(AZStd::forward<T>(t)))) ->
enable_if_t<sizeof(ptrdiff_t) <= sizeof(make_signed_t<decltype(ranges::size(AZStd::forward<T>(t)))>), make_signed_t<decltype(ranges::size(t))>>
{
using ssize_type = make_signed_t<decltype(ranges::size(t))>;
using ssize_type = make_signed_t<decltype(ranges::size(AZStd::forward<T>(t)))>;
return static_cast<ssize_type>(ranges::size(t));
}
};
@@ -487,36 +532,41 @@ namespace AZStd::ranges
template <class T, class = void>
constexpr bool has_size_compare_to_0 = false;
template <class T>
constexpr bool has_size_compare_to_0<T, enable_if_t<convertible_to<decltype(ranges::size(declval<T&>()) == 0), bool> >>
= !has_member_empty<T>;
constexpr bool has_size_compare_to_0<T, enable_if_t<conjunction_v<
bool_constant<!has_member_empty<T>>,
bool_constant<convertible_to<decltype(ranges::size(declval<T&>()) == 0), bool>>
>>> = true;
template <class T, class = void>
constexpr bool has_begin_compare_to_end = false;
template <class T>
constexpr bool has_begin_compare_to_end<T,
enable_if_t<convertible_to<decltype(ranges::begin(declval<T&>()) == ranges::end(declval<T&>())), bool> >>
= !has_member_empty<T> && !has_size_compare_to_0<T>;
enable_if_t<conjunction_v<
bool_constant<!has_member_empty<T>>,
bool_constant<!has_size_compare_to_0<T>>,
bool_constant<convertible_to<decltype(ranges::begin(declval<T&>()) == ranges::end(declval<T&>())), bool>>
>>> = true;
struct empty_fn
{
template<class T>
[[nodiscard]] constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward<T>(t).empty())) ->
enable_if_t<is_lvalue_or_borrowable<T> && has_member_empty<T>, bool>
enable_if_t<conjunction_v<bool_constant<is_lvalue_or_borrowable<T>>, bool_constant<has_member_empty<T>>>, bool>
{
return AZStd::forward<T>(t).empty();
}
template<class T>
[[nodiscard]] constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::size(AZStd::forward<T>(t)) == 0)) ->
enable_if_t<is_lvalue_or_borrowable<T> && has_size_compare_to_0<T>, bool>
enable_if_t<conjunction_v<bool_constant<is_lvalue_or_borrowable<T>>, bool_constant<has_size_compare_to_0<T>>>, bool>
{
return ranges::size(AZStd::forward<T>(t)) == 0;
}
template<class T>
[[nodiscard]] constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::begin(AZStd::forward<T>(t)) == ranges::end(AZStd::forward<T>(t)))) ->
enable_if_t<is_lvalue_or_borrowable<T> && has_begin_compare_to_end<T>, bool>
enable_if_t<conjunction_v<bool_constant<is_lvalue_or_borrowable<T>>, bool_constant<has_begin_compare_to_end<T>>>, bool>
{
return ranges::begin(AZStd::forward<T>(t)) == ranges::end(AZStd::forward<T>(t));
}
@@ -538,14 +588,16 @@ namespace AZStd::ranges
template <class T, class = void>
constexpr bool has_qualified_ranges_begin = false;
template <class T>
constexpr bool has_qualified_ranges_begin<T, enable_if_t<contiguous_iterator<decltype(ranges::begin(declval<T&>()))>> >
= !has_member_data<T>;
constexpr bool has_qualified_ranges_begin<T, enable_if_t<conjunction_v<
bool_constant<!has_member_data<T>>,
bool_constant<contiguous_iterator<decltype(ranges::begin(declval<T&>()))>>
>>> = true;
struct data_fn
{
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward<T>(t).data())) ->
enable_if_t<is_lvalue_or_borrowable<T> && has_member_data<T>,
enable_if_t<conjunction_v<bool_constant<is_lvalue_or_borrowable<T>>, bool_constant<has_member_data<T>>>,
decltype(AZStd::forward<T>(t).data())>
{
return AZStd::forward<T>(t).data();
@@ -553,7 +605,7 @@ namespace AZStd::ranges
template<class T>
constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::to_address(ranges::begin(AZStd::forward<T>(t))))) ->
enable_if_t<is_lvalue_or_borrowable<T> && has_qualified_ranges_begin<T>,
enable_if_t<conjunction_v<bool_constant<is_lvalue_or_borrowable<T>>, bool_constant<has_qualified_ranges_begin<T>>>,
decltype(AZStd::to_address(ranges::begin(AZStd::forward<T>(t))))>
{
return AZStd::to_address(ranges::begin(AZStd::forward<T>(t)));
@@ -612,8 +664,8 @@ namespace AZStd::ranges
// Models borrowed range concept
template<class T>
/*concept*/ constexpr bool borrowed_range = range<T>
&& (is_lvalue_reference_v<T> || enable_borrowed_range<remove_cvref_t<T>>);
/*concept*/ constexpr bool borrowed_range = conjunction_v<bool_constant<range<T>>,
disjunction<is_lvalue_reference<T>, bool_constant<enable_borrowed_range<remove_cvref_t<T>>>>>;
struct dangling
{
@@ -622,9 +674,23 @@ namespace AZStd::ranges
constexpr dangling(T&&...) noexcept {}
};
enum class subrange_kind : bool
{
unsized,
sized
};
template<class I, class S = I,
subrange_kind K = sized_sentinel_for<S, I> ? subrange_kind::sized : subrange_kind::unsized,
class = void>
class subrange;
template<class R>
using borrowed_iterator_t = conditional_t<borrowed_range<R>, iterator_t<R>, dangling>;
template<class R>
using borrowed_subrange_t = conditional_t<borrowed_range<R>, subrange<iterator_t<R>>, dangling>;
// Models sized range concept
namespace Internal
{
@@ -643,7 +709,11 @@ namespace AZStd::ranges
template<class R, class T, class = void>
constexpr bool output_range_impl = false;
template<class R, class T>
constexpr bool output_range_impl<R, T, enable_if_t<has_iterator_t<R>>> = range<R> && output_iterator<iterator_t<R>, T>;
constexpr bool output_range_impl<R, T, enable_if_t<conjunction_v<
bool_constant<has_iterator_t<R>>,
bool_constant<range<R>>,
bool_constant<output_iterator<iterator_t<R>, T>>
>>> = true;
}
template<class R, class T>
@@ -654,7 +724,11 @@ namespace AZStd::ranges
template<class T, class = void>
constexpr bool input_range_impl = false;
template<class T>
constexpr bool input_range_impl<T, enable_if_t<has_iterator_t<T>>> = range<T> && input_iterator<iterator_t<T>>;
constexpr bool input_range_impl<T, enable_if_t<conjunction_v<
bool_constant<has_iterator_t<T>>,
bool_constant<range<T>>,
bool_constant<input_iterator<iterator_t<T>>>
>>> = true;
}
template<class T>
@@ -665,7 +739,11 @@ namespace AZStd::ranges
template<class T, class = void>
constexpr bool forward_range_impl = false;
template<class T>
constexpr bool forward_range_impl<T, enable_if_t<has_iterator_t<T>>> = input_range<T> && forward_iterator<iterator_t<T>>;
constexpr bool forward_range_impl<T, enable_if_t<conjunction_v<
bool_constant<has_iterator_t<T>>,
bool_constant<input_range<T>>,
bool_constant<forward_iterator<iterator_t<T>>>
>>> = true;
}
template<class T>
@@ -676,7 +754,11 @@ namespace AZStd::ranges
template<class T, class = void>
constexpr bool bidirectional_range_impl = false;
template<class T>
constexpr bool bidirectional_range_impl<T, enable_if_t<has_iterator_t<T>>> = forward_range<T> && bidirectional_iterator<iterator_t<T>>;
constexpr bool bidirectional_range_impl<T, enable_if_t<conjunction_v<
bool_constant<has_iterator_t<T>>,
bool_constant<forward_range<T>>,
bool_constant<bidirectional_iterator<iterator_t<T>>>
>>> = true;
}
template<class T>
@@ -687,7 +769,11 @@ namespace AZStd::ranges
template<class T, class = void>
constexpr bool random_access_range_impl = false;
template<class T>
constexpr bool random_access_range_impl<T, enable_if_t<has_iterator_t<T>>> = bidirectional_range<T> && random_access_iterator<iterator_t<T>>;
constexpr bool random_access_range_impl<T, enable_if_t<conjunction_v<
bool_constant<has_iterator_t<T>>,
bool_constant<bidirectional_range<T>>,
bool_constant<random_access_iterator<iterator_t<T>>>
>>> = true;
}
template<class T>
@@ -710,16 +796,19 @@ namespace AZStd::ranges
template<class T, class = void>
constexpr bool contiguous_range_impl = false;
template<class T>
constexpr bool contiguous_range_impl<T, enable_if_t<random_access_range<T>
&& contiguous_iterator<iterator_t<T>>
&& same_as<decltype(ranges::data(declval<T&>())), add_pointer_t<range_reference_t<T>>> >> = true;
constexpr bool contiguous_range_impl<T, enable_if_t<conjunction_v<
bool_constant<random_access_range<T>>,
bool_constant<contiguous_iterator<iterator_t<T>>>,
bool_constant<same_as<decltype(ranges::data(declval<T&>())), add_pointer_t<range_reference_t<T>>>>
>>> = true;
}
template<class T>
/*concept*/ constexpr bool contiguous_range = Internal::contiguous_range_impl<T>;
template<class T>
/*concept*/ constexpr bool common_range = range<T> && same_as<iterator_t<T>, sentinel_t<T>>;
/*concept*/ constexpr bool common_range = conjunction_v<bool_constant<range<T>>,
bool_constant<same_as<iterator_t<T>, sentinel_t<T>>>>;
}
namespace AZStd::ranges
@@ -752,7 +841,7 @@ namespace AZStd::ranges
template<class I, class S>
constexpr auto operator()(I& i, S bound) const ->
enable_if_t<input_or_output_iterator<I>&& sentinel_for<S, I>>
enable_if_t<conjunction_v<bool_constant<input_or_output_iterator<I>>, bool_constant<sentinel_for<S, I>>>>
{
if constexpr (assignable_from<I&, S>)
{
@@ -770,7 +859,8 @@ namespace AZStd::ranges
template<class I, class S>
constexpr auto operator()(I& i, iter_difference_t<I> n, S bound) const ->
enable_if_t<input_or_output_iterator<I>&& sentinel_for<S, I>, iter_difference_t<I>>
enable_if_t<conjunction_v<bool_constant<input_or_output_iterator<I>>, bool_constant<sentinel_for<S, I>>>,
iter_difference_t<I>>
{
if constexpr (sized_sentinel_for<S, I>)
{
@@ -816,7 +906,9 @@ namespace AZStd::ranges
{
template<class I, class S>
constexpr auto operator()(I first, S last) const ->
enable_if_t<input_or_output_iterator<I> && sentinel_for<S, I> && !sized_sentinel_for<S, I>,
enable_if_t<conjunction_v<bool_constant<input_or_output_iterator<I>>,
bool_constant<sentinel_for<S, I>>,
bool_constant<!sized_sentinel_for<S, I>>>,
iter_difference_t<I>>
{
// Since S is not a sized sentinel, can only increment from first to last
@@ -828,7 +920,9 @@ namespace AZStd::ranges
template<class I, class S>
constexpr auto operator()(const I& first, const S& last) const ->
enable_if_t<input_or_output_iterator<I> && sentinel_for<S, I> && sized_sentinel_for<S, I>,
enable_if_t<conjunction_v<bool_constant<input_or_output_iterator<I>>,
bool_constant<sentinel_for<S, I>>,
bool_constant<sized_sentinel_for<S, I>>>,
iter_difference_t<I>>
{
return last - first;
@@ -878,7 +972,7 @@ namespace AZStd::ranges
template<class I, class S>
constexpr auto operator()(I x, S bound) const ->
enable_if_t<input_or_output_iterator<I>&& sentinel_for<S, I>, I>
enable_if_t<conjunction_v<bool_constant<input_or_output_iterator<I>>, bool_constant<sentinel_for<S, I>>>, I>
{
ranges::advance(x, bound);
return x;
@@ -886,7 +980,7 @@ namespace AZStd::ranges
template<class I, class S>
constexpr auto operator()(I x, iter_difference_t<I> n, S bound) const ->
enable_if_t<input_or_output_iterator<I>&& sentinel_for<S, I>, I>
enable_if_t<conjunction_v<bool_constant<input_or_output_iterator<I>>, bool_constant<sentinel_for<S, I>>>, I>
{
ranges::advance(x, n, bound);
return x;
@@ -922,7 +1016,7 @@ namespace AZStd::ranges
template<class I, class S>
constexpr auto operator()(I x, iter_difference_t<I> n, S bound) const ->
enable_if_t<input_or_output_iterator<I>&& sentinel_for<S, I>, I>
enable_if_t<conjunction_v<bool_constant<input_or_output_iterator<I>>, bool_constant<sentinel_for<S, I>>>, I>
{
ranges::advance(x, -n, bound);
return x;
@@ -950,16 +1044,51 @@ namespace AZStd::ranges
// view interface can be used with non-constant class types
template<class D, class = void>
class view_interface;
struct view_base {};
namespace Internal
{
template<class D>
void derived_from_view_interface_template(view_interface<D>&);
template<class T, class = void>
inline constexpr bool is_derived_from_view_interface = false;
template<class T>
inline constexpr bool is_derived_from_view_interface<T,
decltype(derived_from_view_interface_template(declval<remove_cvref_t<T>&>()))> = true;
}
template<class T>
inline constexpr bool enable_view = derived_from<T, view_base> || Internal::is_derived_from_view_interface<T>;
template<class T>
/*concept*/ constexpr bool view = conjunction_v<bool_constant<range<T>>, bool_constant<movable<T>>, bool_constant<enable_view<T>>>;
template<class T>
/*concept*/ constexpr bool viewable_range = conjunction_v<bool_constant<range<T>>,
disjunction<
conjunction<bool_constant<view<remove_cvref_t<T>>>, bool_constant<constructible_from<remove_cvref_t<T>, T>>>,
conjunction<bool_constant<!view<remove_cvref_t<T>>>,
disjunction<is_lvalue_reference<T>,
conjunction<bool_constant<movable<remove_reference_t<T>>>,
bool_constant<!Internal::is_initializer_list<T>>
>
>
>
>
>;
template<class D>
class view_interface< D, enable_if_t<is_class_v<D> && same_as<D, remove_cv_t<D>> >>
class view_interface<D, enable_if_t<is_class_v<D> && same_as<D, remove_cv_t<D>> >>
{
private:
constexpr D& derived() noexcept
{
// Make sure that the derived type is complete
static_assert(sizeof(D) > 0 && derived_from<D, view_interface> && view<D>);
return static_cast<D&>(*this);
}
constexpr const D& derived() const noexcept
{
static_assert(sizeof(D) > 0 && derived_from<D, view_interface> && view<D>);
return static_cast<const D&>(*this);
}
@@ -975,134 +1104,141 @@ namespace AZStd::ranges
return ranges::begin(derived()) == ranges::end(derived());
}
template <class Derived = D, class = void_t<decltype(ranges::empty(declval<Derived>()))>>
constexpr explicit operator bool() const noexcept(noexcept(ranges::empty(derived())))
template <class Derived = D>
constexpr explicit operator bool() const noexcept(noexcept(ranges::empty(static_cast<const Derived&>(*this))))
{
return !ranges::empty(derived());
}
template <class Derived = D>
constexpr auto data() ->
enable_if_t<contiguous_iterator<iterator_t<Derived>>, decltype(to_address(ranges::begin(derived())))>
enable_if_t<contiguous_iterator<iterator_t<Derived>>, decltype(to_address(ranges::begin(static_cast<Derived&>(*this))))>
{
return to_address(ranges::begin(derived()));
}
template <class Derived = D>
constexpr auto data() const ->
enable_if_t<contiguous_iterator<iterator_t<const Derived>>, decltype(to_address(ranges::begin(derived())))>
enable_if_t<range<const Derived> && contiguous_iterator<iterator_t<const Derived>>,
decltype(to_address(ranges::begin(static_cast<const Derived&>(*this))))>
{
return to_address(ranges::begin(derived()));
}
template <class Derived = D>
constexpr auto size() ->
enable_if_t<forward_range<Derived> && sized_sentinel_for<sentinel_t<Derived>, iterator_t<Derived>>,
decltype(ranges::end(derived()) - ranges::begin(derived()))>
enable_if_t<conjunction_v<bool_constant<forward_range<Derived>>,
bool_constant<sized_sentinel_for<sentinel_t<Derived>, iterator_t<Derived>>>>,
decltype(ranges::end(static_cast<Derived&>(*this)) - ranges::begin(static_cast<Derived&>(*this)))>
{
return ranges::end(derived()) - ranges::begin(derived());
}
template <class Derived = D>
constexpr auto size() const ->
enable_if_t<forward_range<const Derived>&& sized_sentinel_for<sentinel_t<const Derived>, iterator_t<const Derived>>,
decltype(ranges::end(derived()) - ranges::begin(derived()))>
enable_if_t<conjunction_v<bool_constant<forward_range<const Derived>>,
bool_constant<sized_sentinel_for<sentinel_t<const Derived>, iterator_t<const Derived>>>>,
decltype(ranges::end(static_cast<const Derived&>(*this)) - ranges::begin(static_cast<const Derived&>(*this)))>
{
return ranges::end(derived()) - ranges::begin(derived());
}
template <class Derived = D>
constexpr auto front() ->
enable_if_t<forward_range<Derived>, decltype(*ranges::begin(derived()))>
enable_if_t<forward_range<Derived>, decltype(*ranges::begin(static_cast<Derived&>(*this)))>
{
return *ranges::begin(derived());
}
template <class Derived = D>
constexpr auto front() const ->
enable_if_t<forward_range<const Derived>, decltype(*ranges::begin(derived()))>
enable_if_t<forward_range<const Derived>, decltype(*ranges::begin(static_cast<const Derived&>(*this)))>
{
return *ranges::begin(derived());
}
template <class Derived = D>
constexpr auto back() ->
enable_if_t<bidirectional_range<Derived> && common_range<Derived>, decltype(*ranges::prev(ranges::end(derived())))>
enable_if_t<conjunction_v<bool_constant<bidirectional_range<Derived>>,
bool_constant<common_range<Derived>>>,
decltype(*ranges::prev(ranges::end(static_cast<Derived&>(*this))))>
{
return *ranges::prev(ranges::end(derived()));
}
template <class Derived = D>
constexpr auto back() const ->
enable_if_t<bidirectional_range<const Derived>&& common_range<const Derived>, decltype(*ranges::prev(ranges::end(derived())))>
enable_if_t<conjunction_v<bool_constant<bidirectional_range<const Derived>>,
bool_constant<common_range<const Derived>>>,
decltype(*ranges::prev(ranges::end(static_cast<const Derived&>(*this))))>
{
return *ranges::prev(ranges::end(derived()));
}
template<class R = D>
constexpr auto operator[](range_difference_t<R> n) ->
enable_if_t<random_access_range<R>, decltype(ranges::begin(derived())[n])>
enable_if_t<random_access_range<R>, decltype(ranges::begin(static_cast<R&>(*this))[n])>
{
return ranges::begin(derived())[n];
}
template<class R = const D>
constexpr auto operator[](range_difference_t<R> n) const ->
enable_if_t<random_access_range<R>, decltype(ranges::begin(derived())[n])>
enable_if_t<random_access_range<R>, decltype(ranges::begin(static_cast<R&>(*this))[n])>
{
return ranges::begin(derived())[n];
}
};
struct view_base {};
// Helper Concepts - https://eel.is/c++draft/ranges#range.utility.helpers
namespace Internal
{
template<class D>
void derived_from_view_interface_template(view_interface<D>&);
template<class T, class = void>
inline constexpr bool is_derived_from_view_interface = false;
template<class T>
inline constexpr bool is_derived_from_view_interface<T,
decltype(derived_from_view_interface_template(declval<T&>()))> = true;
// Helper concepts that are used by range adaptor classes
template<bool Const, class T>
using maybe_const = conditional_t<Const, const T, T>;
template<class R, class = void>
/*concept*/ constexpr bool simple_view = false;
template<class R>
/*concept*/ inline constexpr bool simple_view<R, enable_if_t<conjunction_v<
bool_constant<view<R>>,
bool_constant<range<const R>>,
bool_constant<same_as<iterator_t<R>, iterator_t<const R>>>,
bool_constant<same_as<sentinel_t<R>, sentinel_t<const R>>> >>> = true;
template<class I, class = void>
/*concept*/ constexpr bool has_arrow = false;
template<class I>
/*concept*/ inline constexpr bool has_arrow<I, enable_if_t<conjunction_v<
bool_constant<input_iterator<I>>,
disjunction<is_pointer<I>, sfinae_trigger<decltype(declval<I>().operator->())>>
>>> = true;
template<class T, class U>
/*concept*/ constexpr bool different_from = !same_as<remove_cvref_t<T>, remove_cvref_t<U>>;
}
template<class T>
inline constexpr bool enable_view = derived_from<T, view_base> || Internal::is_derived_from_view_interface<T>;
template<class T>
/*concept*/ constexpr bool view = range<T> && movable<T> && enable_view<T>;
template<class T>
/*concept*/ constexpr bool viewable_range = range<T> &&
((view<remove_cvref_t<T>> && constructible_from<remove_cvref_t<T>, T>) ||
(!view<remove_cvref_t<T>> &&
(is_lvalue_reference_v<T> || (movable<remove_reference_t<T>> && !Internal::is_initializer_list<T>))));
}
namespace AZStd::ranges
{
#if __has_cpp_attribute(no_unique_address)
#define az_no_unique_address [[no_unique_address]]
#else
#define az_no_unique_address
#endif
template<class I1, class I2>
struct in_in_result
{
az_no_unique_address I1 in1;
az_no_unique_address I2 in2;
AZ_NO_UNIQUE_ADDRESS I1 in1;
AZ_NO_UNIQUE_ADDRESS I2 in2;
template<class II1, class II2, class = enable_if_t<convertible_to<const I1&, II1>&& convertible_to<const I2&, II2>> >
template<class II1, class II2, class = enable_if_t<conjunction_v<
bool_constant<convertible_to<const I1&, II1>>, bool_constant<convertible_to<const I2&, II2>>>> >
constexpr operator in_in_result<II1, II2>() const&
{
return { in1, in2 };
}
template<class II1, class II2, class = enable_if_t<convertible_to<I1, II1>&& convertible_to<I2, II2>> >
template<class II1, class II2, class = enable_if_t<conjunction_v<
bool_constant<convertible_to<I1, II1>>, bool_constant<convertible_to<I2, II2>>>> >
constexpr operator in_in_result<II1, II2>()&&
{
return { AZStd::move(in1), AZStd::move(in2) };
}
};
#undef az_no_unique_address
template<class I1, class I2>
using swap_ranges_result = in_in_result<I1, I2>;
@@ -1112,9 +1248,11 @@ namespace AZStd::ranges
{
template<class I1, class S1, class I2, class S2>
constexpr auto operator()(I1 first1, S1 last1, I2 first2, S2 last2) const ->
enable_if_t<input_iterator<I1>&& sentinel_for<S1, I1>
&& input_iterator<I2>&& sentinel_for<S2, I2>
&& indirectly_swappable<I1, I2>,
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_swappable<I1, I2>>>,
swap_ranges_result<I1, I2>>
{
for (; !(first1 == last1 or first2 == last2); ++first1, ++first2)
@@ -1126,8 +1264,10 @@ namespace AZStd::ranges
template<class R1, class R2>
constexpr auto operator()(R1&& r1, R2&& r2) const ->
enable_if_t<input_range<R1>&& input_range<R2>
&& indirectly_swappable<iterator_t<R1>, iterator_t<R2>>,
enable_if_t<conjunction_v<
bool_constant<input_range<R1>>,
bool_constant<input_range<R2>>,
bool_constant<indirectly_swappable<iterator_t<R1>, iterator_t<R2>>>>,
swap_ranges_result<borrowed_iterator_t<R1>, borrowed_iterator_t<R2>>>
{
return operator()(ranges::begin(r1), ranges::end(r1),
@@ -1148,10 +1288,21 @@ namespace AZStd::ranges::Internal
// ranges::swap customization point https://eel.is/c++draft/concepts#concept.swappable-2.2
template <class T, class U>
constexpr auto swap_fn::operator()(T&& t, U&& u) const noexcept(noexcept((*this)(*t, *u)))
->enable_if_t<!is_class_or_enum_with_swap_adl<T, U>
&& is_array_v<T> && is_array_v<U> && (extent_v<T> == extent_v<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>>
>>
{
ranges::swap_ranges(t, u);
}
}
// Opening AZStd::ranges::views namespace to provide access to it in AZStd
namespace AZStd::ranges::views{}
namespace AZStd
{
namespace views = ranges::views;
}
@@ -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
@@ -603,6 +603,23 @@ namespace AZStd
template <class Element, class Traits = AZStd::char_traits<Element>>
class basic_string_view
{
template <class R, class = void>
static constexpr bool has_operator_basic_string_view = false;
template <class R>
static constexpr bool has_operator_basic_string_view<R, void_t<
decltype(declval<remove_cvref_t<R>>().operator basic_string_view<Element, Traits>())
>> = true;
// If the range has a traits_type element, it must match
template <class R, class = void>
static constexpr bool range_trait_type_matches = true;
template <class R>
static constexpr bool range_trait_type_matches<R,
enable_if_t<conjunction_v<
Internal::sfinae_trigger<typename remove_reference_t<R>::traits_type>,
bool_constant<!same_as<typename remove_reference_t<R>::traits_type, Traits>>
>>> = false;
public:
using traits_type = Traits;
using value_type = Element;
@@ -646,6 +663,25 @@ namespace AZStd
, m_size(AZStd::to_address(last) - AZStd::to_address(first))
{}
// double SFINAE is used to defer evaluation of the contiguous range
// until after validating the input type isn't basic_string_view
template <typename R,
typename = enable_if_t<conjunction_v<
bool_constant<!same_as<remove_cvref_t<R>, basic_string_view>>,
bool_constant<!convertible_to<const_pointer, R>>
>>,
typename = enable_if_t<conjunction_v<
bool_constant<ranges::contiguous_range<R>>,
bool_constant<ranges::sized_range<R>>,
bool_constant<same_as<ranges::range_value_t<R>, value_type>>,
bool_constant<!has_operator_basic_string_view<R>>,
bool_constant<range_trait_type_matches<R>>
>>>
constexpr basic_string_view(R&& r)
: m_begin(ranges::data(r))
, m_size(ranges::size(r))
{}
constexpr basic_string_view(const basic_string_view&) noexcept = default;
constexpr basic_string_view(basic_string_view&& other)
{
@@ -982,6 +1018,11 @@ namespace AZStd
size_type m_size{};
};
template<class It, class End>
basic_string_view(It, End)->basic_string_view<iter_value_t<It>>;
template<class R>
basic_string_view(R&&)->basic_string_view<ranges::range_value_t<R>>;
using string_view = basic_string_view<char>;
using wstring_view = basic_string_view<wchar_t>;
+6 -14
View File
@@ -19,22 +19,14 @@
namespace AZStd
{
template<class... Types>
using tuple = std::tuple<Types...>;
template<class T>
using tuple_size = std::tuple_size<T>;
template<size_t I, class T>
using tuple_element = std::tuple_element<I, T>;
template<size_t I, class T>
using tuple_element_t = typename std::tuple_element<I, T>::type;
using std::tuple;
using std::tuple_size;
using std::tuple_size_v;
using std::tuple_element;
using std::tuple_element_t;
// Placeholder structure that can be assigned any value with no effect.
// This is used by AZStd::tie as placeholder for unused arugments
using ignore_t = AZStd::decay_t<decltype(std::ignore)>;
decltype(std::ignore) ignore = std::ignore;
using std::ignore;
using std::make_tuple;
using std::tie;
@@ -7,30 +7,10 @@
*/
#pragma once
#include <AzCore/std/typetraits/typetraits.h>
#include <AzCore/std/typetraits/conditional.h>
#include <AzCore/std/typetraits/integral_constant.h> // for true_type
#include <type_traits>
namespace AZStd
{
template<class... Bases>
struct conjunction
: true_type
{
};
template<class B1>
struct conjunction<B1>
: B1
{
};
template<class B1, class... Bases>
struct conjunction<B1, Bases...>
: conditional_t<!B1::value, B1, conjunction<Bases...>>
{
};
template<class... Bases>
constexpr bool conjunction_v = conjunction<Bases...>::value;
using std::conjunction;
using std::conjunction_v;
} // namespace AZStd
@@ -7,29 +7,10 @@
*/
#pragma once
#include <AzCore/std/typetraits/integral_constant.h>
#include <AzCore/std/typetraits/conditional.h>
#include <type_traits>
namespace AZStd
{
template<class... Bases>
struct disjunction
: false_type
{
};
template<class B1>
struct disjunction<B1>
: B1
{
};
template<class B1, class... Bases>
struct disjunction<B1, Bases...>
: conditional_t<B1::value, B1, disjunction<Bases...>>
{
};
template<class... Bases>
constexpr bool disjunction_v = disjunction<Bases...>::value;
using std::disjunction;
using std::disjunction_v;
}
@@ -13,49 +13,30 @@
namespace AZStd
{
using std::is_constructible;
using std::is_constructible_v;
using std::is_trivially_constructible;
using std::is_trivially_constructible_v;
using std::is_nothrow_constructible;
using std::is_nothrow_constructible_v;
using std::is_default_constructible;
using std::is_default_constructible_v;
using std::is_trivially_default_constructible;
using std::is_trivially_default_constructible_v;
using std::is_nothrow_default_constructible;
using std::is_nothrow_default_constructible_v;
using std::is_copy_constructible;
using std::is_copy_constructible_v;
using std::is_trivially_copy_constructible;
using std::is_trivially_copy_constructible_v;
using std::is_nothrow_copy_constructible;
using std::is_nothrow_copy_constructible_v;
using std::is_move_constructible;
using std::is_move_constructible_v;
using std::is_trivially_move_constructible;
using std::is_trivially_move_constructible_v;
using std::is_nothrow_move_constructible;
template<class T, class... Args>
constexpr bool is_constructible_v = std::is_constructible<T, Args...>::value;
template<class T, class... Args>
constexpr bool is_trivially_constructible_v = std::is_trivially_constructible<T, Args...>::value;
template<class T, class... Args>
constexpr bool is_nothrow_constructible_v = std::is_nothrow_constructible<T, Args...>::value;
template<class T>
constexpr bool is_default_constructible_v = std::is_default_constructible<T>::value;
template<class T>
constexpr bool is_trivially_default_constructible_v = std::is_trivially_default_constructible<T>::value;
template<class T>
constexpr bool is_nothrow_default_constructible_v = std::is_nothrow_default_constructible<T>::value;
template<class T>
constexpr bool is_copy_constructible_v = std::is_copy_constructible<T>::value;
template<class T>
constexpr bool is_trivially_copy_constructible_v = std::is_trivially_copy_constructible<T>::value;
template<class T>
constexpr bool is_nothrow_copy_constructible_v = std::is_nothrow_copy_constructible<T>::value;
template<class T>
constexpr bool is_move_constructible_v = std::is_move_constructible<T>::value;
template<class T>
constexpr bool is_trivially_move_constructible_v = std::is_trivially_move_constructible<T>::value;
template<class T>
constexpr bool is_nothrow_move_constructible_v = std::is_nothrow_move_constructible<T>::value;
using std::is_nothrow_move_constructible_v;
}
@@ -7,16 +7,10 @@
*/
#pragma once
#include <AzCore/std/typetraits/integral_constant.h>
#include <type_traits>
namespace AZStd
{
template<class B>
struct negation
: integral_constant<bool, !B::value>
{
};
template<class B>
constexpr bool negation_v = negation<B>::value;
using std::negation;
using std::negation_v;
}
+12 -102
View File
@@ -92,36 +92,13 @@ namespace AZStd
//////////////////////////////////////////////////////////////////////////
// The structure that encapsulates index lists
template <size_t... Is>
struct index_sequence
{
static constexpr size_t size = sizeof...(Is);
};
// Collects internal details for generating index ranges [MIN, MAX)
namespace Internal
{
// Declare primary template for index range builder
template <size_t MIN, size_t N, size_t... Is>
struct range_builder;
// Base step
template <size_t MIN, size_t... Is>
struct range_builder<MIN, MIN, Is...>
{
typedef index_sequence<Is...> type;
};
// Induction step
template <size_t MIN, size_t N, size_t... Is>
struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
{
};
}
using std::integer_sequence;
using std::index_sequence;
using std::index_sequence_for;
// Create index range [0,N]
template<size_t N>
using make_index_sequence = typename Internal::range_builder<0, N>::type;
using std::make_index_sequence;
using std::make_integer_sequence;
struct piecewise_construct_t {};
static constexpr piecewise_construct_t piecewise_construct{};
@@ -279,31 +256,7 @@ namespace AZStd
//////////////////////////////////////////////////////////////////////////
// Address of
//////////////////////////////////////////////////////////////////////////
namespace Internal
{
template<class T>
struct addr_impl_ref
{
T& m_v;
constexpr addr_impl_ref(T& v)
: m_v(v) {}
constexpr addr_impl_ref& operator=(const addr_impl_ref& v) { m_v = v; return *this; }
constexpr operator T& () const { return m_v; }
};
template<class T>
struct addressof_impl
{
static AZ_FORCE_INLINE T* f(T& v, long) { return reinterpret_cast<T*>(&const_cast<char&>(reinterpret_cast<const volatile char&>(v))); }
static constexpr T* f(T* v, int) { return v; }
};
}
template<class T>
T* addressof(T& v)
{
return Internal::addressof_impl<T>::f(Internal::addr_impl_ref<T>(v), 0);
}
using std::addressof;
// End addressof
//////////////////////////////////////////////////////////////////////////
@@ -446,57 +399,14 @@ namespace AZStd
};
#endif
template <template <class> class, typename...>
struct sequence_and;
using std::in_place_t;
using std::in_place;
template <template <class> class trait, typename T1, typename... Ts>
struct sequence_and<trait, T1, Ts...>
{
static const bool value = trait<T1>::value && sequence_and<trait, Ts...>::value;
};
using std::in_place_type_t;
using std::in_place_type;
template <template <class> class trait>
struct sequence_and<trait>
{
static const bool value = true;
};
template <template <class> class, typename...>
struct sequence_or;
template <template <class> class trait, typename T1, typename... Ts>
struct sequence_or<trait, T1, Ts...>
{
static const bool value = trait<T1>::value || sequence_or<trait, Ts...>::value;
};
template <template <class> class trait>
struct sequence_or<trait>
{
static const bool value = false;
};
struct in_place_t
{
explicit constexpr in_place_t() = default;
};
constexpr in_place_t in_place{};
template<typename T>
struct in_place_type_t
{
explicit constexpr in_place_type_t() = default;
};
template <typename T>
constexpr in_place_type_t<T> in_place_type{};
template<size_t I>
struct in_place_index_t
{
explicit constexpr in_place_index_t() = default;
};
template<size_t I>
constexpr in_place_index_t<I> in_place_index{};
using std::in_place_index_t;
using std::in_place_index;
namespace Internal
{
@@ -8,6 +8,7 @@
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/concepts/concepts.h>
#include <AzCore/std/ranges/ranges_functional.h>
namespace UnitTest
{
@@ -199,4 +200,122 @@ namespace UnitTest
static_assert(!AZStd::strict_weak_order<RelationPredicate, int, Base>);
static_assert(!AZStd::strict_weak_order<RelationPredicate, Base, int>);
}
TEST_F(ConceptsTestFixture, IteratorInvocableConcepts)
{
// concept indirectly unary invocable
// i.e Is deferencing an iterator like type and invoking
// a unary callable a well formed expression
auto CharUnaryCallable = [](const char) -> int { return {}; };
auto IntUnaryCallable = [](int) -> int { return {}; };
auto IntRefUnaryCallable = [](int&) -> int { return {}; };
static_assert(AZStd::indirectly_unary_invocable<decltype(CharUnaryCallable), AZStd::string_view::iterator>);
static_assert(AZStd::indirectly_unary_invocable<decltype(IntUnaryCallable), AZStd::string_view::iterator>);
static_assert(!AZStd::indirectly_unary_invocable<decltype(IntRefUnaryCallable), AZStd::string_view::iterator>);
// concept indirectly regular unary invocable
// i.e Is deferencing an iterator like type and invoking with a unary callable
// which will not modify the input arguments(hence the term "regular") a well formed expression
static_assert(AZStd::indirectly_regular_unary_invocable<decltype(CharUnaryCallable), AZStd::string_view::iterator>);
static_assert(AZStd::indirectly_regular_unary_invocable<decltype(IntUnaryCallable), AZStd::string_view::iterator>);
static_assert(!AZStd::indirectly_regular_unary_invocable<decltype(IntRefUnaryCallable), AZStd::string_view::iterator>);
// concept indirect unary predicate
// i.e Is deferencing an iterator like type and invoking with a unary predicate
// i.e which is a callable that accepts one argument and returns value testable in a boolean context
auto CharUnaryPredicate = [](const char) -> bool { return {}; };
auto IntUnaryPredicate = [](int) -> int { return {}; };// Return value is an int which is convertible to bool
auto IntRefUnaryPredicate = [](int&) -> int { return {}; }; // string_view iterator value type(char) can't bind to an int&
auto CharUnaryNonPredicate = [](const char) -> AZStd::string_view { return {}; }; // string_view is not convertible to bool
static_assert(AZStd::indirect_unary_predicate<decltype(CharUnaryPredicate), AZStd::string_view::iterator>);
static_assert(AZStd::indirect_unary_predicate<decltype(IntUnaryPredicate), AZStd::string_view::iterator>);
static_assert(!AZStd::indirect_unary_predicate<decltype(IntRefUnaryPredicate), AZStd::string_view::iterator>);
static_assert(!AZStd::indirect_unary_predicate<decltype(CharUnaryNonPredicate), AZStd::string_view::iterator>);
// concept indirect binary predicate
// i.e Is deferencing two iterator like types and invoking a binary predicate with those values
// well formed and returns value testable in a boolean context.
auto CharIntBinaryPredicate = [](const char, int) -> bool { return{}; };
auto CharCharRefBinaryPredicate = [](const char, const char&) -> uint32_t { return{}; };
auto UIntRefCharBinaryPredicate = [](uint32_t&, char) -> bool { return{}; };
auto CharCharBinaryNonPredicate = [](const char, const char) -> AZStd::string_view { return {}; };
static_assert(AZStd::indirect_binary_predicate<decltype(CharIntBinaryPredicate),
AZStd::string_view::iterator, AZStd::string_view::iterator>);
static_assert(AZStd::indirect_binary_predicate<decltype(CharCharRefBinaryPredicate),
AZStd::string_view::iterator, AZStd::string_view::iterator>);
// string_view iterator value type(char) cannot bind to int&
static_assert(!AZStd::indirect_binary_predicate<decltype(UIntRefCharBinaryPredicate),
AZStd::string_view::iterator, AZStd::string_view::iterator>);
// string_view is not convertible to bool
static_assert(!AZStd::indirect_binary_predicate<decltype(CharCharBinaryNonPredicate),
AZStd::string_view::iterator, AZStd::string_view::iterator>);
// Ok - iter_reference_t<uint32_t*> = uint32_t&
static_assert(AZStd::indirect_binary_predicate<decltype(UIntRefCharBinaryPredicate),
uint32_t*, AZStd::string_view::iterator>);
// concept indirect equivalence relation
// i.e Is deferencing two iterator like types and invoking a binary predicate with those values
// well formed and returns value testable in a boolean context.
// The dereferenced iterator types should be model an equivalence relationship
// (a == b) && (b == c) == (a ==c)
static_assert(AZStd::indirect_equivalence_relation<decltype(CharIntBinaryPredicate),
AZStd::string_view::iterator, AZStd::string_view::iterator>);
static_assert(AZStd::indirect_equivalence_relation<decltype(CharCharRefBinaryPredicate),
AZStd::string_view::iterator, AZStd::string_view::iterator>);
static_assert(!AZStd::indirect_equivalence_relation<decltype(UIntRefCharBinaryPredicate),
AZStd::string_view::iterator, AZStd::string_view::iterator>);
static_assert(!AZStd::indirect_equivalence_relation<decltype(CharCharBinaryNonPredicate),
AZStd::string_view::iterator, AZStd::string_view::iterator>);
// The "relation" concept requires that both both arguments can be bind to
// either of the two binary parameters
static_assert(!AZStd::indirect_equivalence_relation<decltype(UIntRefCharBinaryPredicate),
uint32_t*, AZStd::string_view::iterator>);
// concept indirect strict weak order
// i.e Is deferencing two iterator like types and invoking a binary predicate with those values
// well formed and returns value testable in a boolean context.
// The dereferenced iterator types should be model a strict weak order relation
// (a < b) && (b < c) == (a < c)
static_assert(AZStd::indirect_strict_weak_order<decltype(CharIntBinaryPredicate),
AZStd::string_view::iterator, AZStd::string_view::iterator>);
static_assert(AZStd::indirect_strict_weak_order<decltype(CharCharRefBinaryPredicate),
AZStd::string_view::iterator, AZStd::string_view::iterator>);
static_assert(!AZStd::indirect_strict_weak_order<decltype(UIntRefCharBinaryPredicate),
AZStd::string_view::iterator, AZStd::string_view::iterator>);
static_assert(!AZStd::indirect_strict_weak_order<decltype(CharCharBinaryNonPredicate),
AZStd::string_view::iterator, AZStd::string_view::iterator>);
// The "relation" concept requires that both both arguments can be bind to
// either of the two binary parameters
static_assert(!AZStd::indirect_strict_weak_order<decltype(UIntRefCharBinaryPredicate),
uint32_t*, AZStd::string_view::iterator>);
// indirect_result_t type alias
static_assert(AZStd::same_as<AZStd::indirect_result_t<decltype(CharCharRefBinaryPredicate),
AZStd::string_view::iterator, const char*>, uint32_t>);
// projected operator* returns indirect result of the projection function
static_assert(AZStd::same_as<AZStd::iter_reference_t<AZStd::projected<int*, AZStd::identity>>, int&>);
}
TEST_F(ConceptsTestFixture, IteratorAlgorithmConcepts)
{
static_assert(AZStd::indirectly_swappable<int*, int*>);
static_assert(!AZStd::indirectly_swappable<int*, const int*>);
auto CharIntIndirectlyComparable = [](const char lhs, int rhs) -> bool { return lhs == rhs; };
static_assert(AZStd::indirectly_comparable<const char*, int*, decltype(CharIntIndirectlyComparable)>);
static_assert(!AZStd::indirectly_comparable<AZStd::string_view, int*, decltype(CharIntIndirectlyComparable)>);
static_assert(AZStd::permutable<typename AZStd::vector<int>::iterator>);
// const iterator isn't indirectlly swappable or indirectly movable
static_assert(!AZStd::permutable<typename AZStd::vector<int>::const_iterator>);
static_assert(AZStd::mergeable<AZStd::vector<int>::iterator, AZStd::string_view::iterator, AZStd::vector<int>::iterator>);
static_assert(!AZStd::mergeable<AZStd::vector<int>::iterator, AZStd::string_view::iterator, AZStd::string_view::iterator>);
static_assert(AZStd::sortable<int*>);
// Not sortable becaue the iter_reference_t<const int*> = const int& which isn't swappable
static_assert(!AZStd::sortable<const int*>);
}
}
@@ -0,0 +1,250 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/string/string_view.h>
#include <AzCore/std/ranges/ranges_algorithm.h>
namespace UnitTest
{
class RangesAlgorithmTestFixture
: public ScopedAllocatorSetupFixture
{};
// range algorithm min and max
TEST_F(RangesAlgorithmTestFixture, RangesMin_ReturnsSmallestElement)
{
// Simple Elements
EXPECT_EQ(-1, AZStd::ranges::min(5, -1));
EXPECT_EQ(78235, AZStd::ranges::min(78235, 124785));
EXPECT_EQ(7, AZStd::ranges::min(7, 7));
// Initializer list
AZStd::initializer_list<int> testIList{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
EXPECT_EQ(-8, AZStd::ranges::min(testIList));
// Range
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
EXPECT_EQ(-8, AZStd::ranges::min(testVector));
}
TEST_F(RangesAlgorithmTestFixture, RangesMax_ReturnsLargestElement)
{
// Simple Elements
EXPECT_EQ(5, AZStd::ranges::max(5, -1));
EXPECT_EQ(124785, AZStd::ranges::max(78235, 124785));
EXPECT_EQ(7, AZStd::ranges::max(7, 7));
// Initializer list
AZStd::initializer_list<int> testIList{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
EXPECT_EQ(1000, AZStd::ranges::max(testIList));
// Range
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
EXPECT_EQ(1000, AZStd::ranges::max(testVector));
}
TEST_F(RangesAlgorithmTestFixture, RangesMinMax_ReturnsSmallestAndLargestValue)
{
// Simple Elements
AZStd::ranges::minmax_result<int> expectedMinMax{ -1, 5 };
AZStd::ranges::minmax_result<int> testMinMax = AZStd::ranges::minmax(5, -1);
EXPECT_EQ(expectedMinMax.min, testMinMax.min);
EXPECT_EQ(expectedMinMax.max, testMinMax.max);
expectedMinMax = { 78235, 124785 };
testMinMax = AZStd::ranges::minmax(78235, 124785);
EXPECT_EQ(expectedMinMax.min, testMinMax.min);
EXPECT_EQ(expectedMinMax.max, testMinMax.max);
expectedMinMax = { 7, 7 };
testMinMax = AZStd::ranges::minmax(7, 7);
EXPECT_EQ(expectedMinMax.min, testMinMax.min);
EXPECT_EQ(expectedMinMax.max, testMinMax.max);
// Initializer list
AZStd::initializer_list<int> testIList{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
expectedMinMax = { -8, 1000 };
testMinMax = AZStd::ranges::minmax(testIList);
EXPECT_EQ(expectedMinMax.min, testMinMax.min);
EXPECT_EQ(expectedMinMax.max, testMinMax.max);
// Range
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
expectedMinMax = { -8, 1000 };
testMinMax = AZStd::ranges::minmax(testVector);
EXPECT_EQ(expectedMinMax.min, testMinMax.min);
EXPECT_EQ(expectedMinMax.max, testMinMax.max);
}
TEST_F(RangesAlgorithmTestFixture, RangesMinElement_ReturnsIteratorToLeftmostSmallestElement)
{
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
// iterator
auto leftmostSmallestIt = AZStd::ranges::min_element(testVector.begin(), testVector.end());
ASSERT_EQ(testVector.begin() + 4, leftmostSmallestIt);
EXPECT_EQ(-8, *leftmostSmallestIt);
// Range
leftmostSmallestIt = AZStd::ranges::min_element(testVector);
ASSERT_EQ(testVector.begin() + 4, leftmostSmallestIt);
EXPECT_EQ(-8, *leftmostSmallestIt);
}
TEST_F(RangesAlgorithmTestFixture, RangesMaxElement_ReturnsIteratorToLeftmostLargestElement)
{
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
// iterator
auto leftmostLargestIt = AZStd::ranges::max_element(testVector.begin(), testVector.end());
ASSERT_EQ(testVector.begin() + 6, leftmostLargestIt);
EXPECT_EQ(1000, *leftmostLargestIt);
// Range
leftmostLargestIt = AZStd::ranges::max_element(testVector);
ASSERT_EQ(testVector.begin() + 6, leftmostLargestIt);
EXPECT_EQ(1000, *leftmostLargestIt);
}
TEST_F(RangesAlgorithmTestFixture, RangesMinMaxElement_ReturnsIteratorToLeftmostSmallestElement_IteratorToRightmostLargestElement)
{
// The behavior of minmax_element is explicitly different from max_element
// as it relates to the max element being returned
// mixmax_element returns the rightmost largest element(assuming comparison function object is ranges::less)
// https://eel.is/c++draft/algorithms#footnoteref-225
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
// iterator
{
auto [leftmostSmallestIt, rightmostLargestIt] = AZStd::ranges::minmax_element(testVector.begin(), testVector.end());
ASSERT_EQ(testVector.begin() + 4, leftmostSmallestIt);
ASSERT_EQ(testVector.begin() + 10, rightmostLargestIt);
EXPECT_EQ(-8, *leftmostSmallestIt);
EXPECT_EQ(1000, *rightmostLargestIt);
}
// Range
auto [leftmostSmallestIt, rightmostLargestIt] = AZStd::ranges::minmax_element(testVector);
ASSERT_EQ(testVector.begin() + 4, leftmostSmallestIt);
ASSERT_EQ(testVector.begin() + 10, rightmostLargestIt);
EXPECT_EQ(-8, *leftmostSmallestIt);
EXPECT_EQ(1000, *rightmostLargestIt);
}
TEST_F(RangesAlgorithmTestFixture, RangesFind_LocatesElementInContainer_Succeeds)
{
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
auto foundIt = AZStd::ranges::find(testVector, 22);
ASSERT_NE(testVector.end(), foundIt);
EXPECT_EQ(22, *foundIt);
foundIt = AZStd::ranges::find_if(testVector, [](int value) { return value < 0; });
ASSERT_NE(testVector.end(), foundIt);
EXPECT_EQ(-8, *foundIt);
foundIt = AZStd::ranges::find_if_not(testVector, [](int value) { return value < 1000; });
ASSERT_NE(testVector.end(), foundIt);
EXPECT_EQ(1000, *foundIt);
}
TEST_F(RangesAlgorithmTestFixture, RangesFindFirstOf_LocatesElementInContainer_Succeeds)
{
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, 1000, 45 };
AZStd::array testArray{ -8, 47 };
auto foundIt = AZStd::ranges::find_first_of(testVector, testArray);
ASSERT_NE(testVector.end(), foundIt);
EXPECT_EQ(47, *foundIt);
AZStd::array<int, 0> emptyArray{};
foundIt = AZStd::ranges::find_first_of(testVector, emptyArray);
EXPECT_EQ(testVector.end(), foundIt);
}
TEST_F(RangesAlgorithmTestFixture, RangesSearch_LocatesElementInContainer_Succeeds)
{
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, -8, 1000, 45 };
AZStd::array testArray{ 1000 };
auto testSubrange = AZStd::ranges::search(testVector, testArray);
ASSERT_FALSE(testSubrange.empty());
EXPECT_EQ(1000, testSubrange.front());
AZStd::array testArray2{ 1000, 45 };
testSubrange = AZStd::ranges::search(testVector, testArray2);
ASSERT_EQ(2, testSubrange.size());
EXPECT_EQ(1000, testSubrange[0]);
EXPECT_EQ(45, testSubrange[1]);
testSubrange = AZStd::ranges::search_n(testVector, 1, 22);
ASSERT_FALSE(testSubrange.empty());
EXPECT_EQ(22, testSubrange.front());
// 2 Consecutive values of 22 does not exist in vector
testSubrange = AZStd::ranges::search_n(testVector, 2, 22);
EXPECT_TRUE(testSubrange.empty());
// 2 Consecutive values of -8 does exist in vector
testSubrange = AZStd::ranges::search_n(testVector, 2,- 8);
ASSERT_EQ(2, testSubrange.size());
EXPECT_EQ(-8, testSubrange[0]);
EXPECT_EQ(-8, testSubrange[1]);
}
TEST_F(RangesAlgorithmTestFixture, RangesFindEnd_LocatesElementLastElement_Succeeds)
{
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, -8, 1000, 45 };
AZStd::array testArray{ -8 };
auto testSubrange = AZStd::ranges::find_end(testVector, testArray);
ASSERT_FALSE(testSubrange.empty());
EXPECT_EQ(-8, testSubrange.front());
EXPECT_EQ(testVector.end() - 3, testSubrange.begin());
}
TEST_F(RangesAlgorithmTestFixture, RangesEqual_IsAbleToCompareTwoRanges_Succeeds)
{
AZStd::vector testVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, -8, 1000, 45 };
AZStd::vector longerVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, -8, 1000, 45, 929 };
AZStd::vector shorterVector{ 5, 1, 22, 47, -8, -5, 1000, 687, 22, -8, -8, 1000 };
AZStd::vector unequalVector{ 5, 1, 22, 47, -8, -5, 1000, 14, 22, -8, -8, 1000, 25 };
EXPECT_TRUE(AZStd::ranges::equal(testVector, testVector));
EXPECT_FALSE(AZStd::ranges::equal(testVector, longerVector));
EXPECT_FALSE(AZStd::ranges::equal(longerVector, testVector));
EXPECT_FALSE(AZStd::ranges::equal(testVector, shorterVector));
EXPECT_FALSE(AZStd::ranges::equal(shorterVector, testVector));
EXPECT_FALSE(AZStd::ranges::equal(testVector, unequalVector));
}
TEST_F(RangesAlgorithmTestFixture, RangesMismatch_Returns_IteratorsToMismatchElements)
{
AZStd::vector testVector{ 1, 2, 3, 4, 5 ,6 };
AZStd::vector secondVector{ 1, 2, 3, 14, 5, 6 };
AZStd::vector<int> emptyVector;
AZStd::vector<int> longerVector{ 1, 2, 3, 4, 5, 6, 7 };
{
auto [mismatchIt1, mismatchIt2] = AZStd::ranges::mismatch(testVector, secondVector);
ASSERT_NE(testVector.end(), mismatchIt1);
EXPECT_EQ(4, *mismatchIt1);
ASSERT_NE(secondVector.end(), mismatchIt2);
EXPECT_EQ(14, *mismatchIt2);
}
{
auto [mismatchIt1, mismatchIt2] = AZStd::ranges::mismatch(testVector, emptyVector);
ASSERT_NE(testVector.end(), mismatchIt1);
EXPECT_EQ(1, *mismatchIt1);
EXPECT_EQ(emptyVector.end(), mismatchIt2);
}
{
auto [mismatchIt1, mismatchIt2] = AZStd::ranges::mismatch(testVector, longerVector);
EXPECT_EQ(testVector.end(), mismatchIt1);
ASSERT_NE(longerVector.end(), mismatchIt2);
EXPECT_EQ(7, *mismatchIt2);
}
}
}
@@ -0,0 +1,483 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/containers/list.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/ranges/all_view.h>
#include <AzCore/std/ranges/elements_view.h>
#include <AzCore/std/ranges/empty_view.h>
#include <AzCore/std/ranges/join_view.h>
#include <AzCore/std/ranges/ranges_adaptor.h>
#include <AzCore/std/ranges/single_view.h>
#include <AzCore/std/ranges/split_view.h>
#include <AzCore/std/ranges/subrange.h>
#include <AzCore/std/ranges/zip_view.h>
#include <AzCore/std/string/string_view.h>
namespace UnitTest
{
class RangesViewTestFixture
: public ScopedAllocatorSetupFixture
{};
TEST_F(RangesViewTestFixture, AllRangeAdaptor_Succeeds)
{
AZStd::string_view testString{ "Hello World" };
auto testAllView = AZStd::ranges::views::all(testString);
EXPECT_EQ(testString.size(), testAllView.size());
EXPECT_EQ(testString.data(), testAllView.data());
EXPECT_EQ(testString.begin(), testAllView.begin());
EXPECT_EQ(testString.end(), testAllView.end());
EXPECT_EQ(testString.empty(), testAllView.empty());
EXPECT_EQ(testString.front(), testAllView.front());
EXPECT_EQ(testString.back(), testAllView.back());
EXPECT_EQ(testString[5], testAllView[5]);
auto testAllViewChain = testString | AZStd::ranges::views::all;
EXPECT_EQ(testString.size(), testAllViewChain.size());
EXPECT_EQ(testString.data(), testAllViewChain.data());
EXPECT_EQ(testString.begin(), testAllViewChain.begin());
EXPECT_EQ(testString.end(), testAllViewChain.end());
EXPECT_EQ(testString.empty(), testAllViewChain.empty());
EXPECT_EQ(testString.front(), testAllViewChain.front());
EXPECT_EQ(testString.back(), testAllViewChain.back());
EXPECT_EQ(testString[5], testAllViewChain[5]);
}
TEST_F(RangesViewTestFixture, Subrange_DeductionGuides_Compile)
{
AZStd::string_view testString{ "Hello World" };
AZStd::ranges::subrange rangeDeduction(testString);
AZStd::ranges::subrange rangeDeductionWithSize(testString, testString.size());
AZStd::ranges::subrange iteratorDeduction(testString.begin(), testString.end());
AZStd::ranges::subrange iteratorDeductionWithSize(testString.begin(), testString.end(),
testString.size());
EXPECT_TRUE(rangeDeduction);
EXPECT_TRUE(rangeDeductionWithSize);
EXPECT_TRUE(iteratorDeduction);
EXPECT_TRUE(iteratorDeductionWithSize);
}
TEST_F(RangesViewTestFixture, Subrange_CanTakeSubsetOfContainer_Succeeds)
{
AZStd::vector<int> testVector{ 1, 3, 5, 6, 7, 6, 89, -178 };
AZStd::ranges::subrange subVector(testVector);
EXPECT_TRUE(subVector);
ASSERT_FALSE(subVector.empty());
EXPECT_EQ(testVector.data(), subVector.data());
EXPECT_EQ(testVector.begin(), subVector.begin());
EXPECT_EQ(testVector.end(), subVector.end());
EXPECT_EQ(testVector.size(), subVector.size());
EXPECT_EQ(testVector[0], subVector[0]);
EXPECT_EQ(testVector.front(), subVector.front());
EXPECT_EQ(testVector.back(), subVector.back());
// Now validate the iterator operations
subVector.advance(2);
subVector = subVector.prev();
subVector.advance(2);
subVector = subVector.next();
subVector.advance(-4);
EXPECT_EQ(testVector.begin(), subVector.begin());
// Obtain a copy of the subrange with the first and last elements removed
AZStd::ranges::subrange subVectorSplice(subVector.begin() + 1, subVector.end() - 1);
EXPECT_TRUE(subVectorSplice);
ASSERT_FALSE(subVector.empty());
EXPECT_LT(testVector.data(), subVectorSplice.data());
EXPECT_EQ(testVector.begin() + 1, subVectorSplice.begin());
EXPECT_EQ(testVector.end() - 1, subVectorSplice.end());
ASSERT_EQ(testVector.size() - 2, subVectorSplice.size());
EXPECT_EQ(testVector[1], subVectorSplice.front());
EXPECT_EQ(testVector[testVector.size() - 2], subVectorSplice.back());
EXPECT_NE(testVector.front(), subVectorSplice.front());
EXPECT_NE(testVector.back(), subVectorSplice.back());
}
TEST_F(RangesViewTestFixture, EmptyView_ReturnsEmptyViewRange_Succeeds)
{
AZStd::ranges::empty_view<int> emptyView;
EXPECT_EQ(nullptr, emptyView.data());
EXPECT_EQ(0, emptyView.size());
EXPECT_TRUE(emptyView.empty());
EXPECT_EQ(emptyView.end(), emptyView.begin());
EXPECT_EQ(nullptr, AZStd::ranges::views::empty<AZStd::string_view>.data());
EXPECT_EQ(0, AZStd::ranges::views::empty<AZStd::string_view>.size());
EXPECT_TRUE(AZStd::ranges::views::empty<AZStd::string_view>.empty());
EXPECT_EQ(AZStd::ranges::views::empty<AZStd::string_view>.end(), AZStd::ranges::views::empty<AZStd::string_view>.begin());
}
TEST_F(RangesViewTestFixture, SingleView_ReturnsViewOverSingleElement_Succeeds)
{
AZStd::string_view testString{ "Hello World" };
AZStd::ranges::single_view singleView{ AZStd::move(testString) };
EXPECT_NE(nullptr, singleView.data());
EXPECT_EQ(1, singleView.size());
EXPECT_FALSE(singleView.empty());
ASSERT_NE(singleView.end(), singleView.begin());
auto singleViewStringIt = singleView.begin();
EXPECT_EQ("Hello World", *singleViewStringIt);
}
TEST_F(RangesViewTestFixture, RefView_CanWrapStringView_Succeeds)
{
AZStd::string_view testString{ "Hello World" };
AZStd::ranges::ref_view refView(testString);
EXPECT_EQ(testString.size(), refView.size());
EXPECT_EQ(testString.data(), refView.data());
EXPECT_EQ(testString.begin(), refView.begin());
EXPECT_EQ(testString.end(), refView.end());
EXPECT_EQ(testString.empty(), refView.empty());
EXPECT_EQ(testString.front(), refView.front());
EXPECT_EQ(testString.back(), refView.back());
EXPECT_EQ(testString[5], refView[5]);
}
TEST_F(RangesViewTestFixture, OwningView_CanWrapStringView_Succeeds)
{
AZStd::string_view sourceView{ "Hello World" };
AZStd::string_view testString{ sourceView };
AZStd::ranges::owning_view owningView(AZStd::move(testString));
EXPECT_TRUE(testString.empty());
EXPECT_FALSE(owningView.empty());
EXPECT_EQ(sourceView.size(), owningView.size());
EXPECT_EQ(sourceView.data(), owningView.data());
EXPECT_EQ(sourceView.begin(), owningView.begin());
EXPECT_EQ(sourceView.end(), owningView.end());
EXPECT_EQ(sourceView.empty(), owningView.empty());
EXPECT_EQ(sourceView.front(), owningView.front());
EXPECT_EQ(sourceView.back(), owningView.back());
EXPECT_EQ(sourceView[5], owningView[5]);
}
MATCHER_P(ZipViewAtSentinel, sentinel, "") {
*result_listener << "zip view has iterated to sentinel";
return !(arg == sentinel);
}
TEST_F(RangesViewTestFixture, ZipView_CompilesWithRange_Succeeds)
{
AZStd::string_view sourceView{ "Hello World" };
AZStd::ranges::zip_view zipView(AZStd::move(sourceView));
auto zipItTuple = zipView.begin();
auto zipSentinelTuple = zipView.end();
ASSERT_THAT(zipItTuple, ZipViewAtSentinel(zipSentinelTuple));
EXPECT_EQ('H', AZStd::get<0>(*zipItTuple));
ptrdiff_t zipDistance = zipSentinelTuple - zipItTuple;
EXPECT_EQ(11, zipDistance);
AZStd::list<int> sourceList{ 1, 2, 3, 4, 5 };
AZStd::ranges::zip_view zipView2(AZStd::move(sourceList));
auto zipListTupleIt = zipView2.begin();
auto zipListTupleEnd = zipView2.end();
ASSERT_THAT(zipListTupleIt, ZipViewAtSentinel(zipListTupleEnd));
}
TEST_F(RangesViewTestFixture, ZipView_CanIteratOverMultipleContainers_Succeeds)
{
AZStd::string_view sourceView{ "abcdef" };
AZStd::vector intVector{ 1, 2, 3, 4, 5 };
AZStd::list<uint32_t> uintList{ 2, 4, 6, 8, 10 };
constexpr int expectedIterations = 5;
int iterationCount{};
for (auto [charX, intY, uintZ] : (AZStd::ranges::views::zip(sourceView, AZStd::move(intVector), AZStd::move(uintList))))
{
++iterationCount;
switch (charX)
{
case 'a':
EXPECT_EQ(1, intY);
EXPECT_EQ(2, uintZ);
break;
case 'b':
EXPECT_EQ(2, intY);
EXPECT_EQ(4, uintZ);
break;
case 'c':
EXPECT_EQ(3, intY);
EXPECT_EQ(6, uintZ);
break;
case 'd':
EXPECT_EQ(4, intY);
EXPECT_EQ(8, uintZ);
break;
case 'e':
EXPECT_EQ(5, intY);
EXPECT_EQ(10, uintZ);
break;
default:
ADD_FAILURE() << "Unexpected character value " << charX << " found when iterating zip view";
}
}
EXPECT_EQ(expectedIterations, iterationCount);
}
TEST_F(RangesViewTestFixture, SplitView_CanSplitPatterns_Succeeds)
{
AZStd::string_view emptyView{ "" };
auto splitView = AZStd::ranges::views::split(emptyView, " ");
auto splitIt = splitView.begin();
EXPECT_EQ(splitView.end(), splitIt);
AZStd::string_view testView1{ "Hello" };
auto splitViewCharPattern = AZStd::ranges::views::split(testView1, ' ');
auto splitCharIt = splitViewCharPattern.begin();
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
++splitCharIt;
EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
AZStd::string_view testView2{ "Hello World" };
splitViewCharPattern = AZStd::ranges::views::split(testView2, ' ');
splitCharIt = splitViewCharPattern.begin();
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
++splitCharIt;
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
++splitCharIt;
EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
AZStd::string_view testView3{ "Hello World Moon" };
splitViewCharPattern = AZStd::ranges::views::split(testView3, ' ');
splitCharIt = splitViewCharPattern.begin();
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
++splitCharIt;
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
++splitCharIt;
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("Moon", AZStd::string_view(*splitCharIt));
++splitCharIt;
EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
AZStd::string_view testView4{ "Hello World Moon " };
splitViewCharPattern = AZStd::ranges::views::split(testView4, ' ');
splitCharIt = splitViewCharPattern.begin();
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
++splitCharIt;
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
++splitCharIt;
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("Moon", AZStd::string_view(*splitCharIt));
++splitCharIt;
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("", AZStd::string_view(*splitCharIt));
++splitCharIt;
EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
AZStd::string_view testView5{ "Hello World Moon " };
splitViewCharPattern = AZStd::ranges::views::split(testView5, ' ');
splitCharIt = splitViewCharPattern.begin();
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
++splitCharIt;
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
++splitCharIt;
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("Moon", AZStd::string_view(*splitCharIt));
++splitCharIt;
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("", AZStd::string_view(*splitCharIt));
++splitCharIt;
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("", AZStd::string_view(*splitCharIt));
++splitCharIt;
EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
AZStd::string_view testView6{ "Hello World Moon" };
splitViewCharPattern = AZStd::ranges::views::split(testView6, ' ');
splitCharIt = splitViewCharPattern.begin();
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("Hello", AZStd::string_view(*splitCharIt));
++splitCharIt;
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("", AZStd::string_view(*splitCharIt));
++splitCharIt;
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("World", AZStd::string_view(*splitCharIt));
++splitCharIt;
ASSERT_NE(splitViewCharPattern.end(), splitCharIt);
EXPECT_EQ("Moon", AZStd::string_view(*splitCharIt));
++splitCharIt;
EXPECT_EQ(splitViewCharPattern.end(), splitCharIt);
}
TEST_F(RangesViewTestFixture, SplitView_SplitsFromNonString_Succeeds)
{
const AZStd::vector<int> testVector{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Split the vector on 3
auto splitView = AZStd::ranges::views::split(testVector, 3);
auto splitIt = splitView.begin();
ASSERT_NE(splitView.end(), splitIt);
auto splitSubrange = *splitIt;
{
AZStd::array expectedValue{ 1, 2 };
EXPECT_TRUE(AZStd::ranges::equal(expectedValue, splitSubrange));
}
++splitIt;
ASSERT_NE(splitView.end(), splitIt);
splitSubrange = *splitIt;
{
AZStd::array expectedValue{ 4, 5, 6, 7, 8, 9, 10 };
EXPECT_TRUE(AZStd::ranges::equal(expectedValue, splitSubrange));
}
}
TEST_F(RangesViewTestFixture, JoinView_IteratesOverInnerViews_Succeeds)
{
constexpr AZStd::string_view expectedString = "HelloWorldMoonSun";
using Rope = AZStd::fixed_vector<AZStd::string_view, 32>;
Rope rope{ "Hello", "World", "Moon", "Sun" };
AZStd::fixed_string<128> accumString;
for (auto&& charElement : AZStd::ranges::join_view(rope))
{
accumString.push_back(charElement);
}
EXPECT_EQ(expectedString, accumString);
}
TEST_F(RangesViewTestFixture, JoinView_IteratesCanIterateOverSplitView_Succeeds)
{
constexpr AZStd::string_view expectedString = "HelloWorldMoonSun";
constexpr AZStd::string_view splitExpression = "Hello,World,Moon,Sun";
AZStd::fixed_string<128> accumString;
for (auto&& charElement : AZStd::ranges::views::join(AZStd::ranges::views::split(splitExpression, ',')))
{
accumString.push_back(charElement);
}
EXPECT_EQ(expectedString, accumString);
}
TEST_F(RangesViewTestFixture, JoinView_IterSwapCustomization_Succeeds)
{
AZStd::fixed_vector<AZStd::string, 8> testVector1{ "World", "Hello" };
AZStd::fixed_vector<AZStd::string, 8> testVector2{ "Value", "First" };
auto joinView1 = AZStd::ranges::views::join(testVector1);
auto joinView2 = AZStd::ranges::views::join(testVector2);
auto joinViewIter1 = joinView1.begin();
auto joinViewIter2 = joinView2.begin();
// swaps the 'W' and 'V'
AZStd::ranges::iter_swap(joinViewIter1, joinViewIter2);
AZStd::ranges::advance(joinViewIter1, 5, joinView1.end());
AZStd::ranges::advance(joinViewIter2, 5, joinView2.end());
// swaps the 'H' and 'F' of the second string of each vector
AZStd::ranges::iter_swap(joinViewIter1, joinViewIter2);
EXPECT_EQ("Vorld", testVector1[0]);
EXPECT_EQ("Fello", testVector1[1]);
EXPECT_EQ("Walue", testVector2[0]);
EXPECT_EQ("Hirst", testVector2[1]);
}
TEST_F(RangesViewTestFixture, JoinView_IterMoveCustomization_Succeeds)
{
using StringWrapper = AZStd::ranges::single_view<AZStd::string>;
AZStd::fixed_vector<StringWrapper, 8> testVector1{ StringWrapper{"5"}, StringWrapper{"10"} };
AZStd::fixed_vector<StringWrapper, 8> testVector2{ StringWrapper{"15"}, StringWrapper{"20"} };
auto joinView1 = AZStd::ranges::views::join(testVector1);
auto joinView2 = AZStd::ranges::views::join(testVector2);
auto joinViewIter1 = joinView1.begin();
auto joinViewIter2 = joinView2.begin();
AZStd::string value = AZStd::ranges::iter_move(joinViewIter1);
EXPECT_EQ("5", value);
EXPECT_TRUE((*joinViewIter1).empty());
++joinViewIter2;
value = AZStd::ranges::iter_move(joinViewIter2);
EXPECT_EQ("20", value);
EXPECT_TRUE((*joinViewIter2).empty());
}
TEST_F(RangesViewTestFixture, ElementsView_CanIterateVectorOfTuple_Succeeds)
{
using TestTuple = AZStd::tuple<int, AZStd::string, bool>;
AZStd::vector testVector{ TestTuple{1, "hello", false}, TestTuple{2, "world", true},
TestTuple{3, "Moon", false} };
auto firstElementView = AZStd::ranges::views::elements<0>(testVector);
auto firstElementBegin = AZStd::ranges::begin(firstElementView);
auto firstElementEnd = AZStd::ranges::end(firstElementView);
EXPECT_NE(firstElementEnd, firstElementBegin);
ASSERT_EQ(3, firstElementView.size());
EXPECT_EQ(1, firstElementView[0]);
EXPECT_EQ(2, firstElementView[1]);
EXPECT_EQ(3, firstElementView[2]);
auto secondElementView = AZStd::ranges::views::elements<1>(testVector);
ASSERT_EQ(3, secondElementView.size());
EXPECT_EQ("hello", secondElementView[0]);
EXPECT_EQ("world", secondElementView[1]);
EXPECT_EQ("Moon", secondElementView[2]);
auto thirdElementView = AZStd::ranges::views::elements<2>(testVector);
ASSERT_EQ(3, thirdElementView.size());
EXPECT_FALSE(thirdElementView[0]);
EXPECT_TRUE(thirdElementView[1]);
EXPECT_FALSE(thirdElementView[2]);
using TestPair = AZStd::pair<AZStd::string, int>;
AZStd::vector testPairVector{ TestPair{"hello", 5}, TestPair{"world", 10}, TestPair{"Sun", 15} };
using ElementsViewBase = AZStd::ranges::views::all_t<decltype((testPairVector))>;
using ElementsViewType = AZStd::ranges::elements_view<ElementsViewBase, 0>;
constexpr AZStd::string_view expectedString = "helloworldSun";
AZStd::string accumString;
for (auto&& stringValue : ElementsViewType(testPairVector))
{
accumString += stringValue;
}
EXPECT_EQ(expectedString, accumString);
}
TEST_F(RangesViewTestFixture, ElementsView_KeysAlias_CanIterateAssociativeContainerKeyType)
{
using PairType = AZStd::pair<int, const char*>;
AZStd::map testMap{ PairType{1, "Hello"}, PairType{2, "World"}, PairType{3, "Sun"}, PairType{45, "RandomText"} };
int accumResult{};
for (int key : AZStd::ranges::views::keys(testMap))
{
accumResult += key;
}
EXPECT_EQ(51, accumResult);
}
TEST_F(RangesViewTestFixture, ElementsView_ValuesAlias_CanIterateAssociativeContainerMappedType)
{
using PairType = AZStd::pair<int, const char*>;
AZStd::map testMap{ PairType{1, "Hello"}, PairType{2, "World"}, PairType{3, "Sun"}, PairType{45, "RandomText"} };
AZStd::string accumResult{};
for (const char* value : AZStd::ranges::views::values(testMap))
{
accumResult += value;
}
EXPECT_EQ("HelloWorldSunRandomText", accumResult);
}
}
+2 -17
View File
@@ -7,28 +7,13 @@
*/
#include <AzCore/UnitTest/UnitTest.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzTest/AzTest.h>
#if defined(HAVE_BENCHMARK)
namespace AzCore
{
class AzCoreBenchmarkEnvironment
: public AZ::Test::BenchmarkEnvironmentBase
{
void SetUpBenchmark() override
{
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
}
void TearDownBenchmark() override
{
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
}
};
}
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV, AzCore::AzCoreBenchmarkEnvironment)
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV, UnitTest::ScopedAllocatorBenchmarkEnvironment)
#else
@@ -291,7 +291,7 @@ namespace Benchmark
for (auto _ : state)
{
state.PauseTiming();
AZStd::vector<void*>& perThreadAllocations = base::GetPerThreadAllocations(state.thread_index);
const size_t numberOfAllocations = perThreadAllocations.size();
size_t totalAllocationSize = 0;
@@ -300,7 +300,7 @@ namespace Benchmark
const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize];
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
totalAllocationSize += allocationSize;
state.ResumeTiming();
perThreadAllocations[allocationIndex] = TestAllocatorType::Allocate(allocationSize, 0);
state.PauseTiming();
@@ -319,6 +319,8 @@ namespace Benchmark
TestAllocatorType::GarbageCollect();
state.SetItemsProcessed(numberOfAllocations);
state.ResumeTiming();
}
}
};
@@ -364,6 +366,8 @@ namespace Benchmark
state.SetItemsProcessed(numberOfAllocations);
TestAllocatorType::GarbageCollect();
state.ResumeTiming();
}
}
};
@@ -519,6 +523,8 @@ namespace Benchmark
state.SetItemsProcessed(itemsProcessed);
TestAllocatorType::GarbageCollect();
state.ResumeTiming();
}
}
};
@@ -207,7 +207,9 @@ set(FILES
AZStd/Optional.cpp
AZStd/Pair.cpp
AZStd/Parallel.cpp
AZStd/RangesAlgorithmTests.cpp
AZStd/RangesTests.cpp
AZStd/RangesViewTests.cpp
AZStd/ScopedLockTests.cpp
AZStd/SetsIntrusive.cpp
AZStd/SmartPtr.cpp
@@ -37,6 +37,7 @@ namespace AzFramework::SurfaceData
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AZStd::fixed_vector<SurfaceTagWeight, Constants::MaxSurfaceWeights>>();
serializeContext->Class<SurfacePoint>()
->Field("m_position", &SurfacePoint::m_position)
->Field("m_normal", &SurfacePoint::m_normal)
@@ -46,6 +47,7 @@ namespace AzFramework::SurfaceData
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<AZStd::fixed_vector<SurfaceTagWeight, Constants::MaxSurfaceWeights>>();
behaviorContext->Class<SurfacePoint>("AzFramework::SurfaceData::SurfacePoint")
->Attribute(AZ::Script::Attributes::Category, "SurfaceData")
->Constructor()
@@ -10,13 +10,19 @@
#include <AzCore/Math/Crc.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/fixed_vector.h>
namespace AzFramework::SurfaceData
{
namespace Constants
{
static constexpr const char* s_unassignedTagName = "(unassigned)";
//! The maximum number of surface weights that we can store.
//! For performance reasons, we want to limit this so that we can preallocate the max size in advance.
//! The current number is chosen to be higher than expected needs, but small enough to avoid being excessively wasteful.
//! (Dynamic structures would end up taking more memory than what we're preallocating)
static constexpr size_t MaxSurfaceWeights = 16;
}
struct SurfaceTagWeight
@@ -70,7 +76,7 @@ namespace AzFramework::SurfaceData
}
};
using SurfaceTagWeightList = AZStd::vector<SurfaceTagWeight>;
using SurfaceTagWeightList = AZStd::fixed_vector<SurfaceTagWeight, Constants::MaxSurfaceWeights>;
struct SurfacePoint final
{
@@ -8,10 +8,12 @@
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Jobs/JobContext.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/std/containers/span.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Render/GeometryIntersectionStructures.h>
#include <AzFramework/SurfaceData/SurfaceData.h>
@@ -193,6 +195,147 @@ namespace AzFramework
//! Given a ray, return the closest intersection with terrain.
virtual RenderGeometry::RayResult GetClosestIntersection(const RenderGeometry::RayRequest& ray) const = 0;
//! A JobContext used to run jobs spawned by calls to the various Process*Async functions.
class TerrainJobContext : public AZ::JobContext
{
public:
TerrainJobContext(AZ::JobManager& jobManager,
int numJobsToComplete)
: JobContext(jobManager)
, m_numJobsToComplete(numJobsToComplete)
{
}
// When a terrain job context is cancelled, all associated
// jobs are still guaranteed to at least begin processing,
// and if any ProcessAsyncParams::m_completionCallback was
// set it's guaranteed to be called even in the event of a
// cancellation. If a job only begins processing after its
// associated job context has been cancelled, no processing
// will occur and the callback will be invoked immediately,
// otherwise the job may either run to completion or cease
// processing early; the callback is invoked in all cases,
// provided one was specified with the original request.
void Cancel() { m_isCancelled = true; }
// Was this TerrainJobContext cancelled?
bool IsCancelled() const { return m_isCancelled; }
// Called by the TerrainSystem when a job associated with
// this TerrainJobContext completes. Returns true if this
// was the final job to be completed, or false otherwise.
bool OnJobCompleted() { return (--m_numJobsToComplete == 0); }
private:
AZStd::atomic_int m_numJobsToComplete = 0;
AZStd::atomic_bool m_isCancelled = false;
};
//! Alias for an optional callback function to invoke when the various Process*Async functions complete.
//! The TerrainJobContext, returned from the original Process*Async function call, is passed as a param
//! to the callback function so it can be queried to see if the job was cancelled or completed normally.
typedef AZStd::function<void(AZStd::shared_ptr<TerrainJobContext>)> ProcessAsyncCompleteCallback;
//! A parameter group struct that can optionally be passed to the various Process*Async API functions.
struct ProcessAsyncParams
{
//! The default minimum number ofpositions per async terrain request job.
static constexpr int32_t MinPositionsPerJobDefault = 8;
//! The default number of jobs which async terrain requests will be split into.
static constexpr int32_t NumJobsDefault = 1;
//! The maximum number of jobs which async terrain requests will be split into.
//! This is not the value itself, rather a constant that can be used to request
//! the work be split into the maximum number of job manager threads available.
static constexpr int32_t NumJobsMax = -1;
//! The desired number of jobs to split async terrain requests into.
//! The actual value used will be clamped to the number of available job manager threads.
//!
//! Note: Currently, splitting the work over multiple threads causes contention when
//! locking various mutexes, resulting in slower overall wall time for async
//! requests split over multiple threads vs one where all the work is done on
//! a single thread. The latter is still preferable over a regular synchronous
//! call because it is just as quick and prevents the main thread from blocking.
//! This note should be removed once the mutex contention issues have been addressed.
int32_t m_desiredNumberOfJobs = NumJobsDefault;
//! The minimum number of positions per async terrain request job.
int32_t m_minPositionsPerJob = MinPositionsPerJobDefault;
//! The callback function that will be invoked when a call to a Process*Async function completes.
//! If the job is cancelled, the completion callback will not be invoked.
ProcessAsyncCompleteCallback m_completionCallback = nullptr;
};
//! Asynchronous versions of the various 'Process*' API functions declared above.
//! It's the responsibility of the caller to ensure all callbacks are threadsafe.
virtual AZStd::shared_ptr<TerrainJobContext> ProcessHeightsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessNormalsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfaceWeightsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfacePointsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessHeightsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessNormalsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfaceWeightsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfacePointsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessHeightsFromRegionAsync(
const AZ::Aabb& inRegion,
const AZ::Vector2& stepSize,
SurfacePointRegionFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessNormalsFromRegionAsync(
const AZ::Aabb& inRegion,
const AZ::Vector2& stepSize,
SurfacePointRegionFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfaceWeightsFromRegionAsync(
const AZ::Aabb& inRegion,
const AZ::Vector2& stepSize,
SurfacePointRegionFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfacePointsFromRegionAsync(
const AZ::Aabb& inRegion,
const AZ::Vector2& stepSize,
SurfacePointRegionFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
private:
// Private variations of the GetSurfacePoint API exposed to BehaviorContext that returns a value instead of
// using an "out" parameter. The "out" parameter is useful for reusing memory allocated in SurfacePoint when
@@ -8,11 +8,10 @@
#pragma once
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/optional.h>
#include <chrono>
namespace AzFramework
{
//! Default value to use for detecting if the mouse has moved far enough after a mouse down to no longer
@@ -106,5 +106,30 @@ namespace UnitTest
GetTerrainRaycastEntityContextId, AzFramework::EntityContextId());
MOCK_CONST_METHOD1(
GetClosestIntersection, AzFramework::RenderGeometry::RayResult(const AzFramework::RenderGeometry::RayRequest&));
MOCK_CONST_METHOD4(
ProcessHeightsFromListAsync, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4(
ProcessNormalsFromListAsync, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4(
ProcessSurfaceWeightsFromListAsync, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4(
ProcessSurfacePointsFromListAsync, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4(
ProcessHeightsFromListOfVector2Async, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4(
ProcessNormalsFromListOfVector2Async, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4(
ProcessSurfaceWeightsFromListOfVector2Async, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4(
ProcessSurfacePointsFromListOfVector2Async, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD5(
ProcessHeightsFromRegionAsync, AZStd::shared_ptr<TerrainJobContext>(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD5(
ProcessNormalsFromRegionAsync, AZStd::shared_ptr<TerrainJobContext>(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD5(
ProcessSurfaceWeightsFromRegionAsync, AZStd::shared_ptr<TerrainJobContext>(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD5(
ProcessSurfacePointsFromRegionAsync, AZStd::shared_ptr<TerrainJobContext>(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
};
} // namespace UnitTest
@@ -46,9 +46,9 @@ namespace AzManipulatorTestFramework
//! Send a double click event.
DerivedDispatcherT* MouseLButtonDoubleClick();
//! Set the keyboard modifier button down.
DerivedDispatcherT* KeyboardModifierDown(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier);
DerivedDispatcherT* KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier);
//! Set the keyboard modifier button up.
DerivedDispatcherT* KeyboardModifierUp(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier);
DerivedDispatcherT* KeyboardModifierUp(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier);
//! Set the mouse position to the specified screen space position.
DerivedDispatcherT* MousePosition(const AzFramework::ScreenPoint& position);
//! Expect the selected manipulator to be interacting.
@@ -81,8 +81,8 @@ namespace AzManipulatorTestFramework
virtual void MouseMButtonUpImpl() = 0;
virtual void MouseLButtonDoubleClickImpl() = 0;
virtual void MousePositionImpl(const AzFramework::ScreenPoint& position) = 0;
virtual void KeyboardModifierDownImpl(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier) = 0;
virtual void KeyboardModifierUpImpl(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier) = 0;
virtual void KeyboardModifierDownImpl(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier) = 0;
virtual void KeyboardModifierUpImpl(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier) = 0;
virtual void ExpectManipulatorBeingInteractedImpl() = 0;
virtual void ExpectManipulatorNotBeingInteractedImpl() = 0;
virtual void SetEntityWorldTransformImpl(AZ::EntityId entityId, const AZ::Transform& transform) = 0;
@@ -94,7 +94,7 @@ namespace AzManipulatorTestFramework
bool m_logging = false;
private:
const char* KeyboardModifierString(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier);
const char* KeyboardModifierString(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier);
static void DebugPrint(const char* format, ...);
};
@@ -215,7 +215,7 @@ namespace AzManipulatorTestFramework
template<typename DerivedDispatcherT>
const char* ActionDispatcher<DerivedDispatcherT>::KeyboardModifierString(
const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier)
const AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier)
{
using namespace AzToolsFramework::ViewportInteraction;
switch (keyModifier)
@@ -235,7 +235,7 @@ namespace AzManipulatorTestFramework
template<typename DerivedDispatcherT>
DerivedDispatcherT* ActionDispatcher<DerivedDispatcherT>::KeyboardModifierDown(
const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier)
const AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier)
{
Log("Keyboard modifier down: %s", KeyboardModifierString(keyModifier));
KeyboardModifierDownImpl(keyModifier);
@@ -244,7 +244,7 @@ namespace AzManipulatorTestFramework
template<typename DerivedDispatcherT>
DerivedDispatcherT* ActionDispatcher<DerivedDispatcherT>::KeyboardModifierUp(
const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier)
const AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier)
{
Log("Keyboard modifier up: %s", KeyboardModifierString(keyModifier));
KeyboardModifierUpImpl(keyModifier);
@@ -66,8 +66,8 @@ namespace AzManipulatorTestFramework
void MouseMButtonUpImpl() override;
void MouseLButtonDoubleClickImpl() override;
void MousePositionImpl(const AzFramework::ScreenPoint& position) override;
void KeyboardModifierDownImpl(const KeyboardModifier& keyModifier) override;
void KeyboardModifierUpImpl(const KeyboardModifier& keyModifier) override;
void KeyboardModifierDownImpl(KeyboardModifier keyModifier) override;
void KeyboardModifierUpImpl(KeyboardModifier keyModifier) override;
void ExpectManipulatorBeingInteractedImpl() override;
void ExpectManipulatorNotBeingInteractedImpl() override;
void SetEntityWorldTransformImpl(AZ::EntityId entityId, const AZ::Transform& transform) override;
@@ -126,12 +126,12 @@ namespace AzManipulatorTestFramework
m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(*m_event);
}
void ImmediateModeActionDispatcher::KeyboardModifierDownImpl(const KeyboardModifier& keyModifier)
void ImmediateModeActionDispatcher::KeyboardModifierDownImpl(const KeyboardModifier keyModifier)
{
ToggleOn(GetMouseInteractionEvent()->m_mouseInteraction.m_keyboardModifiers.m_keyModifiers, keyModifier);
}
void ImmediateModeActionDispatcher::KeyboardModifierUpImpl(const KeyboardModifier& keyModifier)
void ImmediateModeActionDispatcher::KeyboardModifierUpImpl(const KeyboardModifier keyModifier)
{
ToggleOff(GetMouseInteractionEvent()->m_mouseInteraction.m_keyboardModifiers.m_keyModifiers, keyModifier);
}
+28 -14
View File
@@ -164,6 +164,16 @@ namespace AZ
m_envs.push_back(std::move(env));
}
// Remove a registered benchmark from the registry
void RemoveBenchmarkEnvironment(BenchmarkEnvironmentBase* env)
{
auto RemoveBenchmarkFunc = [env](const std::unique_ptr<BenchmarkEnvironmentBase>& envElement)
{
return envElement.get() == env;
};
m_envs.erase(std::remove_if(m_envs.begin(), m_envs.end(), std::move(RemoveBenchmarkFunc)));
}
std::vector<std::unique_ptr<BenchmarkEnvironmentBase>>& GetBenchmarkEnvironments()
{
return m_envs;
@@ -194,21 +204,26 @@ namespace AZ
return *benchmarkEnv;
}
template<typename... Ts>
std::array<BenchmarkEnvironmentBase*, sizeof...(Ts)> RegisterBenchmarkEnvironments()
/*
* An RAII wrapper about registering a BenchmarkEnvironment with the BenchmarkRegistry
* It will unregister the BenchmarkEnvironment with the BenchmarkRegistry on destruction
*/
struct ScopedRegisterBenchmarkEnvironment
{
constexpr size_t EnvironmentCount{ sizeof...(Ts) };
if constexpr (EnvironmentCount)
template<typename T>
ScopedRegisterBenchmarkEnvironment(T& benchmarkEnv)
: m_benchmarkEnv(benchmarkEnv)
{}
~ScopedRegisterBenchmarkEnvironment()
{
std::array<BenchmarkEnvironmentBase*, EnvironmentCount> benchmarkEnvs{ { &RegisterBenchmarkEnvironment<Ts>()... } };
return benchmarkEnvs;
if (auto benchmarkRegistry = AZ::Environment::FindVariable<BenchmarkEnvironmentRegistry>(s_benchmarkEnvironmentName);
benchmarkRegistry != nullptr)
{
benchmarkRegistry->RemoveBenchmarkEnvironment(&m_benchmarkEnv);
}
}
else
{
std::array<BenchmarkEnvironmentBase*, EnvironmentCount> benchmarkEnvs{};
return benchmarkEnvs;
}
}
BenchmarkEnvironmentBase& m_benchmarkEnv;
};
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//! listener class to capture and print test output for embedded platforms
@@ -276,7 +291,7 @@ namespace AZ
#define AZ_BENCHMARK_HOOK_ENV(TEST_ENV) \
AZTEST_EXPORT int AzRunBenchmarks(int argc, char** argv) \
{ \
AZ::Test::RegisterBenchmarkEnvironments<TEST_ENV>(); \
AZ::Test::ScopedRegisterBenchmarkEnvironment scopedBenchmarkEnv(AZ::Test::RegisterBenchmarkEnvironment<TEST_ENV>()); \
auto benchmarkEnvRegistry = AZ::Environment::FindVariable<AZ::Test::BenchmarkEnvironmentRegistry>(AZ::Test::s_benchmarkEnvironmentName); \
std::vector<std::unique_ptr<AZ::Test::BenchmarkEnvironmentBase>>* benchmarkEnvs = benchmarkEnvRegistry ? &(benchmarkEnvRegistry->GetBenchmarkEnvironments()) : nullptr; \
if (benchmarkEnvs != nullptr) \
@@ -307,7 +322,6 @@ AZTEST_EXPORT int AzRunBenchmarks(int argc, char** argv) \
#define AZ_BENCHMARK_HOOK() \
AZTEST_EXPORT int AzRunBenchmarks(int argc, char** argv) \
{ \
AZ::Test::RegisterBenchmarkEnvironments<>(); \
auto benchmarkEnvRegistry = AZ::Environment::FindVariable<AZ::Test::BenchmarkEnvironmentRegistry>(AZ::Test::s_benchmarkEnvironmentName); \
std::vector<std::unique_ptr<AZ::Test::BenchmarkEnvironmentBase>>* benchmarkEnvs = benchmarkEnvRegistry ? &(benchmarkEnvRegistry->GetBenchmarkEnvironments()) : nullptr; \
if (benchmarkEnvs != nullptr) \
@@ -21,7 +21,6 @@
#include <AzToolsFramework/AssetBrowser/AssetEntryChangeset.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/FolderThumbnail.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.h>
@@ -71,9 +70,9 @@ namespace AzToolsFramework
AssetBrowserInteractionNotificationBus::Handler::BusConnect();
using namespace Thumbnailer;
ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(FolderThumbnailCache), ThumbnailContext::DefaultContext);
ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(SourceThumbnailCache), ThumbnailContext::DefaultContext);
ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(ProductThumbnailCache), ThumbnailContext::DefaultContext);
ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(FolderThumbnailCache));
ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(SourceThumbnailCache));
ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(ProductThumbnailCache));
AzFramework::SocketConnection* socketConn = AzFramework::SocketConnection::GetInstance();
AZ_Assert(socketConn, "AzToolsFramework::AssetBrowser::AssetBrowserComponent requires a valid socket conection!");
@@ -82,7 +82,7 @@ namespace AzToolsFramework
});
connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AssetPickerDialog::DoubleClickedSlot);
connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, this,
[this](const QItemSelection&, const QItemSelection&){ AssetPickerDialog::SelectionChangedSlot(); });
[this](const QItemSelection&, const QItemSelection&){ SelectionChangedSlot(); });
connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_ui->m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
@@ -153,7 +153,7 @@ namespace AzToolsFramework
m_ui->m_assetBrowserTableViewWidget, &AssetBrowserTableView::selectionChangedSignal, this,
[this](const QItemSelection&, const QItemSelection&)
{
AssetPickerDialog::SelectionChangedSlot();
SelectionChangedSlot();
});
connect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AssetPickerDialog::DoubleClickedSlot);
@@ -174,8 +174,15 @@ namespace AzToolsFramework
m_tableModel->UpdateTableModelMaps();
}
QTimer::singleShot(0, this, &AssetPickerDialog::RestoreState);
SelectionChangedSlot();
QTimer::singleShot(0, this, [this]() {
RestoreState();
// The selection doesn't propagate immediately, so we need to delay
// it as well so that the OK button can be updated appropriately.
// Otherwise, it will always be disabled when you first launch
// the asset picker dialog.
SelectionChangedSlot();
});
}
AssetPickerDialog::~AssetPickerDialog() = default;
@@ -58,16 +58,16 @@ namespace AzToolsFramework
void keyPressEvent(QKeyEvent* e) override;
void resizeEvent(QResizeEvent* resizeEvent) override;
private Q_SLOTS:
protected Q_SLOTS:
void DoubleClickedSlot(const QModelIndex& index);
void SelectionChangedSlot();
void RestoreState();
void OnFilterUpdated();
private:
protected:
//! Evaluate whether current selection is valid.
//! Valid selection requires exactly one item to be selected, must be source or product type, and must match the wildcard filter
bool EvaluateSelection() const;
virtual bool EvaluateSelection() const;
void UpdatePreview() const;
void SaveState();
@@ -75,7 +75,9 @@ namespace AzToolsFramework
bool foundIt = false;
AZStd::string watchFolder;
AZ::Data::AssetInfo assetInfo;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(foundIt, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, iconPath.toUtf8().constData(), assetInfo, watchFolder);
AssetSystemRequestBus::BroadcastResult(
foundIt, &AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, iconPath.toUtf8().constData(), assetInfo,
watchFolder);
if (foundIt)
{
@@ -67,7 +67,8 @@ namespace AzToolsFramework
bool foundIt = false;
AZStd::string watchFolder;
AZ::Data::AssetInfo assetInfo;
AssetSystemRequestBus::BroadcastResult(foundIt, &AssetSystemRequestBus::Events::GetSourceInfoBySourceUUID, sourceKey->GetSourceUuid(), assetInfo, watchFolder);
AssetSystemRequestBus::BroadcastResult(
foundIt, &AssetSystemRequestBus::Events::GetSourceInfoBySourceUUID, sourceKey->GetSourceUuid(), assetInfo, watchFolder);
QString iconPathToUse;
if (foundIt)
@@ -203,11 +203,6 @@ namespace AzToolsFramework
return selectionModel()->selectedIndexes();
}
void AssetBrowserTreeView::SetThumbnailContext(const char* thumbnailContext) const
{
m_delegate->SetThumbnailContext(thumbnailContext);
}
void AssetBrowserTreeView::SetShowSourceControlIcons(bool showSourceControlsIcons)
{
m_delegate->SetShowSourceControlIcons(showSourceControlsIcons);
@@ -73,7 +73,6 @@ namespace AzToolsFramework
void OnAssetBrowserComponentReady() override;
//////////////////////////////////////////////////////////////////////////
void SetThumbnailContext(const char* context) const;
void SetShowSourceControlIcons(bool showSourceControlsIcons);
void UpdateAfterFilter(bool hasFilter, bool selectFirstValidEntry);
@@ -112,11 +112,6 @@ namespace AzToolsFramework
}
}
void EntryDelegate::SetThumbnailContext(const char* thumbnailContext)
{
m_thumbnailContext = thumbnailContext;
}
void EntryDelegate::SetShowSourceControlIcons(bool showSourceControl)
{
m_showSourceControl = showSourceControl;
@@ -125,8 +120,8 @@ namespace AzToolsFramework
int EntryDelegate::DrawThumbnail(QPainter* painter, const QPoint& point, const QSize& size, Thumbnailer::SharedThumbnailKey thumbnailKey) const
{
SharedThumbnail thumbnail;
ThumbnailerRequestsBus::BroadcastResult(thumbnail, &ThumbnailerRequests::GetThumbnail, thumbnailKey, m_thumbnailContext.c_str());
AZ_Assert(thumbnail, "The shared numbernail was not available from the ThumbnailerRequestsBus.");
ThumbnailerRequestBus::BroadcastResult(thumbnail, &ThumbnailerRequests::GetThumbnail, thumbnailKey);
AZ_Assert(thumbnail, "The shared numbernail was not available from the ThumbnailerRequestBus.");
AZ_Assert(painter, "A null QPainter was passed in to DrawThumbnail.");
if (!painter || !thumbnail || thumbnail->GetState() == Thumbnail::State::Failed)
{
@@ -11,7 +11,6 @@
#if !defined(Q_MOC_RUN)
#include <AzCore/std/function/function_fwd.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: class 'QScopedPointer<QBrushData,QBrushDataPointerDeleter>' needs to have dll-interface to be used by clients of class 'QBrush'
// 4800: 'uint': forcing value to bool 'true' or 'false' (performance warning)
#include <QStyledItemDelegate>
@@ -50,14 +49,11 @@ namespace AzToolsFramework
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
//! Set location where thumbnails are located for this instance of asset browser
void SetThumbnailContext(const char* thumbnailContext);
//! Set whether to show source control icons, this is still temporary mainly to support existing functionality of material browser
void SetShowSourceControlIcons(bool showSourceControl);
protected:
int m_iconSize;
AZStd::string m_thumbnailContext = ThumbnailContext::DefaultContext;
bool m_showSourceControl = false;
//! Draw a thumbnail and return its width
int DrawThumbnail(QPainter* painter, const QPoint& point, const QSize& size, Thumbnailer::SharedThumbnailKey thumbnailKey) const;
@@ -14,6 +14,8 @@
#include <AzToolsFramework/Manipulators/ManipulatorSnapping.h>
#include <AzFramework/Viewport/ViewportColors.h>
#include <AzFramework/Viewport/ViewportConstants.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Component/NonUniformScaleBus.h>
namespace AzToolsFramework
{
@@ -37,20 +39,20 @@ namespace AzToolsFramework
void BoxViewportEdit::UpdateManipulators()
{
AZ::Transform boxWorldFromLocal = AZ::Transform::CreateIdentity();
BoxManipulatorRequestBus::EventResult(
boxWorldFromLocal, m_entityComponentIdPair, &BoxManipulatorRequests::GetCurrentTransform);
AZ::TransformBus::EventResult(
boxWorldFromLocal, m_entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
AZ::Vector3 boxScale = AZ::Vector3::CreateOne();
BoxManipulatorRequestBus::EventResult(
boxScale, m_entityComponentIdPair, &BoxManipulatorRequests::GetBoxScale);
AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne();
AZ::NonUniformScaleRequestBus::EventResult(
nonUniformScale, m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequestBus::Events::GetScale);
AZ::Vector3 boxDimensions = AZ::Vector3::CreateZero();
BoxManipulatorRequestBus::EventResult(
boxDimensions, m_entityComponentIdPair, &BoxManipulatorRequests::GetDimensions);
boxDimensions, m_entityComponentIdPair, &BoxManipulatorRequestBus::Events::GetDimensions);
// ensure we apply the entity scale to the box dimensions so
// the manipulators appear in the correct location
boxDimensions *= boxScale;
AZ::Transform boxLocalTransform = AZ::Transform::CreateIdentity();
BoxManipulatorRequestBus::EventResult(
boxLocalTransform, m_entityComponentIdPair, &BoxManipulatorRequestBus::Events::GetCurrentLocalTransform);
for (size_t manipulatorIndex = 0; manipulatorIndex < m_linearManipulators.size(); ++manipulatorIndex)
{
@@ -58,7 +60,8 @@ namespace AzToolsFramework
{
linearManipulator->SetSpace(boxWorldFromLocal);
linearManipulator->SetLocalTransform(
AZ::Transform::CreateTranslation(s_boxAxes[manipulatorIndex] * 0.5f * boxDimensions));
boxLocalTransform * AZ::Transform::CreateTranslation(s_boxAxes[manipulatorIndex] * 0.5f * boxDimensions));
linearManipulator->SetNonUniformScale(nonUniformScale);
linearManipulator->SetBoundsDirty();
}
}
@@ -69,8 +72,8 @@ namespace AzToolsFramework
m_entityComponentIdPair = entityComponentIdPair;
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
BoxManipulatorRequestBus::EventResult(
worldFromLocal, entityComponentIdPair, &BoxManipulatorRequests::GetCurrentTransform);
AZ::TransformBus::EventResult(
worldFromLocal, entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
for (size_t manipulatorIndex = 0; manipulatorIndex < m_linearManipulators.size(); ++manipulatorIndex)
{
@@ -85,36 +88,35 @@ namespace AzToolsFramework
ManipulatorViews views;
views.emplace_back(CreateManipulatorViewQuadBillboard(
AzFramework::ViewportColors::DefaultManipulatorHandleColor, AzFramework::ViewportConstants::DefaultManipulatorHandleSize));
AzFramework::ViewportColors::DefaultManipulatorHandleColor,
AzFramework::ViewportConstants::DefaultManipulatorHandleSize));
linearManipulator->SetViews(AZStd::move(views));
linearManipulator->InstallMouseMoveCallback(
[this, entityComponentIdPair](
const LinearManipulator::Action& action)
[this, entityComponentIdPair,
transformScale{ linearManipulator->GetSpace().GetUniformScale() }](const LinearManipulator::Action& action)
{
AZ::Transform boxLocalTransform = AZ::Transform::CreateIdentity();
BoxManipulatorRequestBus::EventResult(
boxLocalTransform, entityComponentIdPair, &BoxManipulatorRequestBus::Events::GetCurrentLocalTransform);
const AZ::Vector3 manipulatorPosition = GetPositionInManipulatorFrame(transformScale, boxLocalTransform, action);
// calculate the amount of displacement along an axis this manipulator has moved
// clamp movement so it cannot go negative based on axis direction
const AZ::Vector3 axisDisplacement =
action.LocalPosition().GetAbs() * 2.0f
* AZ::GetMax<float>(0.0f, action.LocalPosition().GetNormalized().Dot(action.m_fixed.m_axis));
AZ::Vector3 boxScale = AZ::Vector3::CreateOne();
BoxManipulatorRequestBus::EventResult(
boxScale, entityComponentIdPair, &BoxManipulatorRequests::GetBoxScale);
manipulatorPosition.GetAbs() * 2.0f
* AZ::GetMax(0.0f, manipulatorPosition.GetNormalized().Dot(action.m_fixed.m_axis));
AZ::Vector3 boxDimensions = AZ::Vector3::CreateZero();
BoxManipulatorRequestBus::EventResult(
boxDimensions, entityComponentIdPair, &BoxManipulatorRequests::GetDimensions);
// ensure we take into account the entity scale using the axis displacement
const AZ::Vector3 scaledAxisDisplacement =
axisDisplacement / boxScale;
boxDimensions, entityComponentIdPair, &BoxManipulatorRequestBus::Events::GetDimensions);
// update dimensions - preserve dimensions not effected by this
// axis, and update current axis displacement
BoxManipulatorRequestBus::Event(
entityComponentIdPair, &BoxManipulatorRequests::SetDimensions,
(NotAxis(action.m_fixed.m_axis) * boxDimensions).GetMax(scaledAxisDisplacement));
entityComponentIdPair, &BoxManipulatorRequestBus::Events::SetDimensions,
(NotAxis(action.m_fixed.m_axis) * boxDimensions).GetMax(axisDisplacement));
UpdateManipulators();
});
@@ -137,4 +139,12 @@ namespace AzToolsFramework
}
}
}
AZ::Vector3 GetPositionInManipulatorFrame(float worldUniformScale, const AZ::Transform& manipulatorLocalTransform,
const LinearManipulator::Action& action)
{
return manipulatorLocalTransform.GetInverse().TransformPoint(
action.m_start.m_localPosition +
action.m_current.m_localPositionOffset / AZ::GetClamp(worldUniformScale, AZ::MinTransformScale, AZ::MaxTransformScale));
}
} // namespace AzToolsFramework
@@ -31,4 +31,10 @@ namespace AzToolsFramework
using BoxManipulators = AZStd::array<AZStd::shared_ptr<LinearManipulator>, 6>;
BoxManipulators m_linearManipulators; ///< Manipulators for editing box size.
};
/// Calculates the position of the manipulator in its own reference frame.
/// Removes the effects of the manipulator local transform, and accounts for world transform scale in
/// the action local offset.
AZ::Vector3 GetPositionInManipulatorFrame(float worldUniformScale, const AZ::Transform& manipulatorLocalTransform,
const LinearManipulator::Action& action);
} // namespace AzToolsFramework
@@ -54,7 +54,8 @@ namespace AzToolsFramework
virtual ~BaseManipulator();
using EntityComponentIds = AZStd::unordered_set<AZ::EntityComponentIdPair>;
using UniqueEntityIds = AZStd::unordered_set<AZ::EntityId>;
using UniqueEntityComponentIds = AZStd::unordered_set<AZ::EntityComponentIdPair>;
//! Callback for the event when the mouse pointer is over this manipulator and the left mouse button is pressed.
//! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera
@@ -137,7 +138,7 @@ namespace AzToolsFramework
}
//! Returns all EntityComponentIdPairs associated with this manipulator.
const EntityComponentIds& EntityComponentIdPairs() const
const UniqueEntityComponentIds& EntityComponentIdPairs() const
{
return m_entityComponentIdPairs;
}
@@ -147,10 +148,10 @@ namespace AzToolsFramework
//! Remove an entity from being affected by this manipulator.
//! @note All components on this entity registered with the manipulator will be removed.
EntityComponentIds::iterator RemoveEntityId(AZ::EntityId entityId);
UniqueEntityComponentIds::iterator RemoveEntityId(AZ::EntityId entityId);
//! Remove a specific component (via a EntityComponentIdPair) being affected by this manipulator.
EntityComponentIds::iterator RemoveEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair);
UniqueEntityComponentIds::iterator RemoveEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair);
//! Is this entity currently being tracked by this manipulator.
bool HasEntityId(AZ::EntityId entityId) const;
@@ -26,11 +26,21 @@ namespace AzToolsFramework
virtual AZ::Vector3 GetDimensions() = 0;
//! Set the X/Y/Z dimensions of the box shape/collider.
virtual void SetDimensions(const AZ::Vector3& dimensions) = 0;
// O3DE_DEPRECATION_NOTICE(GHI-7572)
//! @deprecated Because non-uniform scale effects can be complex, it is recommended to separately use
//! AZ::TransformBus::Events::GetWorldTM, AZ::NonUniformScaleRequests::GetScale and GetCurrentLocalTransform
//! and combine their effects.
//! Get the transform of the box shape/collider.
//! This is used by \ref BoxComponentMode instead of the \ref \AZ::TransformBus
//! because a collider may have an additional translation/orientation offset from
//! the Entity transform.
virtual AZ::Transform GetCurrentTransform() = 0;
//! Get the transform of the box relative to the entity.
virtual AZ::Transform GetCurrentLocalTransform() = 0;
// O3DE_DEPRECATION_NOTICE(GHI-7572)
//! @deprecated Because non-uniform scale effects can be complex, it is recommended to separately use
//! AZ::TransformBus::Events::GetWorldTM, AZ::NonUniformScaleRequests::GetScale and GetCurrentLocalTransform
//! and combine their effects.
//! Get the scale currently applied to the box.
//! With the Box Shape, the largest x/y/z component is taken
//! so scale is always uniform, with colliders the scale may
@@ -80,23 +80,18 @@ namespace AzToolsFramework
m_onMouseMoveCallback = onMouseMoveCallback;
}
void SurfaceManipulator::InstallEntityIdsToIgnoreFn(EntityIdsToIgnoreFn entityIdsToIgnoreCallback)
{
m_entityIdsToIgnoreFn = AZStd::move(entityIdsToIgnoreCallback);
}
void SurfaceManipulator::OnLeftMouseDownImpl(
const ViewportInteraction::MouseInteraction& interaction, [[maybe_unused]] float rayIntersectionDistance)
{
const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace());
const AzFramework::ViewportId viewportId = interaction.m_interactionId.m_viewportId;
const auto& entityComponentIdPairs = EntityComponentIdPairs();
m_rayRequest.m_entityFilter.m_ignoreEntities.clear();
m_rayRequest.m_entityFilter.m_ignoreEntities.reserve(entityComponentIdPairs.size());
AZStd::transform(
entityComponentIdPairs.begin(), entityComponentIdPairs.end(),
AZStd::inserter(m_rayRequest.m_entityFilter.m_ignoreEntities, m_rayRequest.m_entityFilter.m_ignoreEntities.begin()),
[](const AZ::EntityComponentIdPair& entityComponentIdPair)
{
return entityComponentIdPair.GetEntityId();
});
m_rayRequest.m_entityFilter.m_ignoreEntities = m_entityIdsToIgnoreFn(interaction);
// calculate the start and end of the ray
RefreshRayRequest(
@@ -139,6 +134,8 @@ namespace AzToolsFramework
{
const AzFramework::ViewportId viewportId = interaction.m_interactionId.m_viewportId;
m_rayRequest.m_entityFilter.m_ignoreEntities = m_entityIdsToIgnoreFn(interaction);
// update the start and end of the ray
RefreshRayRequest(
m_rayRequest, ViewportInteraction::ViewportScreenToWorldRay(viewportId, interaction.m_mousePick.m_screenCoordinates),
@@ -162,12 +159,12 @@ namespace AzToolsFramework
const ManipulatorManagerState& managerState,
AzFramework::DebugDisplayRequests& debugDisplay,
const AzFramework::CameraState& cameraState,
const ViewportInteraction::MouseInteraction& mouseInteraction)
const ViewportInteraction::MouseInteraction& interaction)
{
m_manipulatorView->Draw(
GetManipulatorManagerId(), managerState, GetManipulatorId(),
ManipulatorState{ TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalPosition(), MouseOver() }, debugDisplay,
cameraState, mouseInteraction);
cameraState, interaction);
}
void SurfaceManipulator::InvalidateImpl()
@@ -40,6 +40,9 @@ namespace AzToolsFramework
//! A Manipulator must only be created and managed through a shared_ptr.
static AZStd::shared_ptr<SurfaceManipulator> MakeShared(const AZ::Transform& worldFromLocal);
//! Callback function to determine which EntityIds to ignore when performing the ray intersection.
using EntityIdsToIgnoreFn = AZStd::function<UniqueEntityIds(const ViewportInteraction::MouseInteraction&)>;
//! The state of the manipulator at the start of an interaction.
struct Start
{
@@ -77,11 +80,13 @@ namespace AzToolsFramework
void InstallLeftMouseUpCallback(const MouseActionCallback& onMouseUpCallback);
void InstallMouseMoveCallback(const MouseActionCallback& onMouseMoveCallback);
void InstallEntityIdsToIgnoreFn(EntityIdsToIgnoreFn entityIdsToIgnoreFn);
void Draw(
const ManipulatorManagerState& managerState,
AzFramework::DebugDisplayRequests& debugDisplay,
const AzFramework::CameraState& cameraState,
const ViewportInteraction::MouseInteraction& mouseInteraction) override;
const ViewportInteraction::MouseInteraction& interaction) override;
void SetView(AZStd::unique_ptr<ManipulatorView>&& view);
@@ -109,6 +114,9 @@ namespace AzToolsFramework
MouseActionCallback m_onLeftMouseUpCallback = nullptr;
MouseActionCallback m_onMouseMoveCallback = nullptr;
//! Customization point to determine which (if any) EntityIds to ignore while performing the ray intersection.
EntityIdsToIgnoreFn m_entityIdsToIgnoreFn = nullptr;
//! Cached ray request initialized at mouse down and updated during mouse move.
AzFramework::RenderGeometry::RayRequest m_rayRequest;
@@ -145,6 +145,15 @@ namespace AzToolsFramework
}
}
void TranslationManipulators::InstallSurfaceManipulatorEntityIdsToIgnoreFn(
SurfaceManipulator::EntityIdsToIgnoreFn entityIdsToIgnoreFn)
{
if (m_surfaceManipulator)
{
m_surfaceManipulator->InstallEntityIdsToIgnoreFn(AZStd::move(entityIdsToIgnoreFn));
}
}
void TranslationManipulators::SetLocalTransformImpl(const AZ::Transform& localTransform)
{
for (AZStd::shared_ptr<LinearManipulator>& manipulator : m_linearManipulators)
@@ -61,6 +61,8 @@ namespace AzToolsFramework
void InstallSurfaceManipulatorMouseMoveCallback(const SurfaceManipulator::MouseActionCallback& onMouseMoveCallback);
void InstallSurfaceManipulatorMouseUpCallback(const SurfaceManipulator::MouseActionCallback& onMouseUpCallback);
void InstallSurfaceManipulatorEntityIdsToIgnoreFn(SurfaceManipulator::EntityIdsToIgnoreFn entityIdsToIgnoreFn);
void SetSpaceImpl(const AZ::Transform& worldFromLocal) override;
void SetLocalTransformImpl(const AZ::Transform& localTransform) override;
void SetLocalPositionImpl(const AZ::Vector3& localPosition) override;
@@ -23,7 +23,15 @@ namespace AzToolsFramework
//////////////////////////////////////////////////////////////////////////
// ThumbnailKey
//////////////////////////////////////////////////////////////////////////
bool ThumbnailKey::IsReady() const { return m_ready; }
void ThumbnailKey::SetReady(bool ready)
{
m_ready = ready;
}
bool ThumbnailKey::IsReady() const
{
return m_ready;
}
bool ThumbnailKey::UpdateThumbnail()
{
@@ -75,10 +83,8 @@ namespace AzToolsFramework
if (m_state == State::Unloaded)
{
m_state = State::Loading;
QThreadPool* threadPool;
ThumbnailContextRequestBus::BroadcastResult(
threadPool,
&ThumbnailContextRequestBus::Handler::GetThreadPool);
QThreadPool* threadPool = {};
ThumbnailerRequestBus::BroadcastResult(threadPool, &ThumbnailerRequestBus::Handler::GetThreadPool);
QFuture<void> future = QtConcurrent::run(threadPool, [this](){ LoadThread(); });
m_watcher.setFuture(future);
}
@@ -26,13 +26,11 @@ namespace AzToolsFramework
//! ThumbnailKey is used to locate thumbnails in thumbnail cache
/*
ThumbnailKey contains any kind of identifiable information to retrieve thumbnails (e.g. assetId, assetType, filename, etc.)
To use thumbnail system, keep reference to your thumbnail key, and retrieve Thumbnail via ThumbnailerRequestsBus
To use thumbnail system, keep reference to your thumbnail key, and retrieve Thumbnail via ThumbnailerRequestBus
*/
class ThumbnailKey
: public QObject
{
friend class ThumbnailContext;
Q_OBJECT
public:
AZ_RTTI(ThumbnailKey, "{43F20F6B-333D-4226-8E4F-331A62315255}");
@@ -40,6 +38,8 @@ namespace AzToolsFramework
ThumbnailKey() = default;
virtual ~ThumbnailKey() = default;
void SetReady(bool ready);
bool IsReady() const;
virtual bool UpdateThumbnail();
@@ -47,13 +47,13 @@ namespace AzToolsFramework
virtual size_t GetHash() const;
virtual bool Equals(const ThumbnailKey* other) const;
Q_SIGNALS:
//! Updated signal is dispatched whenever thumbnail data was changed. Anyone using this thumbnail should listen to this.
void ThumbnailUpdatedSignal() const;
//! Force update mapped thumbnails
void UpdateThumbnailSignal() const;
private:
bool m_ready = false;
};
@@ -114,11 +114,14 @@ namespace AzToolsFramework
ThumbnailProvider() = default;
virtual ~ThumbnailProvider() = default;
virtual bool GetThumbnail(SharedThumbnailKey key, SharedThumbnail& thumbnail) = 0;
//! Priority identifies ThumbnailProvider order in ThumbnailContext
//! Higher priority means this ThumbnailProvider will take precedence in generating a thumbnail when
//! a supplied ThumbnailKey is supported by multiple providers.
virtual int GetPriority() const { return 0; }
//! A unique ThumbnailProvider name identifying it in a ThumbnailContext
//! Priority identifies ThumbnailProvider order
//! Higher priority means this ThumbnailProvider will take precedence in generating a thumbnail when a supplied ThumbnailKey is
//! supported by multiple providers.
virtual int GetPriority() const
{
return 0;
}
//! A unique ThumbnailProvider name identifyier
virtual const char* GetProviderName() const = 0;
};
@@ -1,132 +0,0 @@
/*
* 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 <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <AzToolsFramework/Thumbnails/MissingThumbnail.h>
#include <AzToolsFramework/Thumbnails/LoadingThumbnail.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
AZ_PUSH_DISABLE_WARNING(4244 4251, "-Wunknown-warning-option") // 4251: 'QImageIOHandler::d_ptr': class 'QScopedPointer<QImageIOHandlerPrivate,QScopedPointerDeleter<T>>' needs to have dll-interface to be used by clients of class 'QImageIOHandler'
#include <AzQtComponents/Components/StyledBusyLabel.h>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
{
namespace Thumbnailer
{
ThumbnailContext::ThumbnailContext()
: m_missingThumbnail(new MissingThumbnail())
, m_loadingThumbnail(new LoadingThumbnail())
, m_threadPool(this)
{
ThumbnailContextRequestBus::Handler::BusConnect();
}
ThumbnailContext::~ThumbnailContext()
{
ThumbnailContextRequestBus::Handler::BusDisconnect();
}
bool ThumbnailContext::IsLoading(SharedThumbnailKey key)
{
SharedThumbnail thumbnail;
for (auto& provider : m_providers)
{
if (provider->GetThumbnail(key, thumbnail))
{
return thumbnail->GetState() == Thumbnail::State::Unloaded ||
thumbnail->GetState() == Thumbnail::State::Loading;
}
}
return false;
}
void ThumbnailContext::RedrawThumbnail()
{
AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Broadcast(&AzToolsFramework::AssetBrowser::AssetBrowserViewRequests::Update);
}
QThreadPool* ThumbnailContext::GetThreadPool()
{
return &m_threadPool;
}
SharedThumbnail ThumbnailContext::GetThumbnail(SharedThumbnailKey key)
{
SharedThumbnail thumbnail;
// find provider who can handle supplied key
for (auto& provider : m_providers)
{
if (provider->GetThumbnail(key, thumbnail))
{
// if thumbnail is ready return it
if (thumbnail->GetState() == Thumbnail::State::Ready)
{
return thumbnail;
}
// if thumbnail is not loaded, start loading it, meanwhile return loading thumbnail
if (thumbnail->GetState() == Thumbnail::State::Unloaded)
{
// listen to the loading signal, so the anyone using it will update loading animation
connect(m_loadingThumbnail.data(), &Thumbnail::Updated, key.data(), &ThumbnailKey::ThumbnailUpdatedSignal);
AzQtComponents::StyledBusyLabel* busyLabel;
AzToolsFramework::AssetBrowser::AssetBrowserComponentRequestBus::BroadcastResult(busyLabel, &AzToolsFramework::AssetBrowser::AssetBrowserComponentRequests::GetStyledBusyLabel);
connect(busyLabel, &AzQtComponents::StyledBusyLabel::repaintNeeded, this, &ThumbnailContext::RedrawThumbnail);
// once the thumbnail is loaded, disconnect it from loading thumbnail
connect(thumbnail.data(), &Thumbnail::Updated, this , [this, key, thumbnail, busyLabel]()
{
disconnect(m_loadingThumbnail.data(), &Thumbnail::Updated, key.data(), &ThumbnailKey::ThumbnailUpdatedSignal);
disconnect(busyLabel, &AzQtComponents::StyledBusyLabel::repaintNeeded, this, &ThumbnailContext::RedrawThumbnail);
thumbnail->disconnect();
connect(thumbnail.data(), &Thumbnail::Updated, key.data(), &ThumbnailKey::ThumbnailUpdatedSignal);
connect(key.data(), &ThumbnailKey::UpdateThumbnailSignal, thumbnail.data(), &Thumbnail::Update);
key->m_ready = true;
Q_EMIT key->ThumbnailUpdatedSignal();
});
thumbnail->Load();
}
if (thumbnail->GetState() == Thumbnail::State::Failed)
{
return m_missingThumbnail;
}
return m_loadingThumbnail;
}
}
return m_missingThumbnail;
}
void ThumbnailContext::RegisterThumbnailProvider(SharedThumbnailProvider providerToAdd)
{
auto it = AZStd::find_if(m_providers.begin(), m_providers.end(), [providerToAdd](const SharedThumbnailProvider& provider)
{
return AZ::StringFunc::Equal(provider->GetProviderName(), providerToAdd->GetProviderName());
});
if (it != m_providers.end())
{
AZ_Error("ThumbnailContext", false, "Provider with name %s is already registered with context.", providerToAdd->GetProviderName());
return;
}
m_providers.insert(providerToAdd);
}
void ThumbnailContext::UnregisterThumbnailProvider(const char* providerName)
{
auto it = AZStd::remove_if(m_providers.begin(), m_providers.end(), [providerName](const SharedThumbnailProvider& provider)
{
return AZ::StringFunc::Equal(provider->GetProviderName(), providerName);
});
m_providers.erase(it, m_providers.end());
}
} // namespace Thumbnailer
} // namespace AzToolsFramework
#include "Thumbnails/moc_ThumbnailContext.cpp"
@@ -1,86 +0,0 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/set.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <QObject>
#include <QList>
#include <QThreadPool>
#endif
class QString;
class QPixmap;
namespace AzToolsFramework
{
namespace Thumbnailer
{
class ThumbnailProvider;
//! ThumbnailContext provides distinct thumbnail location for specific context
/*
There can be any number of contexts for every unique feature that may need different types of thumbnails.
For example 'AssetBrowser' context provides thumbnails specific to Asset Browser
'PreviewContext' may provide thumbnails for Preview Widget
'MaterialBrowser' may provide thumbnails for Material Browser
etc.
*/
class ThumbnailContext
: public QObject
, public ThumbnailContextRequestBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ThumbnailContext, AZ::SystemAllocator, 0);
ThumbnailContext();
~ThumbnailContext() override;
//! Is the thumbnail currently loading or is about to load.
bool IsLoading(SharedThumbnailKey key);
//! Retrieve thumbnail by key, generate one if needed
SharedThumbnail GetThumbnail(SharedThumbnailKey key);
//! Add new thumbnail cache
void RegisterThumbnailProvider(SharedThumbnailProvider providerToAdd);
//! Remove thumbnail cache by name if found
void UnregisterThumbnailProvider(const char* providerName);
void RedrawThumbnail();
//! Default context used for most thumbnails
static constexpr const char* DefaultContext = "Default";
// ThumbnailContextRequestBus::Handler interface overrides...
QThreadPool* GetThreadPool() override;
private:
struct ProviderCompare {
bool operator() (const SharedThumbnailProvider& lhs, const SharedThumbnailProvider& rhs) const
{
// sorting in reverse, higher priority means the provider should be considered first
return lhs->GetPriority() > rhs->GetPriority();
}
};
//! Collection of thumbnail caches provided by this context
AZStd::multiset<SharedThumbnailProvider, ProviderCompare> m_providers;
//! Default missing thumbnail used when no thumbnail for given key can be found within this context
SharedThumbnail m_missingThumbnail;
//! Default loading thumbnail used when thumbnail is found by is not yet generated
SharedThumbnail m_loadingThumbnail;
//! There is only a limited number of threads on global threadPool, because there can be many thumbnails rendering at once
//! an individual threadPool is needed to avoid deadlocks
QThreadPool m_threadPool;
};
} // namespace Thumbnailer
} // namespace AzToolsFramework
@@ -30,14 +30,13 @@ namespace AzToolsFramework
{
}
void ThumbnailWidget::SetThumbnailKey(SharedThumbnailKey key, const char* contextName)
void ThumbnailWidget::SetThumbnailKey(SharedThumbnailKey key)
{
if (m_key)
{
disconnect(m_key.data(), &ThumbnailKey::ThumbnailUpdatedSignal, this, &ThumbnailWidget::KeyUpdatedSlot);
}
m_key = key;
m_contextName = contextName;
connect(m_key.data(), &ThumbnailKey::ThumbnailUpdatedSignal, this, &ThumbnailWidget::KeyUpdatedSlot);
repaint();
}
@@ -65,7 +64,7 @@ namespace AzToolsFramework
{
// thumbnail instance is not stored locally, but retrieved each paintEvent since thumbnail mapped to a specific key may change
SharedThumbnail thumbnail;
ThumbnailerRequestsBus::BroadcastResult(thumbnail, &ThumbnailerRequests::GetThumbnail, m_key, m_contextName.c_str());
ThumbnailerRequestBus::BroadcastResult(thumbnail, &ThumbnailerRequests::GetThumbnail, m_key);
QPainter painter(this);
// Scaling and centering pixmap within bounds to preserve aspect ratio
@@ -32,7 +32,7 @@ namespace AzToolsFramework
~ThumbnailWidget() override = default;
//! Call this to set what thumbnail widget will display
void SetThumbnailKey(SharedThumbnailKey key, const char* contextName = "Default");
void SetThumbnailKey(SharedThumbnailKey key);
//! Remove current thumbnail
void ClearThumbnail();
@@ -44,7 +44,6 @@ namespace AzToolsFramework
private:
SharedThumbnailKey m_key;
AZStd::string m_contextName;
private Q_SLOTS:
void KeyUpdatedSlot();
@@ -19,48 +19,30 @@ namespace AzToolsFramework
{
namespace Thumbnailer
{
//! Interaction with thumbnail context
class ThumbnailContextRequests
: public AZ::EBusTraits
{
public:
//! Get thread pool for drawing thumbnails
virtual QThreadPool* GetThreadPool() = 0;
};
using ThumbnailContextRequestBus = AZ::EBus<ThumbnailContextRequests>;
//! Interaction with thumbnailer
class ThumbnailerRequests
: public AZ::EBusTraits
{
public:
//! Add thumbnail context
virtual void RegisterContext(const char* contextName) = 0;
//! Add new thumbnail provider
virtual void RegisterThumbnailProvider(SharedThumbnailProvider provider) = 0;
//! Remove thumbnail context and all associated ThumbnailProviders
virtual void UnregisterContext(const char* contextName) = 0;
//! Return whether a given ThumbnailContext has been registered
virtual bool HasContext(const char* contextName) const = 0;
//! Add new thumbnail provider to ThumbnailContext
virtual void RegisterThumbnailProvider(SharedThumbnailProvider provider, const char* contextName) = 0;
//! Remove thumbnail provider from ThumbnailContext
virtual void UnregisterThumbnailProvider(const char* providerName, const char* contextName) = 0;
//! Remove thumbnail provider
virtual void UnregisterThumbnailProvider(const char* providerName) = 0;
//! Retrieve thumbnail by key,
//! if no thumbnail matching found, one of ThumbnailProviders will attempt to create
//! If no compatible providers found, MissingThumbnail will be returned
virtual SharedThumbnail GetThumbnail(SharedThumbnailKey thumbnailKey, const char* contextName) = 0;
virtual SharedThumbnail GetThumbnail(SharedThumbnailKey thumbnailKey) = 0;
//! Return whether the thumbnail is loading.
virtual bool IsLoading(SharedThumbnailKey thumbnailKey, const char* contextName) = 0;
virtual bool IsLoading(SharedThumbnailKey thumbnailKey) = 0;
//! Get thread pool for drawing thumbnails
virtual QThreadPool* GetThreadPool() = 0;
};
using ThumbnailerRequestBus = AZ::EBus<ThumbnailerRequests>;
using ThumbnailerRequestsBus = AZ::EBus<ThumbnailerRequests>; //deprecated
//! Request product thumbnail to be rendered
class ThumbnailerRendererRequests
@@ -6,11 +6,14 @@
*
*/
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzQtComponents/Components/StyledBusyLabel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/Thumbnails/LoadingThumbnail.h>
#include <AzToolsFramework/Thumbnails/MissingThumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerComponent.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <QApplication>
#include <QStyle>
@@ -19,8 +22,10 @@ namespace AzToolsFramework
{
namespace Thumbnailer
{
ThumbnailerComponent::ThumbnailerComponent()
: m_missingThumbnail(new MissingThumbnail())
, m_loadingThumbnail(new LoadingThumbnail())
, m_threadPool(this)
{
}
@@ -28,14 +33,13 @@ namespace AzToolsFramework
void ThumbnailerComponent::Activate()
{
RegisterContext(ThumbnailContext::DefaultContext);
BusConnect();
ThumbnailerRequestBus::Handler::BusConnect();
}
void ThumbnailerComponent::Deactivate()
{
BusDisconnect();
m_thumbnails.clear();
ThumbnailerRequestBus::Handler::BusDisconnect();
m_providers.clear();
}
void ThumbnailerComponent::Reflect(AZ::ReflectContext* context)
@@ -49,59 +53,114 @@ namespace AzToolsFramework
void ThumbnailerComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("ThumbnailerService", 0x65422b97));
incompatible.push_back(AZ_CRC_CE("ThumbnailerService"));
}
void ThumbnailerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("ThumbnailerService", 0x65422b97));
provided.push_back(AZ_CRC_CE("ThumbnailerService"));
}
void ThumbnailerComponent::RegisterContext(const char* contextName)
void ThumbnailerComponent::RegisterThumbnailProvider(SharedThumbnailProvider provider)
{
AZ_Assert(m_thumbnails.find(contextName) == m_thumbnails.end(), "Context %s already registered", contextName);
m_thumbnails[contextName] = AZStd::make_shared<ThumbnailContext>();
auto it = AZStd::find_if(m_providers.begin(), m_providers.end(), [provider](const SharedThumbnailProvider& existingProvider)
{
return AZ::StringFunc::Equal(provider->GetProviderName(), existingProvider->GetProviderName());
});
if (it != m_providers.end())
{
AZ_Error("ThumbnailerComponent", false, "Provider with name %s is already registered with context.", provider->GetProviderName());
return;
}
m_providers.insert(provider);
}
void ThumbnailerComponent::UnregisterContext(const char* contextName)
void ThumbnailerComponent::UnregisterThumbnailProvider(const char* providerName)
{
AZ_Assert(m_thumbnails.find(contextName) != m_thumbnails.end(), "Context %s not registered", contextName);
m_thumbnails.erase(contextName);
AZStd::erase_if(
m_providers,
[providerName](const SharedThumbnailProvider& provider)
{
return AZ::StringFunc::Equal(provider->GetProviderName(), providerName);
});
}
bool ThumbnailerComponent::HasContext(const char* contextName) const
SharedThumbnail ThumbnailerComponent::GetThumbnail(SharedThumbnailKey key)
{
return m_thumbnails.find(contextName) != m_thumbnails.end();
// find provider who can handle supplied key
for (auto& provider : m_providers)
{
SharedThumbnail thumbnail;
if (provider->GetThumbnail(key, thumbnail))
{
// if thumbnail is ready return it
if (thumbnail->GetState() == Thumbnail::State::Ready)
{
return thumbnail;
}
// if thumbnail is not loaded, start loading it, meanwhile return loading thumbnail
if (thumbnail->GetState() == Thumbnail::State::Unloaded)
{
// listen to the loading signal, so the anyone using it will update loading animation
AzQtComponents::StyledBusyLabel* busyLabel;
AssetBrowser::AssetBrowserComponentRequestBus::BroadcastResult(busyLabel, &AssetBrowser::AssetBrowserComponentRequests::GetStyledBusyLabel);
QObject::connect(m_loadingThumbnail.data(), &Thumbnail::Updated, key.data(), &ThumbnailKey::ThumbnailUpdatedSignal);
QObject::connect(busyLabel, &AzQtComponents::StyledBusyLabel::repaintNeeded, this, &ThumbnailerComponent::RedrawThumbnail);
// once the thumbnail is loaded, disconnect it from loading thumbnail
QObject::connect(thumbnail.data(), &Thumbnail::Updated, this , [this, key, thumbnail, busyLabel]()
{
QObject::disconnect(m_loadingThumbnail.data(), &Thumbnail::Updated, key.data(), &ThumbnailKey::ThumbnailUpdatedSignal);
QObject::disconnect(busyLabel, &AzQtComponents::StyledBusyLabel::repaintNeeded, this, &ThumbnailerComponent::RedrawThumbnail);
thumbnail->disconnect();
QObject::connect(thumbnail.data(), &Thumbnail::Updated, key.data(), &ThumbnailKey::ThumbnailUpdatedSignal);
QObject::connect(key.data(), &ThumbnailKey::UpdateThumbnailSignal, thumbnail.data(), &Thumbnail::Update);
key->SetReady(true);
Q_EMIT key->ThumbnailUpdatedSignal();
});
thumbnail->Load();
}
if (thumbnail->GetState() == Thumbnail::State::Failed)
{
return m_missingThumbnail;
}
return m_loadingThumbnail;
}
}
return m_missingThumbnail;
}
void ThumbnailerComponent::RegisterThumbnailProvider(SharedThumbnailProvider provider, const char* contextName)
bool ThumbnailerComponent::IsLoading(SharedThumbnailKey key)
{
auto it = m_thumbnails.find(contextName);
AZ_Assert(it != m_thumbnails.end(), "Context %s not registered", contextName);
it->second->RegisterThumbnailProvider(provider);
for (auto& provider : m_providers)
{
SharedThumbnail thumbnail;
if (provider->GetThumbnail(key, thumbnail))
{
return thumbnail->GetState() == Thumbnail::State::Unloaded || thumbnail->GetState() == Thumbnail::State::Loading;
}
}
return false;
}
void ThumbnailerComponent::UnregisterThumbnailProvider(const char* providerName, const char* contextName)
QThreadPool* ThumbnailerComponent::GetThreadPool()
{
auto it = m_thumbnails.find(contextName);
AZ_Assert(it != m_thumbnails.end(), "Context %s not registered", contextName);
it->second->UnregisterThumbnailProvider(providerName);
return &m_threadPool;
}
SharedThumbnail ThumbnailerComponent::GetThumbnail(SharedThumbnailKey key, const char* contextName)
void ThumbnailerComponent::RedrawThumbnail()
{
auto it = m_thumbnails.find(contextName);
AZ_Assert(it != m_thumbnails.end(), "Context %s not registered", contextName);
return it->second->GetThumbnail(key);
AssetBrowser::AssetBrowserViewRequestBus::Broadcast(&AssetBrowser::AssetBrowserViewRequests::Update);
}
bool ThumbnailerComponent::IsLoading(SharedThumbnailKey key, const char* contextName)
{
auto it = m_thumbnails.find(contextName);
AZ_Assert(it != m_thumbnails.end(), "Context %s not registered", contextName);
return it->second->IsLoading(key);
}
} // namespace Thumbnailer
} // namespace AzToolsFramework
@@ -7,19 +7,29 @@
*/
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
#if !defined(Q_MOC_RUN)
#include <AzCore/Component/Component.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/set.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <QList>
#include <QObject>
#include <QThreadPool>
#endif
class QString;
class QPixmap;
namespace AzToolsFramework
{
namespace Thumbnailer
{
class ThumbnailContext;
class ThumbnailerComponent
: public AZ::Component
, public ThumbnailerRequestsBus::Handler
, public ThumbnailerRequestBus::Handler
, public QObject
{
public:
AZ_COMPONENT(ThumbnailerComponent, "{80090CA5-6A3A-4554-B5FE-A6D74ECB2D84}")
@@ -27,28 +37,41 @@ namespace AzToolsFramework
ThumbnailerComponent();
virtual ~ThumbnailerComponent();
//////////////////////////////////////////////////////////////////////////
// AZ::Component
//////////////////////////////////////////////////////////////////////////
// AZ::Component overrides...
void Activate() override;
void Deactivate() override;
static void Reflect(AZ::ReflectContext* context);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
//////////////////////////////////////////////////////////////////////////
// ThumbnailerRequests
//////////////////////////////////////////////////////////////////////////
void RegisterContext(const char* contextName) override;
void UnregisterContext(const char* contextName) override;
bool HasContext(const char* contextName) const override;
void RegisterThumbnailProvider(SharedThumbnailProvider provider, const char* contextName) override;
void UnregisterThumbnailProvider(const char* providerName, const char* contextName) override;
SharedThumbnail GetThumbnail(SharedThumbnailKey thumbnailKey, const char* contextName) override;
bool IsLoading(SharedThumbnailKey thumbnailKey, const char* contextName) override;
// ThumbnailerRequestBus::Handler interface overrides...
void RegisterThumbnailProvider(SharedThumbnailProvider provider) override;
void UnregisterThumbnailProvider(const char* providerName) override;
SharedThumbnail GetThumbnail(SharedThumbnailKey thumbnailKey) override;
bool IsLoading(SharedThumbnailKey thumbnailKey) override;
QThreadPool* GetThreadPool() override;
void RedrawThumbnail();
private:
AZStd::unordered_map<AZStd::string, AZStd::shared_ptr<ThumbnailContext>> m_thumbnails;
struct ProviderCompare
{
bool operator()(const SharedThumbnailProvider& lhs, const SharedThumbnailProvider& rhs) const
{
// sorting in reverse, higher priority means the provider should be considered first
return lhs->GetPriority() > rhs->GetPriority();
}
};
//! Collection of thumbnail caches provided by this context
AZStd::multiset<SharedThumbnailProvider, ProviderCompare> m_providers;
//! Default missing thumbnail used when no thumbnail for given key can be found within this context
SharedThumbnail m_missingThumbnail;
//! Default loading thumbnail used when thumbnail is found by is not yet generated
SharedThumbnail m_loadingThumbnail;
//! There is only a limited number of threads on global threadPool, because there can be many thumbnails rendering at once
//! an individual threadPool is needed to avoid deadlocks
QThreadPool m_threadPool;
};
} // Thumbnailer
} // namespace AssetBrowser
@@ -9,7 +9,6 @@
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerNullComponent.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <AzToolsFramework/Thumbnails/MissingThumbnail.h>
namespace AzToolsFramework
@@ -47,35 +46,27 @@ namespace AzToolsFramework
services.push_back(AZ_CRC("ThumbnailerService", 0x65422b97));
}
void ThumbnailerNullComponent::RegisterContext(const char* /*contextName*/)
void ThumbnailerNullComponent::RegisterThumbnailProvider(AzToolsFramework::Thumbnailer::SharedThumbnailProvider /*provider*/)
{
}
void ThumbnailerNullComponent::UnregisterContext(const char* /*contextName*/)
void ThumbnailerNullComponent::UnregisterThumbnailProvider(const char* /*providerName*/)
{
}
bool ThumbnailerNullComponent::HasContext(const char* /*contextName*/) const
{
return false;
}
void ThumbnailerNullComponent::RegisterThumbnailProvider(AzToolsFramework::Thumbnailer::SharedThumbnailProvider /*provider*/, const char* /*contextName*/)
{
}
void ThumbnailerNullComponent::UnregisterThumbnailProvider(const char* /*providerName*/, const char* /*contextName*/)
{
}
AzToolsFramework::Thumbnailer::SharedThumbnail ThumbnailerNullComponent::GetThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey /*key*/, const char* /*contextName*/)
AzToolsFramework::Thumbnailer::SharedThumbnail ThumbnailerNullComponent::GetThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey /*key*/)
{
return m_nullThumbnail;
}
bool ThumbnailerNullComponent::IsLoading(AzToolsFramework::Thumbnailer::SharedThumbnailKey /*thumbnailKey*/, const char* /*contextName*/)
bool ThumbnailerNullComponent::IsLoading(AzToolsFramework::Thumbnailer::SharedThumbnailKey /*thumbnailKey*/)
{
return false;
}
QThreadPool* ThumbnailerNullComponent::GetThreadPool()
{
return nullptr;
}
} // namespace Thumbnailer
} // namespace AzToolsFramework
@@ -19,11 +19,9 @@ namespace AzToolsFramework
{
namespace Thumbnailer
{
class ThumbnailContext;
class ThumbnailerNullComponent
: public AZ::Component
, public AzToolsFramework::Thumbnailer::ThumbnailerRequestsBus::Handler
, public AzToolsFramework::Thumbnailer::ThumbnailerRequestBus::Handler
{
public:
AZ_COMPONENT(ThumbnailerNullComponent, "{8009D651-3FAA-9815-B99E-AF174A3B29D4}")
@@ -42,13 +40,12 @@ namespace AzToolsFramework
//////////////////////////////////////////////////////////////////////////
// ThumbnailerRequests
//////////////////////////////////////////////////////////////////////////
void RegisterContext(const char* contextName) override;
void UnregisterContext(const char* contextName) override;
bool HasContext(const char* contextName) const override;
void RegisterThumbnailProvider(AzToolsFramework::Thumbnailer::SharedThumbnailProvider provider, const char* contextName) override;
void UnregisterThumbnailProvider(const char* providerName, const char* contextName) override;
AzToolsFramework::Thumbnailer::SharedThumbnail GetThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, const char* contextName) override;
bool IsLoading(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, const char* contextName) override;
void RegisterThumbnailProvider(AzToolsFramework::Thumbnailer::SharedThumbnailProvider provider) override;
void UnregisterThumbnailProvider(const char* providerName) override;
AzToolsFramework::Thumbnailer::SharedThumbnail GetThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey) override;
bool IsLoading(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey) override;
QThreadPool* GetThreadPool() override;
private:
AzToolsFramework::Thumbnailer::SharedThumbnail m_nullThumbnail;
};
@@ -7,6 +7,7 @@
*/
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
#include <AzCore/Interface/Interface.h>
@@ -117,9 +118,16 @@ namespace AzToolsFramework
{
}
bool EditorEntityUiHandlerBase::OnEntityDoubleClick([[maybe_unused]] AZ::EntityId entityId) const
bool EditorEntityUiHandlerBase::OnOutlinerItemDoubleClick([[maybe_unused]] const QModelIndex& index) const
{
return false;
}
AZ::EntityId EditorEntityUiHandlerBase::GetEntityIdFromIndex(const QModelIndex& index)
{
QModelIndex firstColumnIndex = index.siblingAtColumn(EntityOutlinerListModel::ColumnName);
return AZ::EntityId(firstColumnIndex.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
}
} // namespace AzToolsFramework
@@ -21,7 +21,6 @@ class QTreeView;
namespace AzToolsFramework
{
//! Defines a handler that can customize entity UI appearance and behavior in the Entity Outliner.
//! This class is meant to be abstract, entities do not have a handler by default.
class EditorEntityUiHandlerBase
{
protected:
@@ -33,7 +32,7 @@ namespace AzToolsFramework
public:
EditorEntityUiHandlerId GetHandlerId();
// # Entity Outliner
// # Entity Outliner Item
//! Returns the item info string that is appended to the item name in the Outliner.
virtual QString GenerateItemInfoString(AZ::EntityId entityId) const;
@@ -41,10 +40,12 @@ namespace AzToolsFramework
virtual QString GenerateItemTooltip(AZ::EntityId entityId) const;
//! Returns the item icon pixmap to display in the Outliner.
virtual QIcon GenerateItemIcon(AZ::EntityId entityId) const;
//! Returns whether the element's lock and visibility state should be accessible in the Outliner
virtual bool CanToggleLockVisibility(AZ::EntityId entityId) const;
//! Returns whether the element's name should be editable
virtual bool CanRename(AZ::EntityId entityId) const;
//! Returns whether the element's lock and visibility state should be accessible in the Outliner
virtual bool CanToggleLockVisibility(AZ::EntityId entityId) const;
// Qt-specific painting functions
//! Paints the background of the item in the Outliner.
virtual void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
@@ -54,24 +55,27 @@ namespace AzToolsFramework
//! Paints the background of the descendant branches of the item in the Outliner.
virtual void PaintDescendantBranchBackground(QPainter* painter, const QTreeView* view, const QRect& rect,
const QModelIndex& index, const QModelIndex& descendantIndex) const;
//! Paints visual elements on the foreground of the item in the Outliner.
virtual void PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
//! Paints visual elements on the foreground of the descendants of the item in the Outliner.
virtual void PaintDescendantForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
const QModelIndex& descendantIndex) const;
// Outliner-specific interactions
//! Triggered when the entity is clicked in the Outliner.
//! @return True if the click has been handled and should not be propagated, false otherwise.
virtual bool OnOutlinerItemClick(const QPoint& position, const QStyleOptionViewItem& option, const QModelIndex& index) const;
//! Triggered when the entity is double-clicked in the Outliner.
//! @return True if the double-click has been handled and should not be propagated, false otherwise.
virtual bool OnOutlinerItemDoubleClick(const QModelIndex& index) const;
//! Triggered when an entity's children are expanded in the Outliner.
virtual void OnOutlinerItemExpand(const QModelIndex& index) const;
//! Triggered when an entity's children are collapsed in the Outliner.
virtual void OnOutlinerItemCollapse(const QModelIndex& index) const;
//! Triggered when the entity is double clicked in the Outliner or in the Viewport.
//! @return True if the double click has been handled and should not be propagated, false otherwise.
virtual bool OnEntityDoubleClick(AZ::EntityId entityId) const;
protected:
static AZ::EntityId GetEntityIdFromIndex(const QModelIndex& index);
private:
EditorEntityUiHandlerId m_handlerId = 0;

Some files were not shown because too many files have changed in this diff Show More