diff --git a/Code/Framework/AzCore/AzCore/IO/Path/Path.cpp b/Code/Framework/AzCore/AzCore/IO/Path/Path.cpp index 6e4dfdaa63..028ffaf535 100644 --- a/Code/Framework/AzCore/AzCore/IO/Path/Path.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Path/Path.cpp @@ -15,9 +15,9 @@ namespace AZ::IO // Class template instantations template class BasicPath; template class BasicPath; - template class PathIterator; - template class PathIterator; - template class PathIterator; + template class PathIterator; + template class PathIterator; + template class PathIterator; // Swap function instantiations template void swap(Path& lhs, Path& rhs) noexcept; @@ -38,16 +38,16 @@ namespace AZ::IO const typename BasicPath::value_type* rhs); // Iterator compare instantiations - template bool operator==(const PathIterator& lhs, - const PathIterator& rhs); - template bool operator==(const PathIterator& lhs, - const PathIterator& rhs); - template bool operator==(const PathIterator& lhs, - const PathIterator& rhs); - template bool operator!=(const PathIterator& lhs, - const PathIterator& rhs); - template bool operator!=(const PathIterator& lhs, - const PathIterator& rhs); - template bool operator!=(const PathIterator& lhs, - const PathIterator& rhs); + template bool operator==(const PathIterator& lhs, + const PathIterator& rhs); + template bool operator==(const PathIterator& lhs, + const PathIterator& rhs); + template bool operator==(const PathIterator& lhs, + const PathIterator& rhs); + template bool operator!=(const PathIterator& lhs, + const PathIterator& rhs); + template bool operator!=(const PathIterator& lhs, + const PathIterator& rhs); + template bool operator!=(const PathIterator& lhs, + const PathIterator& rhs); } diff --git a/Code/Framework/AzCore/AzCore/IO/Path/Path.h b/Code/Framework/AzCore/AzCore/IO/Path/Path.h index 7f89809294..235310a5da 100644 --- a/Code/Framework/AzCore/AzCore/IO/Path/Path.h +++ b/Code/Framework/AzCore/AzCore/IO/Path/Path.h @@ -43,9 +43,9 @@ namespace AZ::IO public: using string_view_type = AZStd::string_view; using value_type = char; - using const_iterator = const PathIterator; + using const_iterator = PathIterator; using iterator = const_iterator; - friend PathIterator; + friend const_iterator; // constructors and destructor constexpr PathView() = default; @@ -319,9 +319,9 @@ namespace AZ::IO using value_type = typename StringType::value_type; using traits_type = typename StringType::traits_type; using string_view_type = AZStd::string_view; - using const_iterator = const PathIterator; + using const_iterator = PathIterator; using iterator = const_iterator; - friend PathIterator; + friend const_iterator; // constructors and destructor constexpr BasicPath() = default; @@ -692,7 +692,7 @@ namespace AZ::IO friend PathType; using iterator_category = AZStd::bidirectional_iterator_tag; - using value_type = PathType; + using value_type = AZStd::remove_cv_t; using difference_type = ptrdiff_t; using pointer = const value_type*; using reference = const value_type&; @@ -703,8 +703,9 @@ namespace AZ::IO constexpr PathIterator() = default; constexpr PathIterator(const PathIterator&) = default; - + constexpr PathIterator(PathIterator&&) noexcept = default; constexpr PathIterator& operator=(const PathIterator&) = default; + constexpr PathIterator& operator=(PathIterator&&) noexcept = default; constexpr reference operator*() const; @@ -733,10 +734,10 @@ namespace AZ::IO ParserState m_state{ Singular }; }; - template - constexpr bool operator==(const PathIterator& lhs, const PathIterator& rhs); - template - constexpr bool operator!=(const PathIterator& lhs, const PathIterator& rhs); + template + constexpr bool operator==(const PathIterator& lhs, const PathIterator& rhs); + template + constexpr bool operator!=(const PathIterator& lhs, const PathIterator& rhs); } #include diff --git a/Code/Framework/AzCore/AzCore/IO/Path/Path.inl b/Code/Framework/AzCore/AzCore/IO/Path/Path.inl index ab991e1750..bde2353112 100644 --- a/Code/Framework/AzCore/AzCore/IO/Path/Path.inl +++ b/Code/Framework/AzCore/AzCore/IO/Path/Path.inl @@ -399,7 +399,7 @@ namespace AZ::IO constexpr auto PathView::begin() const -> const_iterator { auto pathParser = parser::PathParser::CreateBegin(m_path, m_preferred_separator); - PathIterator it; + const_iterator it; it.m_path_ref = this; it.m_state = static_cast(pathParser.m_parser_state); it.m_path_entry_view = pathParser.m_path_raw_entry; @@ -409,7 +409,7 @@ namespace AZ::IO constexpr auto PathView::end() const -> const_iterator { - PathIterator it; + const_iterator it; it.m_state = const_iterator::AtEnd; it.m_path_ref = this; return it; @@ -1262,7 +1262,7 @@ namespace AZ::IO constexpr auto BasicPath::begin() const -> const_iterator { auto pathParser = parser::PathParser::CreateBegin(m_path, m_preferred_separator); - PathIterator it; + const_iterator it; it.m_path_ref = this; it.m_state = static_cast(pathParser.m_parser_state); it.m_path_entry_view = pathParser.m_path_raw_entry; @@ -1273,7 +1273,7 @@ namespace AZ::IO template constexpr auto BasicPath::end() const -> const_iterator { - PathIterator it; + const_iterator it; it.m_state = const_iterator::AtEnd; it.m_path_ref = this; return it; @@ -1529,16 +1529,16 @@ namespace AZ::IO const typename BasicPath::value_type* rhs); // Iterator compare explicit declarations - extern template bool operator==(const PathIterator& lhs, - const PathIterator& rhs); - extern template bool operator==(const PathIterator& lhs, - const PathIterator& rhs); - extern template bool operator==(const PathIterator& lhs, - const PathIterator& rhs); - extern template bool operator!=(const PathIterator& lhs, - const PathIterator& rhs); - extern template bool operator!=(const PathIterator& lhs, - const PathIterator& rhs); - extern template bool operator!=(const PathIterator& lhs, - const PathIterator& rhs); + extern template bool operator==(const PathIterator& lhs, + const PathIterator& rhs); + extern template bool operator==(const PathIterator& lhs, + const PathIterator& rhs); + extern template bool operator==(const PathIterator& lhs, + const PathIterator& rhs); + extern template bool operator!=(const PathIterator& lhs, + const PathIterator& rhs); + extern template bool operator!=(const PathIterator& lhs, + const PathIterator& rhs); + extern template bool operator!=(const PathIterator& lhs, + const PathIterator& rhs); } diff --git a/Code/Framework/AzCore/AzCore/IO/Path/PathParser.inl b/Code/Framework/AzCore/AzCore/IO/Path/PathParser.inl index b19c518ff9..f8712551b0 100644 --- a/Code/Framework/AzCore/AzCore/IO/Path/PathParser.inl +++ b/Code/Framework/AzCore/AzCore/IO/Path/PathParser.inl @@ -10,6 +10,7 @@ #include #include +#include namespace AZ::IO::Internal { @@ -17,7 +18,7 @@ namespace AZ::IO::Internal { return elem == '/' || elem == '\\'; } - template >> + template >> static constexpr bool HasDrivePrefix(InputIt first, EndIt last) { size_t prefixSize = AZStd::distance(first, last); @@ -46,7 +47,7 @@ namespace AZ::IO::Internal //! Windows root names can have include drive letter within them template constexpr auto ConsumeRootName(InputIt entryBeginIter, InputIt entryEndIter, const char preferredSeparator) - -> AZStd::enable_if_t, InputIt> + -> AZStd::enable_if_t, InputIt> { if (preferredSeparator == PosixPathSeparator) { @@ -147,7 +148,7 @@ namespace AZ::IO::Internal //! If the preferred separator is '/' just checks if the path starts with a '/ //! Otherwise a check for a Windows absolute path occurs //! Windows absolute paths can include a RootName - template >> + template >> static constexpr bool IsAbsolute(InputIt first, EndIt last, const char preferredSeparator) { size_t pathSize = AZStd::distance(first, last); @@ -208,11 +209,11 @@ namespace AZ::IO::parser enum ParserState : uint8_t { // Zero is a special sentinel value used by default constructed iterators. - PS_BeforeBegin = PathIterator::BeforeBegin, - PS_InRootName = PathIterator::InRootName, - PS_InRootDir = PathIterator::InRootDir, - PS_InFilenames = PathIterator::InFilenames, - PS_AtEnd = PathIterator::AtEnd + PS_BeforeBegin = PathView::const_iterator::BeforeBegin, + PS_InRootName = PathView::const_iterator::InRootName, + PS_InRootDir = PathView::const_iterator::InRootDir, + PS_InFilenames = PathView::const_iterator::InFilenames, + PS_AtEnd = PathView::const_iterator::AtEnd }; struct PathParser diff --git a/Code/Framework/AzCore/AzCore/PlatformDef.h b/Code/Framework/AzCore/AzCore/PlatformDef.h index 8609ad5756..8416099f15 100644 --- a/Code/Framework/AzCore/AzCore/PlatformDef.h +++ b/Code/Framework/AzCore/AzCore/PlatformDef.h @@ -248,14 +248,14 @@ #if defined(__has_builtin) #if __has_builtin(__builtin_is_constant_evaluated) #define az_builtin_is_constant_evaluated() __builtin_is_constant_evaluated() - #define az_has_builtin_is_constant_evaluated() true + #define az_has_builtin_is_constant_evaluated true #endif #elif AZ_COMPILER_MSVC >= 1928 #define az_builtin_is_constant_evaluated() __builtin_is_constant_evaluated() - #define az_has_builtin_is_constant_evaluated() true + #define az_has_builtin_is_constant_evaluated true #elif AZ_COMPILER_GCC #define az_builtin_is_constant_evaluated() __builtin_is_constant_evaluated() - #define az_has_builtin_is_constant_evaluated() true + #define az_has_builtin_is_constant_evaluated true #endif #endif @@ -271,7 +271,7 @@ } } #define az_builtin_is_constant_evaluated() AZ::Internal::builtin_is_constant_evaluated() - #define az_has_builtin_is_constant_evaluated() false + #define az_has_builtin_is_constant_evaluated false #endif // define builtin functions used by char_traits class for efficient compile time and runtime diff --git a/Code/Framework/AzCore/AzCore/std/azstd_files.cmake b/Code/Framework/AzCore/AzCore/std/azstd_files.cmake index d516a56295..fe169c4964 100644 --- a/Code/Framework/AzCore/AzCore/std/azstd_files.cmake +++ b/Code/Framework/AzCore/AzCore/std/azstd_files.cmake @@ -19,6 +19,7 @@ set(FILES any.h base.h config.h + concepts/concepts.h createdestroy.h docs.h exceptions.h @@ -27,11 +28,14 @@ set(FILES hash.cpp hash.h hash_table.h + iterator/iterator_primitives.h iterator.h limits.h numeric.h math.h optional.h + ranges/iter_move.h + ranges/ranges.h ratio.h reference_wrapper.h sort.h @@ -151,6 +155,7 @@ set(FILES typetraits/alignment_of.h typetraits/config.h typetraits/common_type.h + typetraits/common_reference.h typetraits/conjunction.h typetraits/disjunction.h typetraits/extent.h @@ -217,4 +222,6 @@ set(FILES typetraits/void_t.h typetraits/internal/type_sequence_traits.h typetraits/internal/is_template_copy_constructible.h + utility/declval.h + utility/move.h ) diff --git a/Code/Framework/AzCore/AzCore/std/base.h b/Code/Framework/AzCore/AzCore/std/base.h index 46d1821328..44b11c61ef 100644 --- a/Code/Framework/AzCore/AzCore/std/base.h +++ b/Code/Framework/AzCore/AzCore/std/base.h @@ -30,4 +30,6 @@ namespace AZStd using std::nullptr_t; using sys_time_t = AZ::s64; + + using std::byte; } diff --git a/Code/Framework/AzCore/AzCore/std/concepts/concepts.h b/Code/Framework/AzCore/AzCore/std/concepts/concepts.h new file mode 100644 index 0000000000..420b671f31 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/std/concepts/concepts.h @@ -0,0 +1,840 @@ +/* + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AZStd +{ + // alias std::pointer_traits into the AZStd::namespace + using std::pointer_traits; + + // Alias re-declarations from iterator.h + /// Identifying tag for input iterators. + using input_iterator_tag = std::input_iterator_tag; + /// Identifying tag for output iterators. + using output_iterator_tag = std::output_iterator_tag; + /// Identifying tag for forward iterators. + using forward_iterator_tag = std::forward_iterator_tag; + /// Identifying tag for bidirectional iterators. + using bidirectional_iterator_tag = std::bidirectional_iterator_tag; + /// Identifying tag for random-access iterators. + using random_access_iterator_tag = std::random_access_iterator_tag; + /// Identifying tag for contagious iterators + struct contiguous_iterator_tag; +} + +namespace AZStd::Internal +{ + template + constexpr bool pointer_traits_has_to_address_v = false; + + template + constexpr bool pointer_traits_has_to_address_v::to_address(declval()))>>> > = true; + + + // pointer_traits isn't SFINAE friendly https://cplusplus.github.io/LWG/lwg-active.html#3545 + // So working around that by checking if type T has an element_type alias + template + constexpr bool pointer_traits_valid_and_has_to_address_v = false; + template + constexpr bool pointer_traits_valid_and_has_to_address_v> > + = pointer_traits_has_to_address_v; +} + +namespace AZStd +{ + //! Implements the C++20 to_address function + //! This obtains the address represented by ptr without forming a reference + //! to the pointee type + template + constexpr T* to_address(T* ptr) noexcept + { + static_assert(!AZStd::is_function_v, "Invoking to address on a function pointer is not allowed"); + return ptr; + } + //! Fancy pointer overload which delegates to using a specialization of pointer_traits::to_address + //! if that is a well-formed expression, otherwise it returns ptr->operator->() + //! For example invoking `to_address(AZStd::reverse_iterator(char_ptr))` + //! Returns an element of type const char* + template + constexpr auto to_address(const T& ptr) noexcept + { + if constexpr (AZStd::Internal::pointer_traits_valid_and_has_to_address_v) + { + return pointer_traits::to_address(ptr); + } + else + { + return to_address(ptr.operator->()); + } + } +} + +namespace AZStd::Internal +{ + // Variadic template which maps types to true For SFINAE + template + constexpr bool sfinae_trigger_v = true; + + template + constexpr bool is_class_or_enum = false; + template + constexpr bool is_class_or_enum> || is_enum_v>)>> = true; + + template + constexpr bool assignable_from_impl = false; + template + constexpr bool assignable_from_impl + && common_reference_with&, const remove_reference_t&> + && same_as() = declval()), LHS> >> = true; + + + template + constexpr bool common_with_impl = false; + template + constexpr bool common_with_impl, common_type_t> + && sfinae_trigger_v>(declval()))> + && sfinae_trigger_v>(declval()))> + && common_reference_with, add_lvalue_reference_t> + && common_reference_with>, common_reference_t, add_lvalue_reference_t>> + >> = true; +} + +namespace AZStd +{ + template + /*concept*/ constexpr bool common_with = Internal::common_with_impl; + + template + /*concept*/ constexpr bool assignable_from = Internal::assignable_from_impl; + + template + /*concept*/ constexpr bool constructible_from = destructible && is_constructible_v; + + template + /*concept*/ constexpr bool move_constructible = constructible_from && convertible_to; + + template + /*concept*/ constexpr bool derived_from = is_base_of_v && is_convertible_v; +} + +namespace AZStd::ranges::Internal +{ + template + constexpr bool is_class_or_enum_with_swap_adl = false; + template + constexpr bool is_class_or_enum_with_swap_adl> || is_enum_v> + || is_class_v> || is_enum_v>) + && is_void_v(), declval()))>> + >> = true; + + template + void swap(T&, T&) = delete; + + struct swap_fn + { + template + constexpr auto operator()(T&& t, U&& u) const noexcept(noexcept(swap(AZStd::forward(t), AZStd::forward(u)))) + ->enable_if_t> + { + swap(AZStd::forward(t), AZStd::forward(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 + constexpr auto operator()(T&& t, U&& u) const noexcept(noexcept((*this)(*t, *u))) + ->enable_if_t + && is_array_v && is_array_v && (extent_v == extent_v) + >; + + template + constexpr auto operator()(T& t1, T& t2) const noexcept(noexcept(is_nothrow_move_constructible_v&& is_nothrow_move_assignable_v)) + ->enable_if_t&& assignable_from> + { + 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 + constexpr bool swappable_impl = false; + template + constexpr bool swappable_impl(), declval()))>> = true; + + template + constexpr bool swappable_with_impl = false; + template + constexpr bool swappable_with_impl + && sfinae_trigger_v< + decltype(AZStd::ranges::swap(declval(), declval())), + decltype(AZStd::ranges::swap(declval(), declval())), + decltype(AZStd::ranges::swap(declval(), declval())), + decltype(AZStd::ranges::swap(declval(), declval()))>>> = true; +} +namespace AZStd +{ + template + /*concept*/ constexpr bool signed_integral = integral && is_signed_v; + template + /*concept*/ constexpr bool unsigned_integral = integral && !signed_integral; + + template + /*concept*/ constexpr bool swappable = Internal::swappable_impl; + + template + /*concept*/ constexpr bool swappable_with = Internal::swappable_with_impl; +} + + +namespace AZStd::Internal +{ + // boolean-testable concept (exposition only in the C++standard) + template + constexpr bool boolean_testable_impl = convertible_to; + + template + constexpr bool boolean_testable = false; + template + constexpr bool boolean_testable && boolean_testable_impl())>>> = true; + + // weakly comparable ==, != + template + constexpr bool weakly_equality_comparable_with = false; + template + constexpr bool weakly_equality_comparable_with&>() == declval&>())> + && boolean_testable&>() != declval&>())> + && boolean_testable&>() == declval&>())> + && boolean_testable&>() != declval&>())> + >> = true; + + // partially ordered <, >, <=, >= + template + constexpr bool partially_ordered_with_impl = false; + template + constexpr bool partially_ordered_with_impl&>() < declval&>())> + && boolean_testable&>() > declval&>())> + && boolean_testable&>() <= declval&>())> + && boolean_testable&>() >= declval&>())> + && boolean_testable&>() < declval&>())> + && boolean_testable&>() > declval&>())> + && boolean_testable&>() <= declval&>())> + && boolean_testable&>() >= declval&>())> + >> = true; +} + +namespace AZStd +{ + template + /*concept*/ constexpr bool equality_comparable = Internal::weakly_equality_comparable_with; +} + +namespace AZStd::Internal +{ + // equally_comparable + partially ordered + template + constexpr bool equally_comparable_with_impl = false; + template + constexpr bool equally_comparable_with_impl + && equality_comparable + && common_reference_with&, const remove_reference_t&> + && equality_comparable&, const remove_reference_t&>> + && Internal::weakly_equality_comparable_with + >> = true; +} + +namespace AZStd +{ + template + /*concept*/ constexpr bool equality_comparable_with = Internal::equally_comparable_with_impl; + + template + /*concept*/ constexpr bool partially_ordered_with = Internal::partially_ordered_with_impl; + + template + /*concept*/ constexpr bool totally_ordered = equality_comparable && partially_ordered_with; +} + +namespace AZStd::Internal +{ + // equally_comparable + partially ordered + template + constexpr bool totally_ordered_with_impl = false; + template + constexpr bool totally_ordered_with_impl&& totally_ordered + && equality_comparable_with + && totally_ordered&, const remove_reference_t&>> + && partially_ordered_with + >> = true; +} + +namespace AZStd +{ + template + /*concept*/ constexpr bool totally_ordered_with = Internal::totally_ordered_with_impl; +} + +namespace AZStd::Internal +{ + template + inline constexpr bool is_default_initializable = false; + template + inline constexpr bool is_default_initializable> = true; + + template + constexpr bool default_initializable_impl = false; + template + constexpr bool default_initializable_impl < T, enable_if_t < constructible_from + && sfinae_trigger_v && Internal::is_default_initializable >> = true; + + template + constexpr bool movable_impl = false; + template + constexpr bool movable_impl && move_constructible && + assignable_from && swappable> > = true; + + template + constexpr bool copy_constructible_impl = false; + template + constexpr bool copy_constructible_impl && + constructible_from && convertible_to && + constructible_from && convertible_to && + constructible_from && convertible_to> > = true; +} + +namespace AZStd +{ + // movable + template + /*concept*/ constexpr bool movable = Internal::movable_impl; + + // default_initializable + template + /*concept*/ constexpr bool default_initializable = Internal::default_initializable_impl; + + // copy constructible + template + /*concept*/ constexpr bool copy_constructible = Internal::copy_constructible_impl; +} + +namespace AZStd::Internal +{ + template + constexpr bool copyable_impl = false; + template + constexpr bool copyable_impl && movable && assignable_from && + assignable_from && assignable_from> > = true; +} + +namespace AZStd +{ + // copyable + template + /*concept*/ constexpr bool copyable = Internal::copyable_impl; + + // semiregular + template + /*concept*/ constexpr bool semiregular = copyable && default_initializable; + + // regular + template + /*concept*/ constexpr bool regular = semiregular && equality_comparable; +} + +// Iterator Concepts +namespace AZStd::Internal +{ + template + constexpr bool is_integer_like = integral && !same_as; + + template + constexpr bool is_signed_integer_like = signed_integral; + + template + constexpr bool weakly_incrementable_impl = false; + template + constexpr bool weakly_incrementable_impl + && is_signed_integer_like> + && same_as()), T&> + && sfinae_trigger_v()++)> >> = true; +} + +namespace AZStd +{ + // models weakly_incrementable concept + template + /*concept*/ constexpr bool weakly_incrementable = Internal::weakly_incrementable_impl; + + // models input_or_output_iterator concept + template + /*concept*/ constexpr bool input_or_output_iterator = !is_void_v + && weakly_incrementable; +} + +namespace AZStd::Internal +{ + template + constexpr bool incrementable_impl = false; + template + constexpr bool incrementable_impl + && weakly_incrementable + && same_as()++), T> >> = true; +} + +namespace AZStd +{ + template + /*concept*/ constexpr bool incrementable = Internal::incrementable_impl; +} + +namespace AZStd +{ + template + /*concept*/ constexpr bool sentinel_for = semiregular && + input_or_output_iterator && + Internal::weakly_equality_comparable_with; + template + inline constexpr bool disable_sized_sentinel_for = false; +} + +namespace AZStd::Internal +{ + template + /*concept*/ constexpr bool sized_sentinel_for_impl = false; + template + /*concept*/ constexpr bool sized_sentinel_for_impl + && !disable_sized_sentinel_for, remove_cv_t> + && same_as() - declval()), iter_difference_t> + && same_as() - declval()), iter_difference_t> >> = true; +} + +namespace AZStd +{ + template + /*concept*/ constexpr bool sized_sentinel_for = Internal::sized_sentinel_for_impl; + + template + struct iterator_traits; +} + +namespace AZStd::Internal +{ + // ITER_CONCEPT(I) general concept + template + constexpr bool use_traits_iterator_concept_for_concept = false; + template + constexpr bool use_traits_iterator_concept_for_concept::iterator_concept>> = true; + + template + constexpr bool use_traits_iterator_category_for_concept = false; + template + constexpr bool use_traits_iterator_category_for_concept::iterator_category>> = !use_traits_iterator_concept_for_concept; + + template + constexpr bool use_random_access_iterator_tag_for_concept = false; + template + constexpr bool use_random_access_iterator_tag_for_concept>> = !use_traits_iterator_concept_for_concept + && !use_traits_iterator_category_for_concept; + + template + struct iter_concept; + + template + struct iter_concept>> + { + using type = typename iterator_traits::iterator_concept; + }; + template + struct iter_concept>> + { + using type = typename iterator_traits::iterator_category; + }; + + template + struct iter_concept>> + { + using type = random_access_iterator_tag; + }; + template + using iter_concept_t = typename iter_concept::type; +} + +namespace AZStd +{ + // indirectly readable + template + /*concept*/ constexpr bool indirectly_readable = Internal::indirectly_readable_impl>; +} + +namespace AZStd::Internal +{ + // model the indirectly writable concept + template + constexpr bool indirectly_writable_impl = false; + + template + constexpr bool indirectly_writable_impl() = declval()), + decltype(*declval() = declval()), + decltype(const_cast&&>(*declval()) = declval()), + decltype(const_cast&&>(*declval()) = declval())> + > = true; +} +namespace AZStd +{ + // indirectly writable + template + /*concept*/ constexpr bool indirectly_writable = Internal::indirectly_writable_impl; + + // indirectly movable + template + /*concept*/ constexpr bool indirectly_movable = indirectly_readable && indirectly_writable>; +} + +namespace AZStd::Internal +{ + template + constexpr bool indirectly_movable_storage_impl = false; + + template + constexpr bool indirectly_movable_storage_impl && + indirectly_writable> && + movable> && + constructible_from, iter_rvalue_reference_t> && + assignable_from&, iter_rvalue_reference_t>> > = true; +} + +namespace AZStd +{ + template + /*concept*/ constexpr bool indirectly_movable_storable = Internal::indirectly_movable_storage_impl; +} + +namespace AZStd::Internal +{ + template + constexpr bool indirectly_copyable_impl = false; + + template + constexpr bool indirectly_copyable_impl && + indirectly_writable>> > = true; +} + +namespace AZStd +{ + // indirectly copyable + template + /*concept*/ constexpr bool indirectly_copyable = Internal::indirectly_copyable_impl; +} +namespace AZStd::Internal +{ + template + constexpr bool indirectly_copyable_storable_impl = false; + + template + constexpr bool indirectly_copyable_storable_impl && + indirectly_writable&> && + indirectly_writable&> && + indirectly_writable&&> && + indirectly_writable&&> && + copyable> && + constructible_from, iter_reference_t> && + assignable_from&, iter_reference_t>> > = true; +} + +namespace AZStd +{ + template + /*concept*/ constexpr bool indirectly_copyable_storable = Internal::indirectly_copyable_storable_impl; +} + +namespace AZStd::ranges::Internal +{ + template + void iter_swap(I1, I2) = delete; + + template + constexpr bool iter_swap_adl = false; + + template + constexpr bool iter_swap_adl(), declval()))>> = true; + + template + constexpr bool is_class_or_enum_with_iter_swap_adl = false; + + template + constexpr bool is_class_or_enum_with_iter_swap_adl + && (is_class_v> || is_enum_v>) + && (is_class_v> || is_enum_v>)>> = true; + + struct iter_swap_fn + { + template + constexpr auto operator()(I1&& i1, I2&& i2) const + ->enable_if_t + > + { + iter_swap(AZStd::forward(i1), AZStd::forward(i2)); + } + template + constexpr auto operator()(I1&& i1, I2&& i2) const + ->enable_if_t + && indirectly_readable + && indirectly_readable + && swappable_with, iter_reference_t> + > + { + ranges::swap(*i1, *i2); + } + + template + constexpr auto operator()(I1&& i1, I2&& i2) const + ->enable_if_t + && indirectly_movable_storable + && indirectly_movable_storable + > + { + *AZStd::forward(i1) = iter_exchange_move(AZStd::forward(i2), AZStd::forward(i1)); + } + + private: + template + static constexpr iter_value_t iter_exchange_move(X&& x, Y&& y) + noexcept(noexcept(iter_value_t(iter_move(x))) && noexcept(*x = iter_move(y))) + { + iter_value_t old_value(iter_move(x)); + *x = iter_move(y); + return old_value; + } + }; +} + +namespace AZStd::ranges +{ + inline namespace customization_point_object + { + inline constexpr Internal::iter_swap_fn iter_swap{}; + } +} + + +namespace AZStd::Internal +{ + template + constexpr bool indirectly_swappable_impl = false; + template + constexpr bool indirectly_swappable_impl&& indirectly_readable + && sfinae_trigger_v< + decltype(AZStd::ranges::iter_swap(declval(), declval())), + decltype(AZStd::ranges::iter_swap(declval(), declval())), + decltype(AZStd::ranges::iter_swap(declval(), declval())), + decltype(AZStd::ranges::iter_swap(declval(), declval()))>>> = true; +} + +namespace AZStd +{ + template + /*concept*/ constexpr bool indirectly_swappable = Internal::indirectly_swappable_impl; +} + +namespace AZStd::Internal +{ + template + constexpr bool input_iterator_impl = false; + template + constexpr bool input_iterator_impl + && derived_from, input_iterator_tag> + && indirectly_readable + >> = true; +} + +namespace AZStd +{ + // input iterator + template + /*concept*/ constexpr bool input_iterator = Internal::input_iterator_impl; +} + +namespace AZStd::Internal +{ + template + constexpr bool output_iterator_impl = false; + template + constexpr bool output_iterator_impl + && indirectly_writable + && sfinae_trigger_v()++ = AZStd::declval())> + >> = true; +} + +namespace AZStd +{ + // output iterator + template + /*concept*/ constexpr bool output_iterator = Internal::output_iterator_impl; +} + +namespace AZStd::Internal +{ + template + constexpr bool forward_iterator_impl = false; + template + constexpr bool forward_iterator_impl + && derived_from, forward_iterator_tag> + && incrementable + && sentinel_for> > = true; +} + +namespace AZStd +{ + // forward_iterator + template + /*concept*/ constexpr bool forward_iterator = Internal::forward_iterator_impl; +} + +namespace AZStd::Internal +{ + template + constexpr bool bidirectional_iterator_impl = false; + template + constexpr bool bidirectional_iterator_impl + && derived_from, bidirectional_iterator_tag> + && same_as()), I&> + && same_as()--), I> >> = true; +} + +namespace AZStd +{ + // bidirectional iterator + template + /*concept*/ constexpr bool bidirectional_iterator = Internal::bidirectional_iterator_impl; +} + +namespace AZStd::Internal +{ + template + constexpr bool random_access_iterator_impl = false; + template + constexpr bool random_access_iterator_impl + && derived_from, random_access_iterator_tag> + && totally_ordered + && sized_sentinel_for + && same_as() += declval>()), I&> + && same_as() + declval>()), I> + && same_as>() + declval()), I> + && same_as() -= declval>()), I&> + && same_as() - declval>()), I> + && same_as()[declval>()]), iter_reference_t>>> + = true; +} + +namespace AZStd +{ + template + /*concept*/ constexpr bool random_access_iterator = Internal::random_access_iterator_impl; +} + +namespace AZStd::Internal +{ + template + constexpr bool contiguous_iterator_impl = false; + template + constexpr bool contiguous_iterator_impl + && derived_from, contiguous_iterator_tag> + && is_lvalue_reference_v> + && indirectly_readable + && same_as, remove_cvref_t>> + > > + = same_as())), add_pointer_t>>; +} + +namespace AZStd +{ + // contiguous iterator + template + /*concept*/ constexpr bool contiguous_iterator = Internal::contiguous_iterator_impl; +} + +namespace AZStd::Internal +{ + // models the predicate concept + template + constexpr bool predicate_impl = false; + template + constexpr bool predicate_impl = Internal::boolean_testable>; +} + +namespace AZStd +{ + // models the predicate concept + template + /*concept*/ constexpr bool predicate = Internal::predicate_impl, F, Args...>; + + // models the relation concept + template + /*concept*/ constexpr bool relation = predicate && predicate + && predicate && predicate; + + // models the equivalence_relation concept + template + /*concept*/ constexpr bool equivalence_relation = relation; + + // models the strict_weak_order concept + // Note: semantically this is different than equivalence_relation + template + /*concept*/ constexpr bool strict_weak_order = relation; +} diff --git a/Code/Framework/AzCore/AzCore/std/containers/deque.h b/Code/Framework/AzCore/AzCore/std/containers/deque.h index 9eed66aeb3..d34bdb6b5a 100644 --- a/Code/Framework/AzCore/AzCore/std/containers/deque.h +++ b/Code/Framework/AzCore/AzCore/std/containers/deque.h @@ -135,9 +135,10 @@ namespace AZStd AZ_FORCE_INLINE this_type& operator--() { --m_offset; return *this; } AZ_FORCE_INLINE this_type operator--(int) { this_type tmp = *this; --m_offset; return tmp; } AZ_FORCE_INLINE this_type& operator+=(difference_type offset) { m_offset += offset; return *this; } - AZ_FORCE_INLINE this_type operator+(difference_type offset) { this_type tmp = *this; tmp += offset; return tmp; } + AZ_FORCE_INLINE this_type operator+(difference_type offset) const { this_type tmp = *this; tmp += offset; return tmp; } + friend AZ_FORCE_INLINE this_type operator+(difference_type offset, const this_type& rhs) { this_type tmp = rhs; tmp += offset; return tmp; } AZ_FORCE_INLINE this_type& operator-=(difference_type offset) { m_offset -= offset; return *this; } - AZ_FORCE_INLINE this_type operator-(difference_type offset) { this_type tmp = *this; tmp -= offset; return tmp; } + AZ_FORCE_INLINE this_type operator-(difference_type offset) const { this_type tmp = *this; tmp -= offset; return tmp; } /// ??? AZ_FORCE_INLINE difference_type operator-(const this_type& rhs) const { @@ -197,9 +198,10 @@ namespace AZStd AZ_FORCE_INLINE this_type& operator--() { --base_type::m_offset; return *this; } AZ_FORCE_INLINE this_type operator--(int) { this_type tmp = *this; --base_type::m_offset; return tmp; } AZ_FORCE_INLINE this_type& operator+=(difference_type offset) { base_type::m_offset += offset; return *this; } - AZ_FORCE_INLINE this_type operator+(difference_type offset) { this_type tmp = *this; tmp += offset; return tmp; } + AZ_FORCE_INLINE this_type operator+(difference_type offset) const { this_type tmp = *this; tmp += offset; return tmp; } + friend AZ_FORCE_INLINE this_type operator+(difference_type offset, const this_type& rhs) { this_type tmp = rhs; tmp += offset; return tmp; } AZ_FORCE_INLINE this_type& operator-=(difference_type offset) { base_type::m_offset -= offset; return *this; } - AZ_FORCE_INLINE this_type operator-(difference_type offset) { this_type tmp = *this; tmp -= offset; return tmp; } + AZ_FORCE_INLINE this_type operator-(difference_type offset) const { this_type tmp = *this; tmp -= offset; return tmp; } AZ_FORCE_INLINE difference_type operator-(const this_type& rhs) const { return rhs.m_offset <= base_type::m_offset ? base_type::m_offset - rhs.m_offset : -(difference_type)(rhs.m_offset - base_type::m_offset); diff --git a/Code/Framework/AzCore/AzCore/std/containers/fixed_vector.h b/Code/Framework/AzCore/AzCore/std/containers/fixed_vector.h index 13f3ef2d7a..22edfbd927 100644 --- a/Code/Framework/AzCore/AzCore/std/containers/fixed_vector.h +++ b/Code/Framework/AzCore/AzCore/std/containers/fixed_vector.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -101,7 +102,7 @@ namespace AZStd::Internal //! Invokes destructor on all elements in range //! No-op on empty container //! Nothing to destroy since the storage is empty. - template >> + template >> static constexpr void unsafe_destroy(InputIt, InputIt) noexcept { } @@ -214,7 +215,7 @@ namespace AZStd::Internal //! Destructs elements in the range [begin, end). //! This does not modify the size of the storage //! This is a no-op for trivial types - template >> + template >> void unsafe_destroy(InputIt, InputIt) noexcept { } @@ -334,7 +335,7 @@ namespace AZStd::Internal //! Destructs elements in the range [begin, end). //! This does not modify the size of the storage //! Invokes the destuctor via the AZStd::destroy method - template >> + template >> void unsafe_destroy(InputIt first, InputIt last) noexcept(is_nothrow_destructible_v) { AZSTD_CONTAINER_ASSERT(first >= data() && first <= data() + size(), "begin iterator is not in range of storage"); @@ -410,7 +411,7 @@ namespace AZStd AZStd::uninitialized_fill_n(data(), numElements, value); } - template >> + template >> fixed_vector(InputIt first, InputIt last) { resize_no_construct(AZStd::distance(first, last)); @@ -615,7 +616,7 @@ namespace AZStd insert(end(), numElements, value); } - template >> + template >> void assign(InputIt first, InputIt last) { clear(); @@ -641,8 +642,18 @@ namespace AZStd return &newElement; } - AZStd::uninitialized_move(insertPosPtr, dataEnd, insertPosPtr + 1); + // We need to move data with care, it is overlapping. + + // first move the last element into the uninitialized position as that will not overlap. + pointer nonOverlap = dataEnd - 1; + AZStd::uninitialized_move(nonOverlap, dataEnd, dataEnd); + + // copy the memory backwards while performing AZStd::move on the existing elements the area with overlapping memory + // to move the elments to the right by 1 + AZStd::move_backward(insertPosPtr, nonOverlap, dataEnd); + // add new elements AZStd::construct_at(insertPosPtr, AZStd::forward(args)...); + resize_no_construct(size() + 1); return iterator(insertPosPtr); } iterator insert(const_iterator insertPos, const_reference value) @@ -707,7 +718,7 @@ namespace AZStd } } - template>> + template>> void insert(const_iterator insertPos, InputIt first, InputIt last) { // specialize for iterator categories. diff --git a/Code/Framework/AzCore/AzCore/std/containers/span.h b/Code/Framework/AzCore/AzCore/std/containers/span.h index fbf5f56870..d0ac704fc3 100644 --- a/Code/Framework/AzCore/AzCore/std/containers/span.h +++ b/Code/Framework/AzCore/AzCore/std/containers/span.h @@ -7,9 +7,37 @@ */ #pragma once -#include -#include #include +#include +#include +#include + +namespace AZStd +{ + inline constexpr size_t dynamic_extent = numeric_limits::max(); + + template + class span; +} + +namespace AZStd::Internal +{ + template + inline constexpr bool is_std_array = false; + + template + inline constexpr bool is_std_array<::AZStd::array> = true; + + template + inline constexpr bool is_std_span = false; + + template + inline constexpr bool is_std_span<::AZStd::span> = true; + + template + inline constexpr bool is_array_convertible = is_convertible_v; + +} namespace AZStd { @@ -33,97 +61,149 @@ namespace AZStd * * Since the span does not copy and store any data, it is only valid as long as the data used to create it is valid. */ - template - class span final + template + class span { public: using element_type = T; using value_type = AZStd::remove_cv_t; - - using pointer = T*; - using const_pointer = const T*; - - using reference = T&; - using const_reference = const T&; - using size_type = AZStd::size_t; using difference_type = AZStd::ptrdiff_t; - using iterator = T*; - using const_iterator = const T*; + using pointer = element_type*; + using const_pointer = const element_type*; + + using reference = element_type&; + using const_reference = const element_type&; + + + using iterator = element_type*; + using const_iterator = const element_type*; using reverse_iterator = AZStd::reverse_iterator; using const_reverse_iterator = AZStd::reverse_iterator; - constexpr span(); + inline static constexpr size_t extent = Extent; + + constexpr span() noexcept = default;; ~span() = default; - constexpr span(pointer s, size_type length); + template && + Internal::is_array_convertible>, T> && + Extent == dynamic_extent>* = nullptr> + constexpr span(It first, size_type length); - constexpr span(pointer first, pointer last); + template && + Internal::is_array_convertible>, T> && + Extent != dynamic_extent, int> = 0> + constexpr explicit span(It first, size_type length); - // We explicitly delete this constructor because it's too easy to accidentally - // create a span to just the first element instead of an entire array. - constexpr span(const_pointer s) = delete; + template && + Internal::is_array_convertible>, T> && + sized_sentinel_for && + Extent == dynamic_extent>* = nullptr> + constexpr span(It first, End last); - template - constexpr span(Container& data); + template && + Internal::is_array_convertible>, T> && + sized_sentinel_for && + Extent != dynamic_extent, int> = 0> + constexpr explicit span(It first, End last); - template - constexpr span(const Container& data); + template> + constexpr span(type_identity_t (&arr)[N]) noexcept; - constexpr span(const span&) = default; + template > + constexpr span(array& data) noexcept; + template > + constexpr span(const array& data) noexcept; - constexpr span(span&& other); + template && + ranges::sized_range && + (ranges::borrowed_range || is_const_v) && + !Internal::is_std_span> && + !Internal::is_std_array> && + !is_array_v> && + Internal::is_array_convertible>, element_type> >> + constexpr span(R&& r); + + template >> + constexpr span(const span& other); + + constexpr span(const span&) noexcept = default; constexpr span& operator=(const span& other) = default; - constexpr span& operator=(span&& other); + // subviews -> https://eel.is/c++draft/views#span.sub + template + constexpr span first() const; + template + constexpr span last() const; + template + constexpr auto subspan() const; - constexpr size_type size() const; + constexpr span first(size_type count) const; + constexpr span last(size_type count) const; + constexpr span subspan(size_type offset, size_type count = dynamic_extent) const; - constexpr bool empty() const; + // observers - https://eel.is/c++draft/views#span.obs + constexpr size_type size() const noexcept; + constexpr size_type size_bytes() const noexcept; - constexpr pointer data(); - constexpr const_pointer data() const; + [[nodiscard]] constexpr bool empty() const noexcept; - constexpr const_reference operator[](size_type index) const; - constexpr reference operator[](size_type index); + // element access - https://eel.is/c++draft/views#span.elem + constexpr reference operator[](size_type index) const; + constexpr reference front() const; + constexpr reference back() const; + constexpr pointer data() const noexcept; - constexpr void erase(); + // iterator support - https://eel.is/c++draft/views#span.iterators + constexpr iterator begin() const noexcept; + constexpr iterator end() const noexcept; - constexpr iterator begin(); - constexpr iterator end(); - constexpr const_iterator begin() const; - constexpr const_iterator end() const; - - constexpr const_iterator cbegin() const; - constexpr const_iterator cend() const; - - constexpr reverse_iterator rbegin(); - constexpr reverse_iterator rend(); - constexpr const_reverse_iterator rbegin() const; - constexpr const_reverse_iterator rend() const; - - constexpr const_reverse_iterator crbegin() const; - constexpr const_reverse_iterator crend() const; - - friend bool operator==(span lhs, span rhs) - { - return lhs.m_begin == rhs.m_begin && lhs.m_end == rhs.m_end; - } - - friend bool operator!=(span lhs, span rhs) { return !(lhs == rhs); } - friend bool operator< (span lhs, span rhs) { return lhs.m_begin < rhs.m_begin || lhs.m_begin == rhs.m_begin && lhs.m_end < rhs.m_end; } - friend bool operator> (span lhs, span rhs) { return lhs.m_begin > rhs.m_begin || lhs.m_begin == rhs.m_begin && lhs.m_end > rhs.m_end; } - friend bool operator<=(span lhs, span rhs) { return lhs == rhs || lhs < rhs; } - friend bool operator>=(span lhs, span rhs) { return lhs == rhs || lhs > rhs; } + constexpr reverse_iterator rbegin() const noexcept; + constexpr reverse_iterator rend() const noexcept; private: - pointer m_begin; - pointer m_end; + pointer m_data{}; + size_type m_size{}; }; + // deduction guides https://eel.is/c++draft/views#span.deduct + template >> + span(It, EndOrSize) -> span>>; + + // array deductions + template + span(T(&)[N]) -> span; + template + span(array&) -> span; + template + span(const array&) -> span; + + template >> + span(R&&) -> span>>; + + // [span.objectrep], views of object representation + template + auto as_bytes(span s) noexcept + -> span; + + template + auto as_writable_bytes(span s) noexcept + -> enable_if_t, span>; + } // namespace AZStd +namespace AZStd::ranges +{ + template + inline constexpr bool enable_view> = true; + template + inline constexpr bool enable_borrowed_range> = true; +} + #include diff --git a/Code/Framework/AzCore/AzCore/std/containers/span.inl b/Code/Framework/AzCore/AzCore/std/containers/span.inl index 2b24a11fc3..765056cb67 100644 --- a/Code/Framework/AzCore/AzCore/std/containers/span.inl +++ b/Code/Framework/AzCore/AzCore/std/containers/span.inl @@ -9,116 +9,206 @@ namespace AZStd { - template - inline constexpr span::span() - : m_begin(nullptr) - , m_end(nullptr) - { } + template + template && + Internal::is_array_convertible>, T> && + Extent == dynamic_extent>*> + inline constexpr span::span(It first, size_type length) + : m_data{ to_address(first) } + , m_size{ length } + {} - template - inline constexpr span::span(pointer s, size_type length) - : m_begin(s) - , m_end(m_begin + length) + template + template && + Internal::is_array_convertible>, T> && + Extent != dynamic_extent, int>> + inline constexpr span::span(It first, size_type length) + : m_data{ to_address(first) } + , m_size{ length } + {} + + template + template && + Internal::is_array_convertible>, T> && + sized_sentinel_for && + Extent == dynamic_extent>*> + inline constexpr span::span(It first, End last) + : m_data{to_address(first)} + , m_size(last - first) + {} + + template + template && + Internal::is_array_convertible>, T> && + sized_sentinel_for && + Extent != dynamic_extent, int>> + inline constexpr span::span(It first, End last) + : m_data{to_address(first)} + , m_size(last - first) + {} + + template + template + inline constexpr span::span(type_identity_t(&arr)[N]) noexcept + : m_data{ arr } + , m_size{ N } + {} + + template + template + inline constexpr span::span(array& arr) noexcept + : m_data{ arr.data() } + , m_size{ arr.size() } + {} + template + template + inline constexpr span::span(const array& arr) noexcept + : m_data{ arr.data() } + , m_size{ arr.size() } + {} + + template + template + inline constexpr span::span(R&& r) + : m_data{ ranges::data(r) } + , m_size{ ranges::size(r) } { - if (length == 0) erase(); + AZ_Assert(Extent == dynamic_extent || Extent == m_size, "The extent of the span is non dynamic," + " therefore the range size must match the extent. Extent=%zu, Range size=%zu", + Extent, ranges::size(r)); } - template - inline constexpr span::span(pointer first, pointer last) - : m_begin(first) - , m_end(last) - { } - - template - template - inline constexpr span::span(Container& data) - : m_begin(data.data()) - , m_end(m_begin + data.size()) - { } - - template - template - inline constexpr span::span(const Container& data) - : m_begin(data.data()) - , m_end(m_begin + data.size()) - { } - - template - inline constexpr span::span(span&& other) - : span(other.m_begin, other.m_end) + template + template + inline constexpr span::span(const span& other) + : m_data{ other.data() } + , m_size{ other.size() } { -#if AZ_DEBUG_BUILD // Clearing the original pointers isn't necessary, but is good for debugging - other.m_begin = nullptr; - other.m_end = nullptr; -#endif + AZ_Assert(Extent == dynamic_extent || Extent == m_size, "The extent of the span is non dynamic," + " therefore the current size of the other span must match the extent. Extent=%zu, Other span size=%zu", + Extent, other.size()); } - template - inline constexpr AZStd::size_t span::size() const { return m_end - m_begin; } - - template - inline constexpr bool span::empty() const { return m_end == m_begin; } - - template - inline constexpr Element* span::data() { return m_begin; } - - template - inline constexpr const Element* span::data() const { return m_begin; } - - template - inline constexpr span& span::operator=(span&& other) + // subviews + template + template + inline constexpr auto span::first() const -> span { - m_begin = other.m_begin; - m_end = other.m_end; -#if AZ_DEBUG_BUILD // Clearing the original pointers isn't necessary, but is good for debugging - other.m_begin = nullptr; - other.m_end = nullptr; -#endif - return *this; + static_assert(Count <= Extent, "Count is larger than the Extent of the span, a subview of the first" + " Count elemnts of the span cannot be returned"); + AZ_Assert(Count <= size(), "Count %zu is larger than span size %zu", Count, size()); + return span{data(), Count}; } - - template - inline constexpr const Element& span::operator[](AZStd::size_t index) const + template + inline constexpr auto span::first(size_type count) const -> span + { + AZ_Assert(count <= size(), "Count %zu is larger than current size of span size %zu", count, size()); + return { data(), count }; + } + + template + template + inline constexpr auto span::last() const -> span + { + static_assert(Count <= Extent, "Count is larger than the Extent of the span, a subview of the last" + " Count elements of the span cannot be returned"); + AZ_Assert(Count <= size(), "Count %zu is larger than span size %zu", Count, size()); + return span{data() + (size() - Count), Count}; + } + template + inline constexpr auto span::last(size_type count) const -> span + { + AZ_Assert(count <= size(), "Count %zu is larger than span size %zu", count, size()); + return { data() + (size() - count), count }; + } + + template + template + inline constexpr auto span::subspan() const + { + static_assert(Offset <= Extent && (Count == dynamic_extent || Count <= Extent - Offset), + "Subspan Offset must <= span Extent and the Count must be either dynamic_extent" + " or <= (span Extent - Offset)"); + AZ_Assert(Offset <= size() && (Count == dynamic_extent || Count <= size() - Offset), + "Either the Subspan Offset %zu is larger than the span size %zu or the Count != dynamic_extent and" + " its value %zu is greater than \"span size - Offset\" %zu", + Offset, size(), Count, size() - Offset); + using return_type = span; + return return_type{ data() + Offset, Count != dynamic_extent ? Count : size() - Offset }; + } + + template + inline constexpr auto span::subspan(size_type offset, size_type count) const -> span + { + AZ_Assert(offset <= size() && (count == dynamic_extent || count <= size() - offset), + "Either the Subspan offset %zu is larger than the span size %zu or the count != dynamic_extent and" + " its value %zu is greater than \"span size - offset\" %zu", + offset, size(), count, size() - offset); + return { data() + offset, count != dynamic_extent ? count : size() - offset }; + } + + + // observers + template + inline constexpr auto span::size() const noexcept -> size_type { return m_size; } + + template + inline constexpr auto span::size_bytes() const noexcept -> size_type { return m_size * sizeof(element_type); } + + template + [[nodiscard]] inline constexpr bool span::empty() const noexcept{ return size() == 0; } + + // element access + template + inline constexpr auto span::operator[](size_type index) const -> reference { AZ_Assert(index < size(), "index value is out of range"); - return m_begin[index]; + return data()[index]; } - template - inline constexpr Element& span::operator[](AZStd::size_t index) + template + inline constexpr auto span::front() const -> reference { - AZ_Assert(index < size(), "index value is out of range"); - return m_begin[index]; + AZ_Assert(!empty(), "span cannot be empty when invoking front"); + return *data(); } - template - inline constexpr void span::erase() { m_begin = m_end = nullptr; } - - template - inline constexpr Element* span::begin() { return m_begin; } - template - inline constexpr Element* span::end() { return m_end; } - template - inline constexpr const Element* span::begin() const { return m_begin; } - template - inline constexpr const Element* span::end() const { return m_end; } + template + inline constexpr auto span::back() const -> reference + { + AZ_Assert(!empty(), "span cannot be empty when invoking back"); + return *(data() + (size() - 1)); + } - template - inline constexpr const Element* span::cbegin() const { return m_begin; } - template - inline constexpr const Element* span::cend() const { return m_end; } + // iterator support + template + inline constexpr auto span::data() const noexcept -> pointer { return m_data; } - template - inline constexpr AZStd::reverse_iterator span::rbegin() { return AZStd::reverse_iterator(m_end); } - template - inline constexpr AZStd::reverse_iterator span::rend() { return AZStd::reverse_iterator(m_begin); } - template - inline constexpr AZStd::reverse_iterator span::rbegin() const { return AZStd::reverse_iterator(m_end); } - template - inline constexpr AZStd::reverse_iterator span::rend() const { return AZStd::reverse_iterator(m_begin); } + template + inline constexpr auto span::begin() const noexcept -> iterator{ return m_data; } + template + inline constexpr auto span::end() const noexcept -> iterator { return m_data + m_size; } - template - inline constexpr AZStd::reverse_iterator span::crbegin() const { return AZStd::reverse_iterator(cend()); } - template - inline constexpr AZStd::reverse_iterator span::crend() const { return AZStd::reverse_iterator(cbegin()); } + template + inline constexpr auto span::rbegin() const noexcept -> reverse_iterator { return AZStd::make_reverse_iterator(end()); } + template + inline constexpr auto span::rend() const noexcept -> reverse_iterator { return AZStd::make_reverse_iterator(begin()); } + + + template + inline auto as_bytes(span s) noexcept + -> span + { + return span( + reinterpret_cast(s.data()), s.size_bytes()); + } + + + template + inline auto as_writable_bytes(span s) noexcept + -> enable_if_t, span> + { + return span( + reinterpret_cast(s.data()), s.size_bytes()); + } } // namespace AZStd diff --git a/Code/Framework/AzCore/AzCore/std/createdestroy.h b/Code/Framework/AzCore/AzCore/std/createdestroy.h index 355374a13a..2f5c7fde92 100644 --- a/Code/Framework/AzCore/AzCore/std/createdestroy.h +++ b/Code/Framework/AzCore/AzCore/std/createdestroy.h @@ -7,22 +7,19 @@ */ #pragma once +#include #include #include #include #include #include #include -#include #include #include #include // AZStd::addressof namespace AZStd { - // alias std::pointer_traits into the AZStd::namespace - using std::pointer_traits; - //! Bring the names of uninitialized_default_construct and //! uninitialized_default_construct_n into the AZStd namespace using std::uninitialized_default_construct; @@ -34,42 +31,6 @@ namespace AZStd using std::uninitialized_value_construct_n; } -namespace AZStd::Internal -{ - template - constexpr bool pointer_traits_has_to_address_v = false; - template - constexpr bool pointer_traits_has_to_address_v::to_address(declval()))>> = true; -} - -namespace AZStd -{ - //! Implements the C++20 to_address function - //! This obtains the address represented by ptr without forming a reference - //! to the pointee type - template - constexpr T* to_address(T* ptr) noexcept - { - static_assert(!AZStd::is_function_v, "Invoking to address on a function pointer is not allowed"); - return ptr; - } - //! Fancy pointer overload which delegates to using a specialization of pointer_traits::to_address - //! if that is a well-formed expression, otherwise it returns ptr->operator->() - //! For example invoking `to_address(AZStd::reverse_iterator(char_ptr))` - //! Returns an element of type const char* - template - constexpr auto to_address(const T& ptr) noexcept - { - if constexpr (AZStd::Internal::pointer_traits_has_to_address_v) - { - return pointer_traits::to_address(ptr); - } - else - { - return AZStd::to_address(ptr.operator->()); - } - } -} namespace AZStd::Internal { /** @@ -81,7 +42,7 @@ namespace AZStd::Internal /** * Type has trivial destructor. We don't call it. */ - template ::value_type, bool = is_trivially_destructible_v> + template , bool = is_trivially_destructible_v> struct destroy { static constexpr void range(InputIterator first, InputIterator last) { (void)first; (void)last; } @@ -163,7 +124,7 @@ namespace AZStd::Internal * Default object construction. */ // placement new isn't a core constant expression therefore it cannot be used in a constexpr function - template::value_type, + template, bool = is_trivially_constructible_v> struct construct { @@ -242,93 +203,125 @@ namespace AZStd::Internal ////////////////////////////////////////////////////////////////////////// // Sequence copy. If we use optimized version we use memcpy. /** - * Helper class to determine if we have apply fast copy. There are 2 conditions + * Class to determine if we have apply fast copy. There are 2 conditions * - trivial copy ctor. - * - all iterators satisfy the C++20 are contiguous iterator concept: pointers or iterator classes with - * the iterator_concept typedef set to contiguous_iterator_tag + * - all iterators satisfy the C++20 are contiguous iterator concept */ + template + constexpr bool indirectly_trivially_copyable = false; + template + constexpr bool indirectly_trivially_copyable>> = is_trivially_copyable_v>; + template - struct is_fast_copy_helper - { - using value_type = typename iterator_traits::value_type; - static constexpr bool value = AZStd::is_trivially_copyable_v - && Internal::satisfies_contiguous_iterator_concept_v - && Internal::satisfies_contiguous_iterator_concept_v; - }; + using is_fast_copy = bool_constant + && contiguous_iterator + && contiguous_iterator + >; - // Use this trait to to determine copy mode, based on the iterator category and object copy properties, - // Use it when when you call uninitialized_copy, Internal::copy, Internal::move, etc. - template< typename InputIterator, typename ResultIterator > - struct is_fast_copy - : public ::AZStd::integral_constant::value> {}; + template + constexpr bool is_fast_copy_v = is_fast_copy::value; + + // is_fast_copy argument is no longer used. template - constexpr ForwardIterator copy(const InputIterator& first, const InputIterator& last, ForwardIterator result, const false_type& /* is_fast_copy() */) + constexpr ForwardIterator copy(InputIterator first, InputIterator last, ForwardIterator result, bool) { - InputIterator iter(first); - for (; iter != last; ++result, ++iter) + if constexpr (is_fast_copy_v) { - *result = *iter; - } + // Specialized copy for contiguous iterators which are trivially copyable + size_t numElements = last - first; + if (numElements > 0) + { +#if az_has_builtin_memcpy + static_assert(sizeof(iter_value_t) == sizeof(iter_value_t), "Size of value types must match for a trivial copy"); + __builtin_memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t)); +#else + if (az_builtin_is_constant_evaluated()) + { + for (; first != last; ++result, ++first) + { + *result = *first; + } - return result; + return result; + } + else + { + static_assert(sizeof(iter_value_t) == sizeof(iter_value_t), "Size of value types must match for a trivial copy"); + AZ_Assert((static_cast(&*result) < static_cast(&*first)) + || (static_cast(&*result) >= static_cast(&*first + numElements)), + "AZStd::copy memory overlaps use AZStd::copy_backward!"); + ::memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t)); + } +#endif + } + return result + numElements; + } + else + { + for (; first != last; ++result, ++first) + { + *result = *first; + } + + return result; + } } - // Specialized copy for contiguous iterators (pointers) and trivial copy type. - // This overload cannot be constexpr until builtin_memcpy is added to MSVC compilers - template - inline ForwardIterator copy(const InputIterator& first, const InputIterator& last, ForwardIterator result, const true_type& /* is_fast_copy() */) - { - // \todo Make sure memory ranges don't overlap, otherwise people should use move and move_backward. - static_assert(sizeof(typename iterator_traits::value_type) == sizeof(typename iterator_traits::value_type), "Size of value types must match for a trivial copy"); - AZStd::size_t numElements = last - first; - if (numElements > 0) - { - AZ_Assert((static_cast(&*result) < static_cast(&*first)) || (static_cast(&*result) >= static_cast(&*first + numElements)), "AZStd::copy memory overlaps use AZStd::copy_backward!"); - AZ_Assert((static_cast(&*result + numElements) <= static_cast(&*first)) || (static_cast(&*result + numElements) > static_cast(&*first + numElements)), "AZStd::copy memory overlaps use AZStd::copy_backward!"); - /*AZSTD_STL::*/ memcpy(&*result, &*first, numElements * sizeof(typename iterator_traits::value_type)); - } - return result + numElements; - } // Copy backward. template - constexpr BidirectionalIterator2 copy_backward(const BidirectionalIterator1& first, const BidirectionalIterator1& last, BidirectionalIterator2 result, const false_type& /* is_fast_copy() */) + constexpr BidirectionalIterator2 copy_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 result, bool) { - BidirectionalIterator1 iter(last); - while (first != iter) + if constexpr (is_fast_copy_v) { - *--result = *--iter; + // Specialized copy for contiguous iterators which are trivially copyable + size_t numElements = last - first; + if (numElements > 0) + { +#if az_has_builtin_memmove + static_assert(sizeof(iter_value_t) == sizeof(iter_value_t), "Size of value types must match for a trivial copy"); + result -= numElements; + __builtin_memmove(to_address(result), to_address(first), numElements * sizeof(iter_value_t)); +#else + if (az_builtin_is_constant_evaluated()) + { + while (first != last) + { + *--result = *--last; + } + + return result; + } + else + { + static_assert(sizeof(iter_value_t) == sizeof(iter_value_t), "Size of value types must match for a trivial copy"); + result -= numElements; + AZ_Assert(((&*result + numElements) <= &*first) || ((&*result + numElements) > (&*first + numElements)), "AZStd::copy_backward memory overlaps use AZStd::copy!"); + ::memmove(&*result, &*first, numElements * sizeof(iter_value_t)); + } +#endif + } + return result; } - - return result; - } - - // Specialized copy for contiguous iterators (pointers) and trivial copy type. - // This overload cannot be constexpr until builtin_memcpy is added to MSVC compilers - template - inline BidirectionalIterator2 copy_backward(const BidirectionalIterator1& first, const BidirectionalIterator1& last, BidirectionalIterator2 result, const true_type& /* is_fast_copy() */) - { - // \todo Make sure memory ranges don't overlap, otherwise people should use move and move_backward. - static_assert(sizeof(typename iterator_traits::value_type) == sizeof(typename iterator_traits::value_type), "Size of value types must match for a trivial copy"); - AZStd::size_t numElements = last - first; - if (numElements > 0) + else { - result -= numElements; - AZ_Assert((&*result < &*first) || (&*result >= (&*first + numElements)), "AZStd::copy_backward memory overlaps use AZStd::copy!"); - AZ_Assert(((&*result + numElements) <= &*first) || ((&*result + numElements) > (&*first + numElements)), "AZStd::copy_backward memory overlaps use AZStd::copy!"); - /*AZSTD_STL::*/ memcpy(&*result, &*first, numElements * sizeof(typename iterator_traits::value_type)); + while (first != last) + { + *--result = *--last; + } + + return result; } - return result; } template - constexpr ForwardIterator reverse_copy(const BidirectionalIterator1& first, const BidirectionalIterator1& last, ForwardIterator dest) + constexpr ForwardIterator reverse_copy(BidirectionalIterator1 first, BidirectionalIterator1 last, ForwardIterator dest) { - BidirectionalIterator1 iter(last); - while (iter != first) + while (last != first) { - *(dest++) = *(--iter); + *(dest++) = *(--last); } return dest; @@ -342,143 +335,209 @@ namespace AZStd * Specialized algorithms 20.4.4. We extend that by adding faster specialized versions when we have trivial assign type. */ template - constexpr ForwardIterator uninitialized_copy(const InputIterator& first, const InputIterator& last, ForwardIterator result, const false_type& /* is_fast_copy() */) + constexpr ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result, bool) { - InputIterator iter(first); - for (; iter != last; ++result, ++iter) + // Specialized copy for contiguous iterators which are trivially copyable + if constexpr (Internal::is_fast_copy_v) { - ::new (static_cast(&*result)) typename iterator_traits::value_type(*iter); - } + size_t numElements = last - first; + if (numElements > 0) + { +#if az_has_builtin_memcpy + static_assert(sizeof(iter_value_t) == sizeof(iter_value_t), "Value type sizes must match for a trivial copy"); + __builtin_memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t)); +#else + if (az_builtin_is_constant_evaluated()) + { + for (; first != last; ++result, ++first) + { + construct_at(static_cast*>(to_address(result)), *first); + } - return result; + return result; + } + else + { + static_assert(sizeof(iter_value_t) == sizeof(iter_value_t), "Value type sizes must match for a trivial copy"); + ::memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t)); + } +#endif + } + return result + numElements; + } + else + { + for (; first != last; ++result, ++first) + { + construct_at(static_cast*>(to_address(result)), *first); + } + + return result; + } } - // Specialized copy for contiguous iterators and trivial copy type. - // This overload cannot be constexpr until builtin_memcpy is added to MSVC compilers - template - inline ForwardIterator uninitialized_copy(const InputIterator& first, const InputIterator& last, ForwardIterator result, const true_type& /* is_fast_copy() */) - { - static_assert(sizeof(typename iterator_traits::value_type) == sizeof(typename iterator_traits::value_type), "Value type sizes must match for a trivial copy"); - AZStd::size_t numElements = last - first; - if (numElements > 0) - { - /*AZSTD_STL::*/ - memcpy(&*result, &*first, numElements * sizeof(typename iterator_traits::value_type)); - } - return result + numElements; - } template - constexpr ForwardIterator uninitialized_copy(const InputIterator& first, const InputIterator& last, ForwardIterator result) + constexpr ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result) { - return uninitialized_copy(first, last, result, Internal::is_fast_copy()); + return uninitialized_copy(first, last, result, {}); } // 25.3.1 Copy template constexpr OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result) { - return AZStd::Internal::copy(first, last, result, AZStd::Internal::is_fast_copy()); + return Internal::copy(first, last, result, {}); } template constexpr OutputIterator reverse_copy(BidirectionalIterator first, BidirectionalIterator last, OutputIterator dest) { - return AZStd::Internal::reverse_copy(first, last, dest); + return Internal::reverse_copy(first, last, dest); } template BidirectionalIterator2 copy_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 result) { - return AZStd::Internal::copy_backward(first, last, result, AZStd::Internal::is_fast_copy()); + return Internal::copy_backward(first, last, result, {}); } } namespace AZStd::Internal { ////////////////////////////////////////////////////////////////////////// - // Sequence move. If we use optimized version we use memmove. + // Sequence move template - constexpr ForwardIterator move(const InputIterator& first, const InputIterator& last, ForwardIterator result, const false_type& /* is_fast_copy() */) + constexpr ForwardIterator move(InputIterator first, InputIterator last, ForwardIterator result, bool) { - InputIterator iter(first); - for (; iter != last; ++result, ++iter) + // Specialized copy for contiguous iterators which are trivially copyable + if constexpr (is_fast_copy_v) { - *result = AZStd::move(*iter); + size_t numElements = last - first; + if (numElements > 0) + { +#if az_has_builtin_memcpy + static_assert(sizeof(iter_value_t) == sizeof(iter_value_t), "Size of value types must match for a trivial copy"); + __builtin_memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t)); +#else + if (az_builtin_is_constant_evaluated()) + { + for (; first != last; ++result, ++first) + { + *result = ::AZStd::move(*first); + } + return result; + } + else + { + static_assert(sizeof(iter_value_t) == sizeof(iter_value_t), "Size of value types must match for a trivial copy"); + AZ_Assert((static_cast(&*result) < static_cast(&*first)) + || (static_cast(&*result) >= static_cast(&*first + numElements)), + "AZStd::move memory overlaps use AZStd::move_backward!"); + ::memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t)); + } +#endif + } + return result + numElements; + } + else + { + for (; first != last; ++result, ++first) + { + *result = ::AZStd::move(*first); + } + return result; } - return result; } - // Specialized copy for contiguous iterators (pointers) and trivial copy type. - // This overload cannot be constexpr until builtin_memmove is added to MSVC compilers - template - inline ForwardIterator move(const InputIterator& first, const InputIterator& last, ForwardIterator result, const true_type& /* is_fast_copy() */) - { - static_assert(sizeof(typename iterator_traits::value_type) == sizeof(typename iterator_traits::value_type), "Size of value types must match for a trivial copy"); - AZStd::size_t numElements = last - first; - if (numElements > 0) - { - /*AZSTD_STL::*/ - memmove(&*result, &*first, numElements * sizeof(typename iterator_traits::value_type)); - } - return result + numElements; - } // For generic iterators, move is the same as copy. template - constexpr BidirectionalIterator2 move_backward(const BidirectionalIterator1& first, const BidirectionalIterator1& last, BidirectionalIterator2 result, const false_type& /* is_fast_copy() */) + constexpr BidirectionalIterator2 move_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 result, bool) { - BidirectionalIterator1 iter(last); - while (first != iter) + // Specialized copy for contiguous iterators which are trivially copyable + if constexpr (is_fast_copy_v) { - *--result = AZStd::move(*--iter); + size_t numElements = last - first; + if (numElements > 0) + { +#if az_has_builtin_memmove + static_assert(sizeof(iter_value_t) == sizeof(iter_value_t), "Size of value types must match for a trivial copy"); + result -= numElements; + __builtin_memmove(to_address(result), to_address(first), numElements * sizeof(iter_value_t)); +#else + if (az_builtin_is_constant_evaluated()) + { + while (first != last) + { + *--result = ::AZStd::move(*--last); + } + return result; + } + else + { + static_assert(sizeof(iter_value_t) == sizeof(iter_value_t), "Size of value types must match for a trivial copy"); + result -= numElements; + AZ_Assert((static_cast(&*result + numElements) <= static_cast(&*first)) + || (static_cast(&*result + numElements) > static_cast(&*first + numElements)), + "AZStd::move_backward memory overlaps use AZStd::move!"); + ::memmove(to_address(result), to_address(first), numElements * sizeof(iter_value_t)); + } +#endif + } + return result; } - return result; - } - - // Specialized copy for contiguous iterators (pointers) and trivial copy type. - // This overload cannot be constexpr until builtin_memmove is added to MSVC compilers - template - inline BidirectionalIterator2 move_backward(const BidirectionalIterator1& first, const BidirectionalIterator1& last, BidirectionalIterator2 result, const true_type& /* is_fast_copy() */) - { - // \todo Make sure memory ranges don't overlap, otherwise people should use move and move_backward. - static_assert(sizeof(typename iterator_traits::value_type) == sizeof(typename iterator_traits::value_type), "Size of value types must match for a trivial copy"); - AZStd::size_t numElements = last - first; - result -= numElements; - if (numElements > 0) + else { - /*AZSTD_STL::*/ - memmove(&*result, &*first, numElements * sizeof(typename iterator_traits::value_type)); + while (first != last) + { + *--result = ::AZStd::move(*--last); + } + return result; } - return result; } template - constexpr ForwardIterator uninitialized_move(const InputIterator& first, const InputIterator& last, ForwardIterator result, const false_type& /* is_fast_copy() */) + constexpr ForwardIterator uninitialized_move(InputIterator first, InputIterator last, ForwardIterator result, bool) { - InputIterator iter(first); - - for (; iter != last; ++result, ++iter) + // Specialized copy for contiguous iterators which are trivially copyable + if constexpr (is_fast_copy_v) { - ::new (static_cast(&*result)) typename iterator_traits::value_type(AZStd::move(*iter)); - } - return result; - } - // Specialized copy for contiguous iterators and trivial move type. (since the object is POD we will just perform a copy) - // This overload cannot be constexpr until builtin_memcpy is added to MSVC compilers - template - inline ForwardIterator uninitialized_move(const InputIterator& first, const InputIterator& last, ForwardIterator result, const true_type& /* is_fast_copy() */) - { - static_assert(sizeof(typename iterator_traits::value_type) == sizeof(typename iterator_traits::value_type), "Value type sizes must match for a trivial copy"); - AZStd::size_t numElements = last - first; - if (numElements > 0) - { - /*AZSTD_STL::*/ - memcpy(&*result, &*first, numElements * sizeof(typename iterator_traits::value_type)); - } - return result + numElements; - } + size_t numElements = last - first; + if (numElements > 0) + { +#if az_has_builtin_memcpy + static_assert(sizeof(iter_value_t) == sizeof(iter_value_t), "Value type sizes must match for a trivial copy"); + __builtin_memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t)); +#else + if (az_builtin_is_constant_evaluated()) + { + for (; first != last; ++result, ++first) + { + construct_at(static_cast*>(to_address(result)), ::AZStd::move(*first)); + } + return result; + } + else + { + static_assert(sizeof(iter_value_t) == sizeof(iter_value_t), "Value type sizes must match for a trivial copy"); + ::memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t)); + } +#endif + } + return result + numElements; + } + else + { + for (; first != last; ++result, ++first) + { + construct_at(static_cast*>(to_address(result)), ::AZStd::move(*first)); + } + + return result; + } + } // end of sequence move. ////////////////////////////////////////////////////////////////////////// } @@ -492,19 +551,19 @@ namespace AZStd template ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt result) { - return AZStd::Internal::uninitialized_move(first, last, result, AZStd::Internal::is_fast_copy{}); + return AZStd::Internal::uninitialized_move(first, last, result, {}); } // 25.3.2 Move template OutputIterator move(InputIterator first, InputIterator last, OutputIterator result) { - return AZStd::Internal::move(first, last, result, AZStd::Internal::is_fast_copy()); + return AZStd::Internal::move(first, last, result, {}); } template BidirectionalIterator2 move_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 result) { - return AZStd::Internal::move_backward(first, last, result, AZStd::Internal::is_fast_copy()); + return AZStd::Internal::move_backward(first, last, result, {}); } } @@ -516,63 +575,77 @@ namespace AZStd::Internal * Helper class to determine if we have apply fast fill. There are 3 conditions * - trivial assign * - size of type == 1 (chars) to use memset - * - contiguous iterators (pointers) + * - contiguous iterators */ + template + constexpr bool indirectly_copy_assignable = false; + template + constexpr bool indirectly_copy_assignable>> = + is_trivially_copy_assignable_v> && sizeof(iter_value_t) == 1; + template - struct is_fast_fill_helper - { - using value_type = typename iterator_traits::value_type; - constexpr static bool value = is_trivially_copy_assignable_v && sizeof(value_type) == 1 - && Internal::satisfies_contiguous_iterator_concept_v; - }; - - // Use this trait to to determine fill mode, based on the iterator, value size, etc. - // Use it when you call uninitialized_fill, uninitialized_fill_n, fill and fill_n. - template< typename Iterator > - struct is_fast_fill - : public ::AZStd::integral_constant::value> - {}; + using is_fast_fill = bool_constant && contiguous_iterator>; + template + constexpr bool is_fast_fill_v = is_fast_fill::value; + // The fast fill trait is no longer used + // It is detected using C++20 concepts now template - constexpr void fill(const ForwardIterator& first, const ForwardIterator& last, const T& value, const false_type& /* is_fast_fill() */) + constexpr void fill(ForwardIterator first, ForwardIterator last, const T& value, bool) { - ForwardIterator iter(first); - for (; iter != last; ++iter) + if constexpr (is_fast_fill_v) { - *iter = value; + size_t numElements = last - first; + if (numElements > 0) + { + if (az_builtin_is_constant_evaluated()) + { + for (; first != last; ++first) + { + *first = value; + } + } + else + { + ::memset(to_address(first), reinterpret_cast(value), numElements); + } + } } - } - // Specialized version for character types where memset can be used - // This overload cannot be constexpr until builtin_memset is added to MSVC compilers - template - inline void fill(const ForwardIterator& first, const ForwardIterator& last, const T& value, const true_type& /* is_fast_fill() */) - { - AZStd::size_t numElements = last - first; - if (numElements > 0) + else { - /*AZSTD_STL::*/ - memset((void*)&*first, *reinterpret_cast(&value), numElements); + for (; first != last; ++first) + { + *first = value; + } } } template - constexpr void fill_n(ForwardIterator first, Size numElements, const T& value, const false_type& /* is_fast_fill() */) + constexpr void fill_n(ForwardIterator first, Size numElements, const T& value, bool) { - for (; numElements--; ++first) + if constexpr (is_fast_fill_v) { - *first = value; + if (numElements) + { + if (az_builtin_is_constant_evaluated()) + { + for (; numElements--; ++first) + { + *first = value; + } + } + else + { + ::memset(to_address(first), reinterpret_cast(value), numElements); + } + } } - } - - // Specialized version for character types where memset can be used to perform the fill - // This overload cannot be constexpr until builtin_memset is added to MSVC compilers - template - inline void fill_n(ForwardIterator first, Size numElements, const T& value, const true_type& /* is_fast_fill() */) - { - if (numElements > 0) + else { - /*AZSTD_STL::*/ - memset(&*first, *reinterpret_cast(&value), numElements); + for (; numElements--; ++first) + { + *first = value; + } } } } @@ -580,78 +653,85 @@ namespace AZStd::Internal namespace AZStd { template - constexpr void fill(const ForwardIterator& first, const ForwardIterator& last, const T& value) + constexpr void fill(ForwardIterator first, ForwardIterator last, const T& value) { - Internal::fill(first, last, value, Internal::is_fast_fill()); + Internal::fill(first, last, value, {}); } - template constexpr void fill_n(ForwardIterator first, Size numElements, const T& value) { - Internal::fill_n(first, numElements, value, Internal::is_fast_fill()); + Internal::fill_n(first, numElements, value, {}); } template - constexpr void uninitialized_fill(const ForwardIterator& first, const ForwardIterator& last, const T& value, const false_type& /* is_fast_fill() */) + constexpr void uninitialized_fill(ForwardIterator first, ForwardIterator last, const T& value, bool) { - ForwardIterator iter(first); - for (; iter != last; ++iter) + if constexpr (Internal::is_fast_fill_v) { - ::new (static_cast(&*iter)) typename iterator_traits::value_type(value); + size_t numElements = last - first; + if (numElements > 0) + { + if (az_builtin_is_constant_evaluated()) + { + for (; first != last; ++first) + { + construct_at(static_cast*>(to_address(first)), value); + } + } + else + { + ::memset(to_address(first), reinterpret_cast(value), numElements); + } + } } - } - - // Specialized overload for types which meet the following criteria. - // 1. Has it's iterator_traits::iterator_concept type set to to contiguous_iterator_tag - // 2. Is trivially assignable - // 3. Has a sizeof(T) == 1 - // In such a case memset can be used to fill in the data - // This overload cannot be constexpr until builtin_memset is added to MSVC compilers - template - inline void uninitialized_fill(const ForwardIterator& first, const ForwardIterator& last, const T& value, const true_type& /* is_fast_fill() */) - { - AZStd::size_t numElements = last - first; - if (numElements > 0) + else { - /*AZSTD_STL::*/ - memset(&*first, *reinterpret_cast(&value), numElements); + for (; first != last; ++first) + { + construct_at(static_cast*>(to_address(first)), value); + } } } template constexpr void uninitialized_fill(ForwardIterator first, Size numElements, const T& value) { - return uninitialized_fill(first, numElements, value, Internal::is_fast_fill()); + return uninitialized_fill(first, numElements, value, {}); } template - constexpr void uninitialized_fill_n(ForwardIterator first, Size numElements, const T& value, const false_type& /* is_fast_fill() */) + constexpr void uninitialized_fill_n(ForwardIterator first, Size numElements, const T& value, bool) { - for (; numElements--; ++first) + if constexpr (Internal::is_fast_fill_v) { - ::new (static_cast(&*first)) typename iterator_traits::value_type(value); + if (numElements > 0) + { + if (az_builtin_is_constant_evaluated()) + { + for (; numElements--; ++first) + { + construct_at(static_cast*>(to_address(first)), value); + } + } + else + { + ::memset(to_address(first), reinterpret_cast(value), numElements); + } + } } - } - - // Specialized overload for types which meet the following criteria. - // 1. Has it's iterator_traits::iterator_concept type set to to contiguous_iterator_tag - // 2. Is trivially assignable - // 3. Has a sizeof(T) == 1 - // In such a case memset can be used to fill in the data - template - inline void uninitialized_fill_n(ForwardIterator first, Size numElements, const T& value, const true_type& /* is_fast_fill() */) - { - if (numElements) + else { - /*AZSTD_STL::*/ - memset(&*first, *reinterpret_cast(&value), numElements); + for (; numElements--; ++first) + { + construct_at(static_cast*>(to_address(first)), value); + } } } template constexpr void uninitialized_fill_n(ForwardIterator first, Size numElements, const T& value) { - return uninitialized_fill_n(first, numElements, value, Internal::is_fast_fill()); + return uninitialized_fill_n(first, numElements, value, {}); } } diff --git a/Code/Framework/AzCore/AzCore/std/function/invoke.h b/Code/Framework/AzCore/AzCore/std/function/invoke.h index 88dbbea218..6dccb43e36 100644 --- a/Code/Framework/AzCore/AzCore/std/function/invoke.h +++ b/Code/Framework/AzCore/AzCore/std/function/invoke.h @@ -38,4 +38,12 @@ namespace AZStd { return Internal::INVOKE(Internal::InvokeTraits::forward(f), Internal::InvokeTraits::forward(args)...); } + + // models the invocable concept + template + /*concept*/ constexpr bool invocable = is_invocable_v; + + // models the regular_invocable concept + template + /*concept*/ constexpr bool regular_invocable = invocable; } diff --git a/Code/Framework/AzCore/AzCore/std/iterator.h b/Code/Framework/AzCore/AzCore/std/iterator.h index 8d0d49b649..5bc653e3b6 100644 --- a/Code/Framework/AzCore/AzCore/std/iterator.h +++ b/Code/Framework/AzCore/AzCore/std/iterator.h @@ -8,19 +8,19 @@ #pragma once #include -#include -#include -#include -#include // use by ConstIteratorCast +#include +#include +#include #include -#include +#include +#include #include namespace AZStd { - // Everything unless specified is based on C++ standard 24 (lib.iterators). + // Everything unless specified is based on C++ standard 20 (lib.iterators). /// Identifying tag for input iterators. using input_iterator_tag = std::input_iterator_tag; @@ -51,16 +51,6 @@ namespace AZStd::Internal typename Iterator::reference> > = true; - - template - inline constexpr bool has_iterator_category_v = false; - template - inline constexpr bool has_iterator_category_v> = true; - template - inline constexpr bool has_iterator_concept_v = false; - template - inline constexpr bool has_iterator_concept_v> = true; - // Iterator iterator_category alias must be one of the iterator category tags template struct iterator_traits_category_tags @@ -98,6 +88,8 @@ namespace AZStd struct iterator_traits : Internal::iterator_traits_type_aliases> { + // Internal type alias meant to indicate that this is the primary template + using _is_primary_template = iterator_traits; }; /** @@ -114,45 +106,6 @@ namespace AZStd using iterator_category = random_access_iterator_tag; using iterator_concept = contiguous_iterator_tag; }; - -} - -namespace AZStd::Internal -{ - // iterator_category tag testers - template >> - inline constexpr bool has_iterator_category_convertible_to_v = false; - template - inline constexpr bool has_iterator_category_convertible_to_v = is_convertible_v::iterator_category, Category>; - - template - inline constexpr bool is_input_iterator_v = has_iterator_category_convertible_to_v; - - template - inline constexpr bool is_forward_iterator_v = has_iterator_category_convertible_to_v; - - template - inline constexpr bool is_bidirectional_iterator_v = has_iterator_category_convertible_to_v; - - template - inline constexpr bool is_random_access_iterator_v = has_iterator_category_convertible_to_v; - - template - inline constexpr bool is_contiguous_iterator_v = has_iterator_category_convertible_to_v; - - template - inline constexpr bool is_exactly_input_iterator_v = has_iterator_category_convertible_to_v && !has_iterator_category_convertible_to_v; - - // iterator concept testers - template - inline constexpr bool derived_from = is_base_of_v && is_convertible_v; - - template >> - inline constexpr bool satisfies_iterator_concept = false; - template - inline constexpr bool satisfies_iterator_concept = derived_from::iterator_concept, Concept>; - template - inline constexpr bool satisfies_contiguous_iterator_concept_v = satisfies_iterator_concept; } namespace AZStd diff --git a/Code/Framework/AzCore/AzCore/std/iterator/iterator_primitives.h b/Code/Framework/AzCore/AzCore/std/iterator/iterator_primitives.h new file mode 100644 index 0000000000..39d89e1f2f --- /dev/null +++ b/Code/Framework/AzCore/AzCore/std/iterator/iterator_primitives.h @@ -0,0 +1,200 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace AZStd +{ + // Bring in std utility functions into AZStd namespace + using std::forward; + + // forward declare iterator_traits to avoid iterator.h include + template + struct iterator_traits; +} + +// C++20 range traits for iteratable types +namespace AZStd::Internal +{ + // Models the can-reference concept which isn't available until C++20 + // template + template + constexpr bool can_reference = true; + template <> + inline constexpr bool can_reference = false; + + // Models the dereferencable concept which isn't available until C++20 + template + /*concept*/ constexpr bool dereferenceable = false; + template + constexpr bool dereferenceable())>>> = true; + + template + constexpr bool is_primary_template_v = false; + template + constexpr bool is_primary_template_v>> = true; + + // indirectly readable traits + template + constexpr bool has_value_type_v = false; + template + constexpr bool has_value_type_v> = true; + template + constexpr bool has_element_type_v = false; + template + constexpr bool has_element_type_v> = true; + + template + struct object_type_value_requires {}; + template + struct object_type_value_requires>> + { + using value_type = remove_cv_t; + }; + template + struct indirectly_readable_requires {}; + template + struct indirectly_readable_requires> + && is_void_v::value_type>> >> + { + // iterator_traits has been been specialized + using value_type = typename iterator_traits::value_type; + }; + + template + struct indirectly_readable_requires> + && is_array_v>> + { + using value_type = remove_cv_t>; + }; + + template + struct indirectly_readable_requires> + && has_value_type_v && !has_element_type_v>> + : object_type_value_requires {}; + + template + struct indirectly_readable_requires> + && has_element_type_v && !has_value_type_v>> + : object_type_value_requires {}; + + template + struct indirectly_readable_requires> + && has_value_type_v&& has_element_type_v + && same_as, remove_cv_t> >> + : object_type_value_requires {}; + + // incrementable traits + template + constexpr bool has_difference_type_v = false; + template + constexpr bool has_difference_type_v> = true; + + template + struct object_type_difference_requires {}; + template + struct object_type_difference_requires>> + { + using difference_type = ptrdiff_t; + }; + + template + struct incrementable_requires {}; + // iterator_traits has been specialized + template + struct incrementable_requires> + && is_void_v::difference_type>> >> + { + using difference_type = typename iterator_traits::difference_type; + }; + template + struct incrementable_requires> + && has_difference_type_v>> + { + using difference_type = typename T::difference_type; + }; + template + struct incrementable_requires> + && !has_difference_type_v + && integral() - declval())> >> + { + using difference_type = make_signed_t() - declval())>; + }; +} + +namespace AZStd +{ + // indirectly_readable_traits for iter_value_t + template + struct indirectly_readable_traits + : Internal::indirectly_readable_requires {}; + template + struct indirectly_readable_traits + : Internal::object_type_value_requires {}; + template + struct indirectly_readable_traits + : indirectly_readable_traits {}; + + template + using iter_value_t = typename indirectly_readable_traits>::value_type; + + template + using iter_reference_t = enable_if_t, decltype(*declval())>; + + // incrementable_traits for iter_difference_t + template + struct incrementable_traits + : Internal::incrementable_requires {}; + template + struct incrementable_traits + : Internal::object_type_difference_requires {}; + template + struct incrementable_traits + : incrementable_traits {}; + + template + using iter_difference_t = typename incrementable_traits>::difference_type; + + template + using iter_rvalue_reference_t = decltype(ranges::iter_move(declval())); + + namespace Internal + { + // model the indirectly readable concept + template + constexpr bool indirectly_readable_impl = false; + + template + constexpr bool indirectly_readable_impl()), iter_reference_t> + && same_as())), iter_rvalue_reference_t> + && common_reference_with&&, iter_value_t&> + && common_reference_with&&, iter_rvalue_reference_t&> + && common_reference_with&&, const iter_value_t&>>> = true; + } + + template + using iter_common_reference_t = enable_if_t, + common_reference_t, iter_value_t&>>; +} diff --git a/Code/Framework/AzCore/AzCore/std/ranges/iter_move.h b/Code/Framework/AzCore/AzCore/std/ranges/iter_move.h new file mode 100644 index 0000000000..a9519f03c5 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/std/ranges/iter_move.h @@ -0,0 +1,81 @@ +/* + * 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace AZStd +{ + // Bring in std utility functions into AZStd namespace + using std::forward; +} + +// C++20 range traits for iteratable types +namespace AZStd::ranges::Internal +{ + void iter_move(); + + template + constexpr bool iter_move_adl = false; + + template + constexpr bool iter_move_adl()))>> = true; + + template + constexpr bool is_class_or_enum_with_iter_move_adl = false; + + template + constexpr bool is_class_or_enum_with_iter_move_adl + && (is_class_v> || is_enum_v>)>> + = true; + + struct iter_move_fn + { + template + constexpr auto operator()(It&& it) const + ->enable_if_t, + decltype(iter_move(AZStd::forward(it)))> + { + return iter_move(AZStd::forward(it)); + } + template + constexpr auto operator()(It&& it) const + ->enable_if_t&& is_lvalue_reference_v(it))>, + decltype(AZStd::move(*AZStd::forward(it)))> + { + return AZStd::move(*AZStd::forward(it)); + } + template + constexpr auto operator()(It&& it) const + ->enable_if_t && !is_lvalue_reference_v(it))>, + decltype(*AZStd::forward(it))> + { + return *AZStd::forward(it); + } + }; +} + +namespace AZStd::ranges +{ + inline namespace customization_point_object + { + inline constexpr auto iter_move = Internal::iter_move_fn{}; + } +} diff --git a/Code/Framework/AzCore/AzCore/std/ranges/ranges.h b/Code/Framework/AzCore/AzCore/std/ranges/ranges.h new file mode 100644 index 0000000000..a3ead81456 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/std/ranges/ranges.h @@ -0,0 +1,1157 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AZStd +{ + // alias std:: reverse_iterator names into AZStd:: + using std::make_reverse_iterator; +} + +namespace AZStd::ranges +{ + // Range variable templates + template + inline constexpr bool enable_borrowed_range = false; + + template + inline constexpr bool disable_sized_range = false; + + namespace Internal + { + // Variadic template which maps types to true For SFINAE + template + constexpr bool sfinae_trigger_v = true; + + template + constexpr bool is_lvalue_or_borrowable = is_lvalue_reference_v || enable_borrowed_range>; + + //! begin + template + constexpr bool has_member_begin = false; + template + constexpr bool has_member_begin().begin())>> = true; + + template + constexpr bool has_unqualified_begin = false; + template + constexpr bool has_unqualified_begin()))>> + = !has_member_begin && AZStd::Internal::is_class_or_enum; + + template + void begin(T&) = delete; + template + void begin(const T&) = delete; + + struct begin_fn + { + template + constexpr auto operator()(T& t) const noexcept -> + enable_if_t && sfinae_trigger_v>, + decltype(t + 0)> + { + return t + 0; + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward(t).begin())) -> + enable_if_t&& is_lvalue_or_borrowable + && !is_array_v&& has_member_begin, + decltype(AZStd::forward(t).begin())> + { + return AZStd::forward(t).begin(); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(begin(AZStd::forward(t)))) -> + enable_if_t&& is_lvalue_or_borrowable + && !is_array_v && has_unqualified_begin, + decltype(begin(AZStd::forward(t)))> + { + return begin(AZStd::forward(t)); + } + }; + } + inline namespace customization_point_object + { + inline constexpr Internal::begin_fn begin{}; + } + + template + using iterator_t = decltype(ranges::begin(declval())); + + namespace Internal + { + template + constexpr bool has_iterator_t = has_member_begin; + + //! end + template + constexpr bool has_member_end = false; + template + constexpr bool has_member_end().end())>> = true; + + template + constexpr bool has_unqualified_end = false; + template + constexpr bool has_unqualified_end()))>> + = !has_member_end && AZStd::Internal::is_class_or_enum; + + template + void end(T&) = delete; + template + void end(const T&) = delete; + + struct end_fn + { + template + constexpr auto operator()(T& t) const noexcept -> + enable_if_t && extent_v != 0, + decltype(t + extent_v)> + { + return t + extent_v; + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward(t).end())) -> + enable_if_t>&& is_lvalue_or_borrowable + && !is_array_v && has_member_end, + decltype(AZStd::forward(t).end())> + { + return AZStd::forward(t).end(); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(end(AZStd::forward(t)))) -> + enable_if_t>&& is_lvalue_or_borrowable + && !is_array_v && has_unqualified_end, + decltype(end(AZStd::forward(t)))> + { + return end(AZStd::forward(t)); + } + }; + } + inline namespace customization_point_object + { + inline constexpr Internal::end_fn end{}; + } + + namespace Internal + { + //! cbegin + struct cbegin_fn + { + template + constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::begin(static_cast(t)))) + ->enable_if_t, decltype(ranges::begin(static_cast(t)))> + { + return ranges::begin(static_cast(t)); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::begin(static_cast(t)))) -> + enable_if_t, decltype(ranges::begin(static_cast(t)))> + { + return ranges::begin(static_cast(t)); + } + }; + } + inline namespace customization_point_object + { + inline constexpr Internal::cbegin_fn cbegin{}; + } + + namespace Internal + { + //! cend + struct cend_fn + { + template + constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::end(static_cast(t)))) + ->enable_if_t, decltype(ranges::end(static_cast(t)))> + { + return ranges::end(static_cast(t)); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::end(static_cast(t)))) -> + enable_if_t, decltype(ranges::end(static_cast(t)))> + { + return ranges::end(static_cast(t)); + } + }; + } + inline namespace customization_point_object + { + inline constexpr Internal::cend_fn cend{}; + } + + namespace Internal + { + //! rbegin + template + constexpr bool has_member_rbegin = false; + template + constexpr bool has_member_rbegin().rbegin())>> = true; + + template + constexpr bool has_unqualified_rbegin = false; + template + constexpr bool has_unqualified_rbegin()))>> + = !has_member_rbegin && AZStd::Internal::is_class_or_enum; + + template + constexpr bool has_bidirectional_rbegin = false; + template + constexpr bool has_bidirectional_rbegin())), decltype(ranges::end(declval()))> + && bidirectional_iterator()))> + && bidirectional_iterator()))> + >> = !has_member_rbegin && !has_unqualified_rbegin; + + + template + void rbegin(T&) = delete; + template + void rbegin(const T&) = delete; + + struct rbegin_fn + { + template + constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward(t).rbegin())) -> + enable_if_t && is_lvalue_or_borrowable + && has_member_rbegin, + decltype(AZStd::forward(t).rbegin())> + { + return AZStd::forward(t).rbegin(); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(rbegin(AZStd::forward(t)))) -> + enable_if_t && is_lvalue_or_borrowable + && has_unqualified_rbegin, + decltype(rbegin(AZStd::forward(t)))> + { + return rbegin(AZStd::forward(t)); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::make_reverse_iterator(ranges::end(AZStd::forward(t))))) -> + enable_if_t, + decltype(AZStd::make_reverse_iterator(ranges::end(AZStd::forward(t))))> + { + return AZStd::make_reverse_iterator(ranges::end(AZStd::forward(t))); + } + }; + } + + inline namespace customization_point_object + { + inline constexpr Internal::rbegin_fn rbegin{}; + } + + namespace Internal + { + //! rend + template + constexpr bool has_member_rend = false; + template + constexpr bool has_member_rend().rend())>> = true; + + template + constexpr bool has_unqualified_rend = false; + template + constexpr bool has_unqualified_rend()))>> + = !has_member_rend && AZStd::Internal::is_class_or_enum; + + template + constexpr bool has_bidirectional_rend = false; + template + constexpr bool has_bidirectional_rend())), decltype(ranges::end(declval()))> + && bidirectional_iterator()))> + && bidirectional_iterator()))> + >> = !has_member_rend && !has_unqualified_rend; + + template + void rend(T&) = delete; + template + void rend(const T&) = delete; + + struct rend_fn + { + template + constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward(t).rend())) -> + enable_if_t && is_lvalue_or_borrowable + && has_member_rend, + decltype(AZStd::forward(t).rend())> + { + return AZStd::forward(t).rend(); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(rend(AZStd::forward(t)))) -> + enable_if_t && is_lvalue_or_borrowable + && has_unqualified_rend, + decltype(rend(AZStd::forward(t)))> + { + return rend(AZStd::forward(t)); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::make_reverse_iterator(ranges::begin(AZStd::forward(t))))) -> + enable_if_t, + decltype(AZStd::make_reverse_iterator(ranges::begin(AZStd::forward(t))))> + { + return AZStd::make_reverse_iterator(ranges::begin(AZStd::forward(t))); + } + }; + } + + inline namespace customization_point_object + { + inline constexpr Internal::rend_fn rend{}; + } + + namespace Internal + { + //! crbegin + struct crbegin_fn + { + template + constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::rbegin(static_cast(t)))) + ->enable_if_t, decltype(ranges::rbegin(static_cast(t)))> + { + return ranges::rbegin(static_cast(t)); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::rbegin(static_cast(t)))) -> + enable_if_t, decltype(ranges::rbegin(static_cast(t)))> + { + return ranges::rbegin(static_cast(t)); + } + }; + } + inline namespace customization_point_object + { + inline constexpr Internal::crbegin_fn crbegin{}; + } + + namespace Internal + { + //! crend + struct crend_fn + { + template + constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::rend(static_cast(t)))) + ->enable_if_t, decltype(ranges::rend(static_cast(t)))> + { + return ranges::rend(static_cast(t)); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::rend(static_cast(t)))) -> + enable_if_t, decltype(ranges::rend(static_cast(t)))> + { + return ranges::rend(static_cast(t)); + } + }; + } + inline namespace customization_point_object + { + inline constexpr Internal::crend_fn crend{}; + } + + namespace Internal + { + //! size + template + constexpr bool has_member_size = false; + template + constexpr bool has_member_size().size())>> = true; + + template + constexpr bool has_unqualified_size = false; + template + constexpr bool has_unqualified_size()))>> + = !has_member_size && AZStd::Internal::is_class_or_enum; + + template + constexpr bool has_end_subtract_begin = false; + template + constexpr bool has_end_subtract_begin()) - ranges::begin(declval()))>> + = !has_member_size && !has_unqualified_size; + + template + void size(T&) = delete; + template + void size(const T&) = delete; + + struct size_fn + { + template + constexpr auto operator()(T&) const noexcept -> + enable_if_t && extent_v != 0, + decltype(extent_v)> + { + return extent_v; + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward(t).size())) -> + enable_if_t> && has_member_size + && AZStd::Internal::is_integer_like(t).size())>, + decltype(AZStd::forward(t).size())> + { + return AZStd::forward(t).size(); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(size(AZStd::forward(t)))) -> + enable_if_t> + && has_unqualified_size + && AZStd::Internal::is_integer_like(t)))>, + decltype(size(AZStd::forward(t)))> + { + return size(AZStd::forward(t)); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::end(AZStd::forward(t)) - ranges::begin(AZStd::forward(t)))) -> + enable_if_t< + has_end_subtract_begin + && sized_sentinel_for(t))), decltype(ranges::begin(AZStd::forward(t)))> + && forward_iterator(t)))>, + AZStd::make_unsigned_t(t)) - ranges::begin(AZStd::forward(t)))>> + { + using size_type = AZStd::make_unsigned_t(t)) - ranges::begin(AZStd::forward(t)))>; + return static_cast(ranges::end(AZStd::forward(t)) - ranges::begin(AZStd::forward(t))); + } + }; + } + inline namespace customization_point_object + { + inline constexpr Internal::size_fn size{}; + } + + namespace Internal + { + //! ssize + struct ssize_fn + { + template + constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::size(t))) -> + enable_if_t<(sizeof(ptrdiff_t) > sizeof(make_signed_t)), ptrdiff_t> + { + return static_cast(ranges::size(t)); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::size(t))) -> + enable_if_t), make_signed_t> + { + using ssize_type = make_signed_t; + return static_cast(ranges::size(t)); + } + }; + } + inline namespace customization_point_object + { + inline constexpr Internal::ssize_fn ssize{}; + } + + namespace Internal + { + //! empty + template + constexpr bool has_member_empty = false; + template + constexpr bool has_member_empty().empty()), bool>>> = true; + + template + constexpr bool has_size_compare_to_0 = false; + template + constexpr bool has_size_compare_to_0()) == 0), bool> >> + = !has_member_empty; + + template + constexpr bool has_begin_compare_to_end = false; + + template + constexpr bool has_begin_compare_to_end()) == ranges::end(declval())), bool> >> + = !has_member_empty && !has_size_compare_to_0; + + struct empty_fn + { + template + [[nodiscard]] constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward(t).empty())) -> + enable_if_t && has_member_empty, bool> + { + return AZStd::forward(t).empty(); + } + + template + [[nodiscard]] constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::size(AZStd::forward(t)) == 0)) -> + enable_if_t && has_size_compare_to_0, bool> + { + return ranges::size(AZStd::forward(t)) == 0; + } + + template + [[nodiscard]] constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::begin(AZStd::forward(t)) == ranges::end(AZStd::forward(t)))) -> + enable_if_t && has_begin_compare_to_end, bool> + { + return ranges::begin(AZStd::forward(t)) == ranges::end(AZStd::forward(t)); + } + }; + } + inline namespace customization_point_object + { + inline constexpr Internal::empty_fn empty{}; + } + + namespace Internal + { + //! data + template + constexpr bool has_member_data = false; + template + constexpr bool has_member_data().data())>> = true; + + template + constexpr bool has_qualified_ranges_begin = false; + template + constexpr bool has_qualified_ranges_begin()))>> > + = !has_member_data; + + struct data_fn + { + template + constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::forward(t).data())) -> + enable_if_t && has_member_data, + decltype(AZStd::forward(t).data())> + { + return AZStd::forward(t).data(); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(AZStd::to_address(ranges::begin(AZStd::forward(t))))) -> + enable_if_t && has_qualified_ranges_begin, + decltype(AZStd::to_address(ranges::begin(AZStd::forward(t))))> + { + return AZStd::to_address(ranges::begin(AZStd::forward(t))); + } + }; + } + inline namespace customization_point_object + { + inline constexpr Internal::data_fn data{}; + } + + namespace Internal + { + //! cdata + struct cdata_fn + { + template + constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::data(static_cast(t)))) -> + enable_if_t, decltype(ranges::data(static_cast(t)))> + { + return ranges::data(static_cast(t)); + } + + template + constexpr auto operator()(T&& t) const noexcept(noexcept(ranges::data(static_cast(t)))) -> + enable_if_t, decltype(ranges::data(static_cast(t)))> + { + return ranges::data(static_cast(t)); + } + }; + } + inline namespace customization_point_object + { + inline constexpr Internal::cdata_fn cdata{}; + } +} + +namespace AZStd::ranges +{ + namespace Internal + { + template + constexpr bool range_impl = false; + template + constexpr bool range_impl())), decltype(ranges::end(declval()))>> = true; + } + + // Models range concept + template + /*concept*/ constexpr bool range = Internal::range_impl; + + // sentinal type can now be defined after the range concept has been modeled + template + using sentinel_t = enable_if_t, decltype(ranges::end(declval()))>; + + // Models borrowed range concept + template + /*concept*/ constexpr bool borrowed_range = range + && (is_lvalue_reference_v || enable_borrowed_range>); + + struct dangling + { + constexpr dangling() = default; + template + constexpr dangling(T&&...) noexcept {} + }; + + template + using borrowed_iterator_t = conditional_t, iterator_t, dangling>; + + // Models sized range concept + namespace Internal + { + template + constexpr bool sized_range_impl = false; + template + constexpr bool sized_range_impl + && sfinae_trigger_v()))> >> = true; + } + + template + /*concept*/ constexpr bool sized_range = Internal::sized_range_impl; + + namespace Internal + { + template + constexpr bool output_range_impl = false; + template + constexpr bool output_range_impl>> = range && output_iterator, T>; + } + + template + /*concept*/ constexpr bool output_range = Internal::output_range_impl; + + namespace Internal + { + template + constexpr bool input_range_impl = false; + template + constexpr bool input_range_impl>> = range && input_iterator>; + } + + template + /*concept*/ constexpr bool input_range = Internal::input_range_impl; + + namespace Internal + { + template + constexpr bool forward_range_impl = false; + template + constexpr bool forward_range_impl>> = input_range && forward_iterator>; + } + + template + /*concept*/ constexpr bool forward_range = Internal::forward_range_impl; + + namespace Internal + { + template + constexpr bool bidirectional_range_impl = false; + template + constexpr bool bidirectional_range_impl>> = forward_range && bidirectional_iterator>; + } + + template + /*concept*/ constexpr bool bidirectional_range = Internal::bidirectional_range_impl; + + namespace Internal + { + template + constexpr bool random_access_range_impl = false; + template + constexpr bool random_access_range_impl>> = bidirectional_range && random_access_iterator>; + } + + template + /*concept*/ constexpr bool random_access_range = Internal::random_access_range_impl; + + template + using range_size_t = enable_if_t, decltype(ranges::size(declval()))>; + template + using range_difference_t = enable_if_t, iter_difference_t>>; + template + using range_value_t = enable_if_t, iter_value_t>>; + template + using range_reference_t = enable_if_t, iter_reference_t>>; + template + using range_rvalue_reference_t = enable_if_t, iter_rvalue_reference_t>>; + + + namespace Internal + { + template + constexpr bool contiguous_range_impl = false; + template + constexpr bool contiguous_range_impl + && contiguous_iterator> + && same_as())), add_pointer_t>> >> = true; + } + + template + /*concept*/ constexpr bool contiguous_range = Internal::contiguous_range_impl; + + template + /*concept*/ constexpr bool common_range = range && same_as, sentinel_t>; +} + +namespace AZStd::ranges +{ + // iterator operations + // ranges::advance + namespace Internal + { + struct advance_fn + { + template + constexpr auto operator()(I& i, iter_difference_t n) const -> + enable_if_t> + { + if constexpr (random_access_iterator) + { + i += n; + } + else + { + for (; n > 0; ++i, --n) {} + + // The Precondition is that if I is not a bidirectional iterator, n must be positive + if constexpr (bidirectional_iterator) + { + for (; n < 0; --i, ++n) {} + } + } + } + + template + constexpr auto operator()(I& i, S bound) const -> + enable_if_t&& sentinel_for> + { + if constexpr (assignable_from) + { + i = AZStd::move(bound); + } + else if constexpr (sized_sentinel_for) + { + operator()(i, bound - i); + } + else + { + for (; i != bound; ++i) {} + } + } + + template + constexpr auto operator()(I& i, iter_difference_t n, S bound) const -> + enable_if_t&& sentinel_for, iter_difference_t> + { + if constexpr (sized_sentinel_for) + { + if (const auto dist = bound - i; + (n > 0 && n > dist) || (n < 0 && n < dist)) + { + // advance is limited to the i reach bound + operator()(i, bound); + return n - dist; + } + else if (n != 0) + { + // advance is limited by the value of n + operator()(i, n); + return 0; + } + + return 0; + } + else + { + for (; i != bound && n > 0; ++i, --n) {} + if constexpr (bidirectional_iterator && same_as) + { + for (; i != bound && n < 0; --i, ++n) {} + } + + return n; + } + } + }; + } + + inline namespace customization_point_object + { + inline constexpr Internal::advance_fn advance{}; + } + + // ranges::distance + namespace Internal + { + struct distance_fn + { + template + constexpr auto operator()(I first, S last) const -> + enable_if_t && sentinel_for && !sized_sentinel_for, + iter_difference_t> + { + // Since S is not a sized sentinel, can only increment from first to last + iter_difference_t result{}; + for (; first != last; ++first, ++result) {} + + return result; + } + + template + constexpr auto operator()(const I& first, const S& last) const -> + enable_if_t && sentinel_for && sized_sentinel_for, + iter_difference_t> + { + return last - first; + } + + template + constexpr auto operator()(R&& r) const -> + enable_if_t, range_difference_t> + { + if constexpr (sized_range) + { + return ranges::size(r); + } + else + { + operator()(ranges::begin(r), ranges::end(r)); + } + } + }; + } + + inline namespace customization_point_object + { + inline constexpr Internal::distance_fn distance{}; + } + + // ranges::next + namespace Internal + { + struct next_fn + { + template + constexpr auto operator()(I x) const -> + enable_if_t, I> + { + ++x; + return x; + } + + template + constexpr auto operator()(I x, iter_difference_t n) const -> + enable_if_t, I> + { + ranges::advance(x, n); + return x; + } + + template + constexpr auto operator()(I x, S bound) const -> + enable_if_t&& sentinel_for, I> + { + ranges::advance(x, bound); + return x; + } + + template + constexpr auto operator()(I x, iter_difference_t n, S bound) const -> + enable_if_t&& sentinel_for, I> + { + ranges::advance(x, n, bound); + return x; + } + }; + } + + inline namespace customization_point_object + { + inline constexpr Internal::next_fn next{}; + } + + //ranges::prev + namespace Internal + { + struct prev_fn + { + template + constexpr auto operator()(I x) const -> + enable_if_t, I> + { + --x; + return x; + } + + template + constexpr auto operator()(I x, iter_difference_t n) const -> + enable_if_t, I> + { + ranges::advance(x, -n); + return x; + } + + template + constexpr auto operator()(I x, iter_difference_t n, S bound) const -> + enable_if_t&& sentinel_for, I> + { + ranges::advance(x, -n, bound); + return x; + } + }; + } + + inline namespace customization_point_object + { + inline constexpr Internal::prev_fn prev{}; + } +} + +namespace AZStd::ranges +{ + namespace Internal + { + template + constexpr bool is_initializer_list = false; + template + constexpr bool is_initializer_list> = true; + } + + //! views + // view interface can be used with non-constant class types + template + class view_interface; + template + class view_interface< D, enable_if_t && same_as> >> + { + private: + constexpr D& derived() noexcept + { + return static_cast(*this); + } + constexpr const D& derived() const noexcept + { + return static_cast(*this); + } + + public: + template + constexpr auto empty() -> enable_if_t, bool> + { + return ranges::begin(derived()) == ranges::end(derived()); + } + template + constexpr auto empty() const -> enable_if_t, bool> + { + return ranges::begin(derived()) == ranges::end(derived()); + } + + template ()))>> + constexpr explicit operator bool() const noexcept(noexcept(ranges::empty(derived()))) + { + return !ranges::empty(derived()); + } + + template + constexpr auto data() -> + enable_if_t>, decltype(to_address(ranges::begin(derived())))> + { + return to_address(ranges::begin(derived())); + } + template + constexpr auto data() const -> + enable_if_t>, decltype(to_address(ranges::begin(derived())))> + { + return to_address(ranges::begin(derived())); + } + + template + constexpr auto size() -> + enable_if_t && sized_sentinel_for, iterator_t>, + decltype(ranges::end(derived()) - ranges::begin(derived()))> + { + return ranges::end(derived()) - ranges::begin(derived()); + } + template + constexpr auto size() const -> + enable_if_t&& sized_sentinel_for, iterator_t>, + decltype(ranges::end(derived()) - ranges::begin(derived()))> + { + return ranges::end(derived()) - ranges::begin(derived()); + } + + template + constexpr auto front() -> + enable_if_t, decltype(*ranges::begin(derived()))> + { + return *ranges::begin(derived()); + } + template + constexpr auto front() const -> + enable_if_t, decltype(*ranges::begin(derived()))> + { + return *ranges::begin(derived()); + } + + template + constexpr auto back() -> + enable_if_t && common_range, decltype(*ranges::prev(ranges::end(derived())))> + { + return *ranges::prev(ranges::end(derived())); + } + + template + constexpr auto back() const -> + enable_if_t&& common_range, decltype(*ranges::prev(ranges::end(derived())))> + { + return *ranges::prev(ranges::end(derived())); + } + + template + constexpr auto operator[](range_difference_t n) -> + enable_if_t, decltype(ranges::begin(derived())[n])> + { + return ranges::begin(derived())[n]; + } + template + constexpr auto operator[](range_difference_t n) const -> + enable_if_t, decltype(ranges::begin(derived())[n])> + { + return ranges::begin(derived())[n]; + } + }; + + struct view_base {}; + namespace Internal + { + template + void derived_from_view_interface_template(view_interface&); + template + inline constexpr bool is_derived_from_view_interface = false; + template + inline constexpr bool is_derived_from_view_interface()))> = true; + } + template + inline constexpr bool enable_view = derived_from || Internal::is_derived_from_view_interface; + + template + /*concept*/ constexpr bool view = range && movable && enable_view; + + template + /*concept*/ constexpr bool viewable_range = range && + ((view> && constructible_from, T>) || + (!view> && + (is_lvalue_reference_v || (movable> && !Internal::is_initializer_list)))); +} + + +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 + struct in_in_result + { + az_no_unique_address I1 in1; + az_no_unique_address I2 in2; + + template&& convertible_to> > + constexpr operator in_in_result() const& + { + return { in1, in2 }; + } + + template&& convertible_to> > + constexpr operator in_in_result()&& + { + return { AZStd::move(in1), AZStd::move(in2) }; + } + }; + +#undef az_no_unique_address + + template + using swap_ranges_result = in_in_result; + + namespace Internal + { + struct swap_ranges_fn + { + template + constexpr auto operator()(I1 first1, S1 last1, I2 first2, S2 last2) const -> + enable_if_t&& sentinel_for + && input_iterator&& sentinel_for + && indirectly_swappable, + swap_ranges_result> + { + for (; !(first1 == last1 or first2 == last2); ++first1, ++first2) + { + ranges::iter_swap(first1, first2); + } + return { AZStd::move(first1), AZStd::move(first2) }; + } + + template + constexpr auto operator()(R1&& r1, R2&& r2) const -> + enable_if_t&& input_range + && indirectly_swappable, iterator_t>, + swap_ranges_result, borrowed_iterator_t>> + { + return operator()(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2)); + } + }; + } + inline namespace customization_point_object + { + constexpr Internal::swap_ranges_fn swap_ranges{}; + } +} + +namespace AZStd::ranges::Internal +{ + // Implementation of ranges::swap customization point overload which calls ranges::swap_ranges + // Must be done after the ranges::swap_ranges function has been declared + // ranges::swap customization point https://eel.is/c++draft/concepts#concept.swappable-2.2 + template + constexpr auto swap_fn::operator()(T&& t, U&& u) const noexcept(noexcept((*this)(*t, *u))) + ->enable_if_t + && is_array_v && is_array_v && (extent_v == extent_v) + > + { + ranges::swap_ranges(t, u); + } +} diff --git a/Code/Framework/AzCore/AzCore/std/string/fixed_string.h b/Code/Framework/AzCore/AzCore/std/string/fixed_string.h index ea841bc4ca..bbaab391b4 100644 --- a/Code/Framework/AzCore/AzCore/std/string/fixed_string.h +++ b/Code/Framework/AzCore/AzCore/std/string/fixed_string.h @@ -74,7 +74,7 @@ namespace AZStd constexpr basic_fixed_string(const_pointer ptr); // #6 - template && !is_convertible_v>> + template && !is_convertible_v>> constexpr basic_fixed_string(InputIt first, InputIt last); // #7 @@ -146,7 +146,7 @@ namespace AZStd constexpr auto append(size_type count, Element ch) -> basic_fixed_string&; template constexpr auto append(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, basic_fixed_string&>; + -> enable_if_t && !is_convertible_v, basic_fixed_string&>; constexpr auto append(AZStd::initializer_list ilist) -> basic_fixed_string&; constexpr auto assign(const basic_fixed_string& rhs) -> basic_fixed_string&; @@ -161,7 +161,7 @@ namespace AZStd constexpr auto assign(size_type count, Element ch) -> basic_fixed_string&; template constexpr auto assign(InputIt first, InputIt last) - ->enable_if_t && !is_convertible_v, basic_fixed_string&>; + ->enable_if_t && !is_convertible_v, basic_fixed_string&>; constexpr auto assign(AZStd::initializer_list ilist) -> basic_fixed_string&; @@ -179,7 +179,7 @@ namespace AZStd constexpr auto insert(const_iterator insertPos, size_type count, Element ch) -> iterator; template constexpr auto insert(const_iterator insertPos, InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, iterator>; + -> enable_if_t && !is_convertible_v, iterator>; constexpr auto insert(const_iterator insertPos, AZStd::initializer_list ilist) -> iterator; @@ -215,7 +215,7 @@ namespace AZStd constexpr auto replace(const_iterator first, const_iterator last, size_type count, Element ch) -> basic_fixed_string&; template constexpr auto replace(const_iterator first, const_iterator last, InputIt first2, InputIt last2) - -> enable_if_t && !is_convertible_v, basic_fixed_string&>; + -> enable_if_t && !is_convertible_v, basic_fixed_string&>; constexpr auto replace(const_iterator first, const_iterator last, AZStd::initializer_list ilist) -> basic_fixed_string&; constexpr auto at(size_type offset) -> reference; diff --git a/Code/Framework/AzCore/AzCore/std/string/fixed_string.inl b/Code/Framework/AzCore/AzCore/std/string/fixed_string.inl index caa047f3c8..65fc1d88be 100644 --- a/Code/Framework/AzCore/AzCore/std/string/fixed_string.inl +++ b/Code/Framework/AzCore/AzCore/std/string/fixed_string.inl @@ -325,14 +325,14 @@ namespace AZStd template template inline constexpr auto basic_fixed_string::append(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, basic_fixed_string&> + -> enable_if_t && !is_convertible_v, basic_fixed_string&> { - if constexpr (Internal::satisfies_contiguous_iterator_concept_v + if constexpr (contiguous_iterator && is_same_v::value_type, value_type>) { return append(AZStd::to_address(first), AZStd::distance(first, last)); } - else if constexpr (Internal::is_forward_iterator_v) + else if constexpr (forward_iterator) { // Input Iterator pointer type doesn't match the const_pointer type // So the elements need to be appended one by one into the buffer @@ -461,14 +461,14 @@ namespace AZStd template template inline constexpr auto basic_fixed_string::assign(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, basic_fixed_string&> + -> enable_if_t && !is_convertible_v, basic_fixed_string&> { - if constexpr (Internal::satisfies_contiguous_iterator_concept_v + if constexpr (contiguous_iterator && is_same_v::value_type, value_type>) { return assign(AZStd::to_address(first), AZStd::distance(first, last)); } - else if constexpr (Internal::is_forward_iterator_v) + else if constexpr (forward_iterator) { // Input Iterator pointer type doesn't match the const_pointer type // So the elements need to be assigned one by one into the buffer @@ -627,15 +627,15 @@ namespace AZStd template template inline constexpr auto basic_fixed_string::insert(const_iterator insertPos, - InputIt first, InputIt last)-> enable_if_t && !is_convertible_v, iterator> + InputIt first, InputIt last)-> enable_if_t && !is_convertible_v, iterator> { // insert [_First, _Last) at _Where size_type insertOffset = AZStd::distance(cbegin(), insertPos); - if constexpr (Internal::satisfies_contiguous_iterator_concept_v + if constexpr (contiguous_iterator && is_same_v::value_type, value_type>) { insert(insertOffset, AZStd::to_address(first), AZStd::distance(first, last)); } - else if constexpr (Internal::is_forward_iterator_v) + else if constexpr (forward_iterator) { // Input Iterator pointer type doesn't match the const_pointer type // So the elements need to be inserted one by one into the buffer @@ -927,14 +927,14 @@ namespace AZStd template template inline constexpr auto basic_fixed_string::replace(const_iterator first, const_iterator last, - InputIt replaceFirst, InputIt replaceLast) -> enable_if_t && !is_convertible_v, basic_fixed_string&> + InputIt replaceFirst, InputIt replaceLast) -> enable_if_t && !is_convertible_v, basic_fixed_string&> { // replace [first, last) with [replaceFirst,replaceLast) - if constexpr (Internal::satisfies_contiguous_iterator_concept_v + if constexpr (contiguous_iterator && is_same_v::value_type, value_type>) { return replace(first, last, AZStd::to_address(replaceFirst), AZStd::distance(replaceFirst, replaceLast)); } - else if constexpr (Internal::is_forward_iterator_v) + else if constexpr (forward_iterator) { // Input Iterator pointer type doesn't match the const_pointer type // So the elements need to be appended one by one into the buffer diff --git a/Code/Framework/AzCore/AzCore/std/string/string.h b/Code/Framework/AzCore/AzCore/std/string/string.h index c65d699d15..c2f4a6953e 100644 --- a/Code/Framework/AzCore/AzCore/std/string/string.h +++ b/Code/Framework/AzCore/AzCore/std/string/string.h @@ -114,28 +114,23 @@ namespace AZStd assign(count, ch); } - template && !is_convertible_v>> + template && !is_convertible_v>> inline basic_string(InputIt first, InputIt last, const Allocator& alloc = Allocator()) : m_storage{ skip_element_tag{}, alloc } { // construct from [first, last) assign(first, last); } - inline basic_string(const_pointer first, const_pointer last) - { // construct from [first, last), const pointers - assign(first, last - first); - } - inline basic_string(const this_type& rhs) : m_storage{ skip_element_tag{}, rhs.m_storage.second() } { - assign(rhs, 0, npos); + assign(rhs); } inline basic_string(this_type&& rhs) - : m_storage{ skip_element_tag{}, AZStd::move(rhs.m_storage.second()) } + : m_storage{ skip_element_tag{}, rhs.m_storage.second() } { - assign(AZStd::forward(rhs)); + assign(AZStd::move(rhs)); } inline basic_string(const this_type& rhs, size_type rhsOffset, size_type count = npos) @@ -251,14 +246,14 @@ namespace AZStd template inline auto append(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, this_type&> + -> enable_if_t && !is_convertible_v, this_type&> { // append [first, last) - if constexpr (Internal::satisfies_contiguous_iterator_concept_v + if constexpr (contiguous_iterator && is_same_v::value_type, value_type>) { return append(AZStd::to_address(first), AZStd::distance(first, last)); } - else if constexpr (Internal::is_forward_iterator_v) + else if constexpr (forward_iterator) { // Input Iterator pointer type doesn't match the const_pointer type // So the elements need to be appended one by one into the buffer @@ -299,7 +294,7 @@ namespace AZStd inline this_type& assign(const this_type& rhs) { - return assign(rhs, 0, npos); + return this != &rhs ? assign(rhs, 0, npos) : *this; } inline this_type& assign(basic_string_view view) @@ -319,7 +314,8 @@ namespace AZStd pointer rhsData = rhs.data(); // Memmove the right hand side string data if it is using the short string optimization // Otherwise set the pointer to the right hand side - if (rhs.m_storage.first().ShortStringOptimizationActive()) + if (rhs.m_storage.first().ShortStringOptimizationActive() || + (get_allocator() != rhs.get_allocator() && !allocator_traits::propagate_on_container_move_assignment::value)) { Traits::move(data, rhsData, rhs.size() + 1); // string + null-terminator } @@ -395,14 +391,14 @@ namespace AZStd template auto assign(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, this_type&> + -> enable_if_t && !is_convertible_v, this_type&> { - if constexpr (Internal::satisfies_contiguous_iterator_concept_v + if constexpr (contiguous_iterator && is_same_v::value_type, value_type>) { return assign(AZStd::to_address(first), AZStd::distance(first, last)); } - else if constexpr (Internal::is_forward_iterator_v) + else if constexpr (forward_iterator) { // forward iterator pointer type doesn't match the const_pointer type // So the elements need to be assigned one by one into the buffer @@ -431,7 +427,7 @@ namespace AZStd inputCopy.push_back(static_cast(*first)); } - return assign(inputCopy.c_str(), inputCopy.size()); + return assign(AZStd::move(inputCopy)); } } inline this_type& insert(size_type offset, const this_type& rhs) { return insert(offset, rhs, 0, npos); } @@ -539,15 +535,15 @@ namespace AZStd template auto insert(const_iterator insertPos, InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, iterator> + -> enable_if_t && !is_convertible_v, iterator> { // insert [_First, _Last) at _Where size_type insertOffset = AZStd::distance(cbegin(), insertPos); - if constexpr (Internal::satisfies_contiguous_iterator_concept_v + if constexpr (contiguous_iterator && is_same_v::value_type, value_type>) { insert(insertOffset, AZStd::to_address(first), AZStd::distance(first, last)); } - else if constexpr (Internal::is_forward_iterator_v) + else if constexpr (forward_iterator) { // Input Iterator pointer type doesn't match the const_pointer type // So the elements need to be inserted one by one into the buffer @@ -834,14 +830,14 @@ namespace AZStd template inline auto replace(const_iterator first, const_iterator last, InputIt replaceFirst, InputIt replaceLast) - -> enable_if_t && !is_convertible_v, this_type&> + -> enable_if_t && !is_convertible_v, this_type&> { - if constexpr (Internal::satisfies_contiguous_iterator_concept_v + if constexpr (contiguous_iterator && is_same_v::value_type, value_type>) { return replace(first, last, AZStd::to_address(replaceFirst), AZStd::distance(replaceFirst, replaceLast)); } - else if constexpr (Internal::is_forward_iterator_v) + else if constexpr (forward_iterator) { // Input Iterator pointer type doesn't match the const_pointer type // So the elements need to be appended one by one into the buffer @@ -1031,12 +1027,19 @@ namespace AZStd // same allocator, swap storage m_storage.first().swap(rhs.m_storage.first()); } + else if (allocator_traits::propagate_on_container_swap::value) + { + // The allocator propagates on swap, so the allocators can be swapped + m_storage.first().swap(rhs.m_storage.first()); + using AZStd::swap; + swap(m_storage.second(), rhs.m_storage.second()); + } else { // different allocator, do multiple assigns - this_type tmp = *this; - *this = rhs; - rhs = tmp; + this_type tmp = AZStd::move(*this); + *this = AZStd::move(rhs); + rhs = AZStd::move(tmp); } } diff --git a/Code/Framework/AzCore/AzCore/std/string/string_view.h b/Code/Framework/AzCore/AzCore/std/string/string_view.h index 1579e43f4d..c333e2cea1 100644 --- a/Code/Framework/AzCore/AzCore/std/string/string_view.h +++ b/Code/Framework/AzCore/AzCore/std/string/string_view.h @@ -7,6 +7,7 @@ */ #pragma once +#include #include #include #include @@ -613,8 +614,9 @@ namespace AZStd {} template - && is_same_v::value_type, value_type> + contiguous_iterator + && sized_sentinel_for + && is_same_v, value_type> && !is_convertible_v> > constexpr basic_string_view(It first, End last) @@ -961,23 +963,6 @@ namespace AZStd using string_view = basic_string_view; using wstring_view = basic_string_view; - template> - using basic_const_string = basic_string_view; - using const_string = string_view; - using const_wstring = wstring_view; - - template > - constexpr typename basic_string_view::const_iterator begin(basic_string_view sv) - { - return sv.begin(); - } - - template > - constexpr typename basic_string_view::const_iterator end(basic_string_view sv) - { - return sv.end(); - } - inline namespace literals { inline namespace string_view_literals @@ -1024,6 +1009,15 @@ namespace AZStd } // namespace AZStd +namespace AZStd::ranges +{ + template + inline constexpr bool enable_borrowed_range> = true; + + template + inline constexpr bool enable_view> = true; +} + //! Use this macro to simplify safe printing of a string_view which may not be null-terminated. //! Example: AZStd::string::format("Safely formatted: %.*s", AZ_STRING_ARG(myString)); #define AZ_STRING_ARG(str) aznumeric_cast(str.size()), str.data() diff --git a/Code/Framework/AzCore/AzCore/std/typetraits/common_reference.h b/Code/Framework/AzCore/AzCore/std/typetraits/common_reference.h new file mode 100644 index 0000000000..51fd9c1dda --- /dev/null +++ b/Code/Framework/AzCore/AzCore/std/typetraits/common_reference.h @@ -0,0 +1,230 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AZStd +{ + template class TQual, template class UQual> + struct basic_common_reference + {}; +} + +namespace AZStd::Internal +{ + // const volatile and reference qualifier copy templates + template + struct copy_cv_qual + { + using type = conditional_t, conditional_t, const volatile QualType, const QualType>, + conditional_t, volatile QualType, QualType>>; + }; + + template + using copy_cv_qual_t = typename copy_cv_qual::type; + + static_assert(is_same_v, float>); + static_assert(is_same_v, const float>); + static_assert(is_same_v, volatile float>); + static_assert(is_same_v, const volatile float>); + static_assert(is_same_v, const float>); + static_assert(is_same_v, const float>); + static_assert(is_same_v, const volatile float>); + static_assert(is_same_v, const volatile float>); + static_assert(is_same_v, volatile float>); + static_assert(is_same_v, const volatile float>); + static_assert(is_same_v, volatile float>); + static_assert(is_same_v, const volatile float>); + static_assert(is_same_v, const volatile float>); + static_assert(is_same_v, const volatile float>); + static_assert(is_same_v, const volatile float>); + static_assert(is_same_v, const volatile float>); + + template + struct copy_reference_qual + { + using type = conditional_t, QualType&, + conditional_t, QualType&&, QualType>>; + }; + + template + using copy_reference_qual_t = typename copy_reference_qual::type; + + static_assert(is_same_v, float>); + static_assert(is_same_v, float&>); + static_assert(is_same_v, float&&>); + static_assert(is_same_v, float&>); + static_assert(is_same_v, float&>); + static_assert(is_same_v, float&>); + static_assert(is_same_v, float&&>); + static_assert(is_same_v, float&>); + static_assert(is_same_v, float&&>); + + template + using copy_cvref_qual_t = copy_cv_qual_t, QualType>; + + template + struct copy_qualifiers_from_t + { + template + using templ = copy_cvref_qual_t; + }; + + template + using cond_res = decltype(false ? declval, remove_reference_t>&>() + : declval, remove_reference_t>&>()); + + // common reference helper templates begin + template + struct common_reference_base_reference_test; + + // COMMON_REF is defined within the C++ standard at https://eel.is/c++draft/meta.trans.other#3.5 + template + struct common_reference_base_reference_test&& is_lvalue_reference_v, + void_t> >> + { + // Uses the ternary operator for determining the common type + using type = cond_res; + }; + + template + struct common_reference_base_reference_test&& is_rvalue_reference_v>> + { + using C = remove_reference_t&, remove_reference_t&>::type>; + using type = AZStd::enable_if_t&& is_convertible_v, C>; + }; + + template + struct common_reference_base_reference_test&& is_lvalue_reference_v>> + { + // Turn rvalue references to const lvalue references + using D = typename common_reference_base_reference_test&, remove_reference_t&>::type; + using type = AZStd::enable_if_t, D>; + }; + + template + struct common_reference_base_reference_test&& is_rvalue_reference_v>> + { + // Swap the parameters to call the 3rd specialization for common_reference_base_reference_test + using type = typename common_reference_base_reference_test::type; + }; + + template + constexpr bool has_reference_test = false; + + template + constexpr bool has_reference_test::type>> = true; + + template + struct basic_common_reference_test; + + template + struct basic_common_reference_test, remove_cvref_t, + copy_qualifiers_from_t::template templ, copy_qualifiers_from_t::template templ>::type>> + { + using type = typename basic_common_reference, remove_cvref_t, + copy_qualifiers_from_t::template templ, copy_qualifiers_from_t::template templ>::type; + }; + + template + constexpr bool has_basic_common_reference_test = false; + + template + constexpr bool has_basic_common_reference_test::type>> = true; + + template + constexpr bool has_condition_result_test = false; + + template + constexpr bool has_condition_result_test() : declval())>> = true; + + template + struct common_reference_base_test + {}; + + template + struct common_reference_base_test>> + : common_reference_base_reference_test + {}; + template + struct common_reference_base_test + && has_basic_common_reference_test>> + : basic_common_reference_test + {}; + template + struct common_reference_base_test + && !has_basic_common_reference_test && has_condition_result_test>> + { + using type = decltype(false ? declval() : declval()); + }; + template + struct common_reference_base_test + && !has_basic_common_reference_test && !has_condition_result_test>> + : common_type + {}; + + template + struct common_reference_base + {}; + + template + struct common_reference_base + { + using type = T; + }; + template + struct common_reference_base + : common_reference_base_test + {}; + + template + struct common_reference_base + : common_reference_base::type, V, Rs...> + {}; +} +namespace AZStd +{ + template + struct common_reference + : Internal::common_reference_base + {}; + + template + using common_reference_t = typename common_reference::type; + + // models the common reference concept + namespace Internal + { + template + constexpr bool common_reference_with_impl = false; + template + constexpr bool common_reference_with_impl, common_reference_t> + && convertible_to> + && convertible_to> + >> = true; + } + + template + /*concept*/ constexpr bool common_reference_with = Internal::common_reference_with_impl; +} diff --git a/Code/Framework/AzCore/AzCore/std/typetraits/is_convertible.h b/Code/Framework/AzCore/AzCore/std/typetraits/is_convertible.h index 65f3b9908c..1f2933cecf 100644 --- a/Code/Framework/AzCore/AzCore/std/typetraits/is_convertible.h +++ b/Code/Framework/AzCore/AzCore/std/typetraits/is_convertible.h @@ -8,12 +8,26 @@ #pragma once +#include #include +#include +#include namespace AZStd { using std::is_convertible; + using std::is_convertible_v; + + // models the C++20 convertible_to concept + namespace Internal + { + template + constexpr bool convertible_to_impl = false; + template + constexpr bool convertible_to_impl, void_t(declval()))>>> = true; + } template - constexpr bool is_convertible_v = std::is_convertible_v; + /*concept*/ constexpr bool convertible_to = Internal::convertible_to_impl; } diff --git a/Code/Framework/AzCore/AzCore/std/typetraits/is_destructible.h b/Code/Framework/AzCore/AzCore/std/typetraits/is_destructible.h index ac03f01d3b..39d79781f4 100644 --- a/Code/Framework/AzCore/AzCore/std/typetraits/is_destructible.h +++ b/Code/Framework/AzCore/AzCore/std/typetraits/is_destructible.h @@ -21,4 +21,7 @@ namespace AZStd constexpr bool is_trivially_destructible_v = std::is_trivially_destructible::value; template constexpr bool is_nothrow_destructible_v = std::is_nothrow_destructible::value; + + template + /*concept*/ constexpr bool destructible = is_nothrow_destructible_v; } diff --git a/Code/Framework/AzCore/AzCore/std/typetraits/is_floating_point.h b/Code/Framework/AzCore/AzCore/std/typetraits/is_floating_point.h index 7408866ed7..b973ac714c 100644 --- a/Code/Framework/AzCore/AzCore/std/typetraits/is_floating_point.h +++ b/Code/Framework/AzCore/AzCore/std/typetraits/is_floating_point.h @@ -13,4 +13,7 @@ namespace AZStd { using std::is_floating_point; using std::is_floating_point_v; + + template + /*concept*/ constexpr bool floating_point = is_floating_point_v; } diff --git a/Code/Framework/AzCore/AzCore/std/typetraits/is_integral.h b/Code/Framework/AzCore/AzCore/std/typetraits/is_integral.h index a51e06dc06..eb699aa6d1 100644 --- a/Code/Framework/AzCore/AzCore/std/typetraits/is_integral.h +++ b/Code/Framework/AzCore/AzCore/std/typetraits/is_integral.h @@ -13,4 +13,7 @@ namespace AZStd { using std::is_integral; using std::is_integral_v; + + template + /*concept*/ constexpr bool integral = is_integral_v; } diff --git a/Code/Framework/AzCore/AzCore/std/typetraits/is_same.h b/Code/Framework/AzCore/AzCore/std/typetraits/is_same.h index 858e9b1f52..61037d7549 100644 --- a/Code/Framework/AzCore/AzCore/std/typetraits/is_same.h +++ b/Code/Framework/AzCore/AzCore/std/typetraits/is_same.h @@ -13,4 +13,8 @@ namespace AZStd { using std::is_same; using std::is_same_v; + + // models the same_as concept + template + /*concept*/ constexpr bool same_as = is_same_v; } diff --git a/Code/Framework/AzCore/AzCore/std/typetraits/typetraits.h b/Code/Framework/AzCore/AzCore/std/typetraits/typetraits.h index d29b0c28ee..bf45d15836 100644 --- a/Code/Framework/AzCore/AzCore/std/typetraits/typetraits.h +++ b/Code/Framework/AzCore/AzCore/std/typetraits/typetraits.h @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/Code/Framework/AzCore/AzCore/std/utility/declval.h b/Code/Framework/AzCore/AzCore/std/utility/declval.h new file mode 100644 index 0000000000..c017bb142a --- /dev/null +++ b/Code/Framework/AzCore/AzCore/std/utility/declval.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include + +namespace AZStd +{ + using std::declval; +} diff --git a/Code/Framework/AzCore/AzCore/std/utility/move.h b/Code/Framework/AzCore/AzCore/std/utility/move.h new file mode 100644 index 0000000000..71f2a49b18 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/std/utility/move.h @@ -0,0 +1,19 @@ +/* + * 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 + +namespace AZStd +{ + // rvalue + // rvalue move + template + constexpr AZStd::remove_reference_t&& move(T&& t) + { + return static_cast&&>(t); + } +} diff --git a/Code/Framework/AzCore/AzCore/std/utils.h b/Code/Framework/AzCore/AzCore/std/utils.h index 4c918250de..d3ead8022b 100644 --- a/Code/Framework/AzCore/AzCore/std/utils.h +++ b/Code/Framework/AzCore/AzCore/std/utils.h @@ -22,22 +22,15 @@ #include #include #include +#include +#include #include namespace AZStd { ////////////////////////////////////////////////////////////////////////// - // rvalue - // rvalue move - template - constexpr AZStd::remove_reference_t&& move(T && t) - { - return static_cast&&>(t); - } - using std::forward; - using std::declval; using std::exchange; template diff --git a/Code/Framework/AzCore/Tests/AZStd/ConceptsTests.cpp b/Code/Framework/AzCore/Tests/AZStd/ConceptsTests.cpp new file mode 100644 index 0000000000..718edfcde4 --- /dev/null +++ b/Code/Framework/AzCore/Tests/AZStd/ConceptsTests.cpp @@ -0,0 +1,202 @@ +/* + * 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 +#include + +namespace UnitTest +{ + class ConceptsTestFixture + : public ScopedAllocatorSetupFixture + {}; + + TEST_F(ConceptsTestFixture, GeneralConcepts) + { + + // concept same_as + static_assert(AZStd::same_as); + static_assert(!AZStd::same_as); + + // concept derived_from + static_assert(AZStd::derived_from); + static_assert(!AZStd::derived_from); + + // concept convertible_to + static_assert(AZStd::convertible_to); + static_assert(!AZStd::convertible_to); + + + // Test structs to validate common_reference_with and common_with concepts + struct Base {}; + struct TestBase : Base {}; + struct NoMove + { + NoMove(NoMove&&) = delete; + NoMove& operator=(NoMove&&) = delete; + }; + struct NoDestructible + { + ~NoDestructible() = delete; + }; + struct NoDefaultInitializable + { + NoDefaultInitializable(bool); + }; + + struct CopyOnly + { + CopyOnly(const CopyOnly&) = default; + }; + struct MoveOnly + { + MoveOnly(MoveOnly&&) = default; + }; + + struct MoveableButNotCopyable + { + MoveableButNotCopyable(MoveableButNotCopyable&&) = default; + MoveableButNotCopyable& operator=(MoveableButNotCopyable&&) = default; + }; + + // concept common_reference_with + static_assert(AZStd::common_reference_with); + static_assert(AZStd::same_as, const Base&>); + static_assert(!AZStd::common_reference_with); + + // concept common_with + static_assert(AZStd::common_with); + static_assert(!AZStd::common_with); + + // arithmetic concepts + // concept integral + static_assert(AZStd::integral); + static_assert(!AZStd::integral); + + // concept signed_integral + static_assert(AZStd::signed_integral); + static_assert(!AZStd::signed_integral); + static_assert(!AZStd::signed_integral); + + // concept signed_integral + static_assert(AZStd::unsigned_integral); + static_assert(!AZStd::unsigned_integral); + static_assert(!AZStd::unsigned_integral); + + // concept floating_point + static_assert(AZStd::floating_point); + static_assert(!AZStd::floating_point); + + // concept assignable_from + static_assert(AZStd::assignable_from); + static_assert(!AZStd::assignable_from); + + // concept swappable + static_assert(AZStd::swappable); + static_assert(!AZStd::swappable); + static_assert(AZStd::swappable_with); + static_assert(!AZStd::swappable_with); + + // concept destructible + static_assert(AZStd::destructible); + static_assert(!AZStd::destructible); + + // concept constructible_from + static_assert(AZStd::constructible_from); + static_assert(!AZStd::constructible_from); + + // concept default_initializable + static_assert(AZStd::default_initializable); + static_assert(!AZStd::default_initializable); + + // concept move_constructible + static_assert(AZStd::move_constructible); + static_assert(!AZStd::move_constructible); + + // concept copy_constructible + static_assert(AZStd::copy_constructible); + static_assert(!AZStd::copy_constructible); + + // concept equality_comparable + static_assert(AZStd::equality_comparable); + static_assert(!AZStd::equality_comparable); + static_assert(AZStd::equality_comparable_with); + static_assert(!AZStd::equality_comparable_with); + static_assert(!AZStd::equality_comparable_with); + + // concept totally_ordered + static_assert(AZStd::totally_ordered); + static_assert(!AZStd::totally_ordered); + static_assert(AZStd::totally_ordered_with); + static_assert(!AZStd::totally_ordered_with); + static_assert(!AZStd::totally_ordered_with); + + // concept movable + static_assert(AZStd::movable); + static_assert(!AZStd::movable); + + // concept copyable + static_assert(AZStd::copyable); + static_assert(!AZStd::copyable); + + // concept semiregular + static_assert(AZStd::semiregular); + static_assert(!AZStd::semiregular); + + // concept regular + static_assert(AZStd::regular); + static_assert(!AZStd::regular); + + // concept invocable + static_assert(AZStd::invocable); + static_assert(!AZStd::invocable); + + // concept predicate + auto BooleanPredicate = [](double) -> int + { + return 0; + }; + auto BasePredicate = [](int) -> Base + { + return Base{}; + }; + + static_assert(AZStd::predicate); + static_assert(!AZStd::predicate); + static_assert(!AZStd::predicate); + + // concept relation + struct RelationPredicate + { + bool operator()(AZStd::string_view, Base) const; + bool operator()(Base, AZStd::string_view) const; + bool operator()(AZStd::string_view, AZStd::string_view) const; + bool operator()(Base, Base) const; + + // non-complete relation + bool operator()(Base, int) const; + bool operator()(int, Base) const; + }; + + static_assert(AZStd::relation); + static_assert(AZStd::relation); + static_assert(!AZStd::relation); + static_assert(!AZStd::relation); + + //concept equivalence_relation + static_assert(AZStd::equivalence_relation); + static_assert(AZStd::equivalence_relation); + static_assert(!AZStd::equivalence_relation); + static_assert(!AZStd::equivalence_relation); + + //concept strict_weak_order + static_assert(AZStd::strict_weak_order); + static_assert(AZStd::strict_weak_order); + static_assert(!AZStd::strict_weak_order); + static_assert(!AZStd::strict_weak_order); + } +} diff --git a/Code/Framework/AzCore/Tests/AZStd/Iterators.cpp b/Code/Framework/AzCore/Tests/AZStd/Iterators.cpp index 720aa0c7a8..28b596398e 100644 --- a/Code/Framework/AzCore/Tests/AZStd/Iterators.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/Iterators.cpp @@ -6,6 +6,7 @@ * */ #include "UserTypes.h" +#include #include #include #include @@ -13,13 +14,11 @@ #include #include -using namespace AZStd; -using namespace UnitTestInternal; namespace UnitTest { class Iterators - : public AllocatorsFixture + : public ScopedAllocatorSetupFixture { }; @@ -28,30 +27,30 @@ namespace UnitTest { Container int_container = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }}; - typename Container::iterator iter_begin = begin(int_container); + typename Container::iterator iter_begin = AZStd::begin(int_container); EXPECT_EQ(*iter_begin, 0); - EXPECT_EQ(*next(iter_begin), 1); - EXPECT_EQ(*next(iter_begin, 2), 2); + EXPECT_EQ(*AZStd::next(iter_begin), 1); + EXPECT_EQ(*AZStd::next(iter_begin, 2), 2); ++iter_begin; EXPECT_EQ(*iter_begin, 1); typename Container::iterator iter_end = end(int_container); EXPECT_EQ(iter_end, int_container.end()); - EXPECT_EQ(*prev(iter_end), 9); - EXPECT_EQ(*prev(iter_end, 2), 8); + EXPECT_EQ(*AZStd::prev(iter_end), 9); + EXPECT_EQ(*AZStd::prev(iter_end, 2), 8); --iter_end; EXPECT_EQ(*iter_end, 9); - typename Container::reverse_iterator iter_rbegin = rbegin(int_container); + typename Container::reverse_iterator iter_rbegin = AZStd::rbegin(int_container); EXPECT_EQ(*iter_rbegin, 9); - EXPECT_EQ(*next(iter_rbegin), 8); - EXPECT_EQ(*next(iter_rbegin, 2), 7); + EXPECT_EQ(*AZStd::next(iter_rbegin), 8); + EXPECT_EQ(*AZStd::next(iter_rbegin, 2), 7); ++iter_rbegin; EXPECT_EQ(*iter_rbegin, 8); - typename Container::reverse_iterator iter_rend = rend(int_container); - EXPECT_EQ(*prev(iter_rend), 0); - EXPECT_EQ(*prev(iter_rend, 2), 1); + typename Container::reverse_iterator iter_rend = AZStd::rend(int_container); + EXPECT_EQ(*AZStd::prev(iter_rend), 0); + EXPECT_EQ(*AZStd::prev(iter_rend, 2), 1); --iter_rend; EXPECT_EQ(*iter_rend, 0); @@ -65,30 +64,30 @@ namespace UnitTest { Container int_container = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }}; - typename Container::const_iterator iter_cbegin = cbegin(int_container); + typename Container::const_iterator iter_cbegin = AZStd::cbegin(int_container); EXPECT_EQ(*iter_cbegin, 0); - EXPECT_EQ(*next(iter_cbegin), 1); - EXPECT_EQ(*next(iter_cbegin, 2), 2); + EXPECT_EQ(*AZStd::next(iter_cbegin), 1); + EXPECT_EQ(*AZStd::next(iter_cbegin, 2), 2); ++iter_cbegin; EXPECT_EQ(*iter_cbegin, 1); - typename Container::const_iterator iter_cend = cend(int_container); + typename Container::const_iterator iter_cend = AZStd::cend(int_container); EXPECT_EQ(iter_cend, int_container.cend()); - EXPECT_EQ(*prev(iter_cend), 9); - EXPECT_EQ(*prev(iter_cend, 2), 8); + EXPECT_EQ(*AZStd::prev(iter_cend), 9); + EXPECT_EQ(*AZStd::prev(iter_cend, 2), 8); --iter_cend; EXPECT_EQ(*iter_cend, 9); - typename Container::const_reverse_iterator iter_crbegin = crbegin(int_container); + typename Container::const_reverse_iterator iter_crbegin = AZStd::crbegin(int_container); EXPECT_EQ(*iter_crbegin, 9); - EXPECT_EQ(*next(iter_crbegin), 8); - EXPECT_EQ(*next(iter_crbegin, 2), 7); + EXPECT_EQ(*AZStd::next(iter_crbegin), 8); + EXPECT_EQ(*AZStd::next(iter_crbegin, 2), 7); ++iter_crbegin; EXPECT_EQ(*iter_crbegin, 8); - typename Container::const_reverse_iterator iter_crend = crend(int_container); - EXPECT_EQ(*prev(iter_crend), 0); - EXPECT_EQ(*prev(iter_crend, 2), 1); + typename Container::const_reverse_iterator iter_crend = AZStd::crend(int_container); + EXPECT_EQ(*AZStd::prev(iter_crend), 0); + EXPECT_EQ(*AZStd::prev(iter_crend, 2), 1); --iter_crend; EXPECT_EQ(*iter_crend, 0); } @@ -98,15 +97,15 @@ namespace UnitTest { const ConstContainer const_int_container = {{ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }}; - typename ConstContainer::const_iterator const_iter_begin = begin(const_int_container); + typename ConstContainer::const_iterator const_iter_begin = AZStd::begin(const_int_container); EXPECT_EQ(*const_iter_begin, 10); - EXPECT_EQ(*next(const_iter_begin), 11); - EXPECT_EQ(*next(const_iter_begin, 2), 12); + EXPECT_EQ(*AZStd::next(const_iter_begin), 11); + EXPECT_EQ(*AZStd::next(const_iter_begin, 2), 12); - typename ConstContainer::const_iterator const_iter_end = end(const_int_container); + typename ConstContainer::const_iterator const_iter_end = AZStd::end(const_int_container); EXPECT_EQ(const_iter_end, const_int_container.end()); - EXPECT_EQ(*prev(const_iter_end), 19); - EXPECT_EQ(*prev(const_iter_end, 2), 18); + EXPECT_EQ(*AZStd::prev(const_iter_end), 19); + EXPECT_EQ(*AZStd::prev(const_iter_end, 2), 18); } TEST_F(Iterators, FunctionWrappers_MutableContainers) @@ -136,87 +135,78 @@ namespace UnitTest { int int_array[10] = { 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 }; - EXPECT_EQ(*begin(int_array), 20); - EXPECT_EQ(*next(begin(int_array)), 21); - EXPECT_EQ(*next(begin(int_array), 2), 22); + EXPECT_EQ(*AZStd::begin(int_array), 20); + EXPECT_EQ(*AZStd::next(AZStd::begin(int_array)), 21); + EXPECT_EQ(*AZStd::next(AZStd::begin(int_array), 2), 22); - EXPECT_EQ(end(int_array) - AZ_ARRAY_SIZE(int_array), begin(int_array)); - EXPECT_EQ(*prev(end(int_array)), 29); - EXPECT_EQ(*prev(end(int_array), 2), 28); + EXPECT_EQ(AZStd::end(int_array) - AZ_ARRAY_SIZE(int_array), AZStd::begin(int_array)); + EXPECT_EQ(*AZStd::prev(AZStd::end(int_array)), 29); + EXPECT_EQ(*AZStd::prev(AZStd::end(int_array), 2), 28); - EXPECT_EQ(*rbegin(int_array), 29); - EXPECT_EQ(*next(rbegin(int_array)), 28); - EXPECT_EQ(*next(rbegin(int_array), 2), 27); + EXPECT_EQ(*AZStd::rbegin(int_array), 29); + EXPECT_EQ(*AZStd::next(AZStd::rbegin(int_array)), 28); + EXPECT_EQ(*AZStd::next(AZStd::rbegin(int_array), 2), 27); - EXPECT_EQ(*prev(rend(int_array)), 20); - EXPECT_EQ(*prev(rend(int_array), 2), 21); + EXPECT_EQ(*AZStd::prev(AZStd::rend(int_array)), 20); + EXPECT_EQ(*AZStd::prev(AZStd::rend(int_array), 2), 21); - EXPECT_EQ(*crbegin(int_array), 29); - EXPECT_EQ(*next(crbegin(int_array)), 28); - EXPECT_EQ(*next(crbegin(int_array), 2), 27); + EXPECT_EQ(*AZStd::crbegin(int_array), 29); + EXPECT_EQ(*AZStd::next(AZStd::crbegin(int_array)), 28); + EXPECT_EQ(*AZStd::next(AZStd::crbegin(int_array), 2), 27); - EXPECT_EQ(*prev(crend(int_array)), 20); - EXPECT_EQ(*prev(crend(int_array), 2), 21); + EXPECT_EQ(*AZStd::prev(AZStd::crend(int_array)), 20); + EXPECT_EQ(*AZStd::prev(AZStd::crend(int_array), 2), 21); //verify we can successfully modify the value in a non-const iterator - *begin(int_array) = -42; - EXPECT_EQ(*begin(int_array), -42); + *AZStd::begin(int_array) = -42; + EXPECT_EQ(*AZStd::begin(int_array), -42); } TEST_F(Iterators, FunctionWrappers_ConstRawArray) { const int const_int_array[10] = { 30, 31, 32, 33, 34, 35, 36, 37, 38, 39 }; - EXPECT_EQ(*cbegin(const_int_array), 30); - EXPECT_EQ(*next(cbegin(const_int_array)), 31); - EXPECT_EQ(*next(cbegin(const_int_array), 2), 32); + EXPECT_EQ(*AZStd::cbegin(const_int_array), 30); + EXPECT_EQ(*AZStd::next(AZStd::cbegin(const_int_array)), 31); + EXPECT_EQ(*AZStd::next(AZStd::cbegin(const_int_array), 2), 32); - EXPECT_EQ(cend(const_int_array) - AZ_ARRAY_SIZE(const_int_array), cbegin(const_int_array)); - EXPECT_EQ(*prev(cend(const_int_array)), 39); - EXPECT_EQ(*prev(cend(const_int_array), 2), 38); + EXPECT_EQ(AZStd::cend(const_int_array) - AZ_ARRAY_SIZE(const_int_array), AZStd::cbegin(const_int_array)); + EXPECT_EQ(*AZStd::prev(AZStd::cend(const_int_array)), 39); + EXPECT_EQ(*AZStd::prev(AZStd::cend(const_int_array), 2), 38); } TEST_F(Iterators, IteratorTraits_ResolveAtCompileTime) { using list_type = AZStd::list; - static_assert(AZStd::Internal::has_iterator_category_v); - static_assert(AZStd::Internal::has_iterator_type_aliases_v); constexpr bool list_type_iterator_type_aliases = AZStd::Internal::has_iterator_type_aliases_v; static_assert(AZStd::is_convertible_v::iterator_category, AZStd::input_iterator_tag>); - static_assert(is_same_v::iterator_category, bidirectional_iterator_tag>); - static_assert(is_same_v::value_type, int>); - static_assert(is_same_v::difference_type, AZStd::ptrdiff_t>); - static_assert(is_same_v::pointer, int*>); - static_assert(is_same_v::reference, int&>); - static_assert(AZStd::Internal::is_input_iterator_v); - static_assert(!AZStd::Internal::has_iterator_concept_v>); - static_assert(!AZStd::Internal::satisfies_contiguous_iterator_concept_v); + static_assert(AZStd::is_same_v::iterator_category, AZStd::bidirectional_iterator_tag>); + static_assert(AZStd::is_same_v::value_type, int>); + static_assert(AZStd::is_same_v::difference_type, AZStd::ptrdiff_t>); + static_assert(AZStd::is_same_v::pointer, int*>); + static_assert(AZStd::is_same_v::reference, int&>); + static_assert(AZStd::input_iterator); + static_assert(!AZStd::contiguous_iterator); - static_assert(AZStd::Internal::has_iterator_category_v); - static_assert(AZStd::Internal::has_iterator_type_aliases_v); constexpr bool list_type_const_iterator_type_aliases = AZStd::Internal::has_iterator_type_aliases_v; static_assert(AZStd::is_convertible_v::iterator_category, AZStd::input_iterator_tag>); - static_assert(is_same_v::iterator_category, bidirectional_iterator_tag>); - static_assert(is_same_v::value_type, int>); - static_assert(is_same_v::difference_type, AZStd::ptrdiff_t>); - static_assert(is_same_v::pointer, const int*>); - static_assert(is_same_v::reference, const int&>); - static_assert(AZStd::Internal::is_input_iterator_v); - static_assert(!AZStd::Internal::has_iterator_concept_v>); - static_assert(!AZStd::Internal::satisfies_contiguous_iterator_concept_v); + static_assert(AZStd::is_same_v::iterator_category, AZStd::bidirectional_iterator_tag>); + static_assert(AZStd::is_same_v::value_type, int>); + static_assert(AZStd::is_same_v::difference_type, AZStd::ptrdiff_t>); + static_assert(AZStd::is_same_v::pointer, const int*>); + static_assert(AZStd::is_same_v::reference, const int&>); + static_assert(AZStd::input_iterator); + static_assert(!AZStd::contiguous_iterator); using pointer_type = const char*; - static_assert(AZStd::Internal::has_iterator_category_v>); - static_assert(AZStd::Internal::has_iterator_type_aliases_v>); - static_assert(is_same_v::iterator_concept, contiguous_iterator_tag>); - static_assert(is_same_v::iterator_category, random_access_iterator_tag>); - static_assert(is_same_v::value_type, char>); - static_assert(is_same_v::difference_type, AZStd::ptrdiff_t>); - static_assert(is_same_v::pointer, const char*>); - static_assert(is_same_v::reference, const char&>); - static_assert(AZStd::Internal::has_iterator_concept_v>); - static_assert(AZStd::Internal::satisfies_contiguous_iterator_concept_v); + static_assert(AZStd::is_same_v::iterator_concept, AZStd::contiguous_iterator_tag>); + static_assert(AZStd::is_same_v::iterator_category, AZStd::random_access_iterator_tag>); + static_assert(AZStd::is_same_v::value_type, char>); + static_assert(AZStd::is_same_v::difference_type, AZStd::ptrdiff_t>); + static_assert(AZStd::is_same_v::pointer, const char*>); + static_assert(AZStd::is_same_v::reference, const char&>); + static_assert(AZStd::contiguous_iterator); } } diff --git a/Code/Framework/AzCore/Tests/AZStd/RangesTests.cpp b/Code/Framework/AzCore/Tests/AZStd/RangesTests.cpp new file mode 100644 index 0000000000..7d0751a519 --- /dev/null +++ b/Code/Framework/AzCore/Tests/AZStd/RangesTests.cpp @@ -0,0 +1,583 @@ +/* + * 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 +#include +#include + +namespace UnitTest +{ + class RangesTestFixture + : public ScopedAllocatorSetupFixture + {}; + + struct RangeLikeCustomizationPoint {}; + + RangeLikeCustomizationPoint* begin(RangeLikeCustomizationPoint& rangeLike) + { + return &rangeLike; + } + RangeLikeCustomizationPoint* end(RangeLikeCustomizationPoint& rangeLike) + { + return &rangeLike; + } + const RangeLikeCustomizationPoint* cbegin(const RangeLikeCustomizationPoint& rangeLike) + { + return &rangeLike; + } + const RangeLikeCustomizationPoint* cend(const RangeLikeCustomizationPoint& rangeLike) + { + return &rangeLike; + } + RangeLikeCustomizationPoint* rbegin(RangeLikeCustomizationPoint& rangeLike) + { + return &rangeLike; + } + RangeLikeCustomizationPoint* rend(RangeLikeCustomizationPoint& rangeLike) + { + return &rangeLike; + } + const RangeLikeCustomizationPoint* crbegin(const RangeLikeCustomizationPoint& rangeLike) + { + return &rangeLike; + } + const RangeLikeCustomizationPoint* crend(const RangeLikeCustomizationPoint& rangeLike) + { + return &rangeLike; + } + + constexpr size_t size(const RangeLikeCustomizationPoint&) + { + return 0; + } + constexpr size_t size(RangeLikeCustomizationPoint&) + { + return 0; + } + + + // range access + TEST_F(RangesTestFixture, RangesBegin_Compiles_WithExtentArray) + { + using ArrayExtentType = int[5]; + + ArrayExtentType extentArray{}; + EXPECT_EQ(extentArray + 0, AZStd::ranges::begin(extentArray)); + } + + TEST_F(RangesTestFixture, RangesBegin_DoesNotCompile_WithNoExtentArray) + { + using ArrayNoExtentType = int[]; + static_assert(!AZStd::invocable); + } + + TEST_F(RangesTestFixture, RangesBegin_Compiles_WithMemberOverload) + { + AZStd::string_view strView; + EXPECT_EQ(strView.begin(), AZStd::ranges::begin(strView)); + } + + TEST_F(RangesTestFixture, RangesBegin_Compiles_WithADL) + { + RangeLikeCustomizationPoint rangeLike; + EXPECT_EQ(&rangeLike, AZStd::ranges::begin(rangeLike)); + } + + TEST_F(RangesTestFixture, RangesEnd_Compiles_WithExtentArray) + { + using ArrayExtentType = int[5]; + + ArrayExtentType extentArray{}; + EXPECT_EQ(extentArray + 5, AZStd::ranges::end(extentArray)); + } + + TEST_F(RangesTestFixture, RangesEnd_DoesNotCompile_WithNoExtentArray) + { + using ArrayNoExtentType = int[]; + static_assert(!AZStd::invocable); + } + + TEST_F(RangesTestFixture, RangesEnd_Compiles_WithMemberOverload) + { + AZStd::string_view strView; + EXPECT_EQ(strView.end(), AZStd::ranges::end(strView)); + } + TEST_F(RangesTestFixture, RangesEnd_Compiles_WithADL) + { + RangeLikeCustomizationPoint rangeLike; + EXPECT_EQ(&rangeLike, AZStd::ranges::end(rangeLike)); + } + + TEST_F(RangesTestFixture, RangesCBegin_Compiles_WithExtentArray) + { + using ArrayExtentType = int[5]; + + ArrayExtentType extentArray{}; + EXPECT_EQ(extentArray + 0, AZStd::ranges::cbegin(extentArray)); + } + + TEST_F(RangesTestFixture, RangesCBegin_Compiles_WithMemberOverload) + { + AZStd::string_view strView; + EXPECT_EQ(strView.cbegin(), AZStd::ranges::cbegin(strView)); + } + + TEST_F(RangesTestFixture, RangesCBegin_Compiles_WithADL) + { + RangeLikeCustomizationPoint rangeLike; + EXPECT_EQ(&rangeLike, AZStd::ranges::cbegin(rangeLike)); + } + + TEST_F(RangesTestFixture, RangesCEnd_Compiles_WithExtentArray) + { + using ArrayExtentType = int[5]; + + ArrayExtentType extentArray{}; + EXPECT_EQ(extentArray + 5, AZStd::ranges::cend(extentArray)); + } + + TEST_F(RangesTestFixture, RangesCEnd_Compiles_WithMemberOverload) + { + AZStd::string_view strView; + EXPECT_EQ(strView.cend(), AZStd::ranges::cend(strView)); + } + + TEST_F(RangesTestFixture, RangesCEnd_Compiles_WithADL) + { + RangeLikeCustomizationPoint rangeLike; + EXPECT_EQ(&rangeLike, AZStd::ranges::cend(rangeLike)); + } + + TEST_F(RangesTestFixture, RangesRBegin_Compiles_WithExtentArray) + { + using ArrayExtentType = int[5]; + + ArrayExtentType extentArray{}; + EXPECT_EQ(extentArray + 5, AZStd::ranges::rbegin(extentArray).base()); + } + + TEST_F(RangesTestFixture, RangesRBegin_Compiles_WithMemberOverload) + { + AZStd::string_view strView; + EXPECT_EQ(strView.rbegin(), AZStd::ranges::rbegin(strView)); + } + TEST_F(RangesTestFixture, RangesRBegin_Compiles_WithADL) + { + RangeLikeCustomizationPoint rangeLike; + EXPECT_EQ(&rangeLike, AZStd::ranges::rbegin(rangeLike)); + } + + TEST_F(RangesTestFixture, RangesREnd_Compiles_WithExtentArray) + { + using ArrayExtentType = int[5]; + + ArrayExtentType extentArray{}; + EXPECT_EQ(extentArray, AZStd::ranges::rend(extentArray).base()); + } + + TEST_F(RangesTestFixture, RangesREnd_Compiles_WithMemberOverload) + { + AZStd::string_view strView; + EXPECT_EQ(strView.rend(), AZStd::ranges::rend(strView)); + } + TEST_F(RangesTestFixture, RangesREnd_Compiles_WithADL) + { + RangeLikeCustomizationPoint rangeLike; + EXPECT_EQ(&rangeLike, AZStd::ranges::rend(rangeLike)); + } + + TEST_F(RangesTestFixture, RangesCRBegin_Compiles_WithExtentArray) + { + using ArrayExtentType = int[5]; + + ArrayExtentType extentArray{}; + EXPECT_EQ(extentArray + 5, AZStd::ranges::crbegin(extentArray).base()); + } + + TEST_F(RangesTestFixture, RangesCRBegin_Compiles_WithMemberOverload) + { + AZStd::string_view strView; + EXPECT_EQ(strView.crbegin(), AZStd::ranges::crbegin(strView)); + } + TEST_F(RangesTestFixture, RangesCRBegin_Compiles_WithADL) + { + RangeLikeCustomizationPoint rangeLike; + EXPECT_EQ(&rangeLike, AZStd::ranges::crbegin(rangeLike)); + } + + TEST_F(RangesTestFixture, RangesCREnd_Compiles_WithExtentArray) + { + using ArrayExtentType = int[5]; + + ArrayExtentType extentArray{}; + EXPECT_EQ(extentArray + 0, AZStd::ranges::crend(extentArray).base()); + } + + TEST_F(RangesTestFixture, RangesCREnd_Compiles_WithMemberOverload) + { + AZStd::string_view strView; + EXPECT_EQ(strView.crend(), AZStd::ranges::crend(strView)); + } + + TEST_F(RangesTestFixture, RangesCREnd_Compiles_WithADL) + { + RangeLikeCustomizationPoint rangeLike; + EXPECT_EQ(&rangeLike, AZStd::ranges::crend(rangeLike)); + } + + // range access - size + TEST_F(RangesTestFixture, RangesSize_Compiles_WithExtentArray) + { + using ArrayExtentType = int[5]; + + constexpr ArrayExtentType extentArray{}; + static_assert(5 == AZStd::ranges::size(extentArray)); + } + + TEST_F(RangesTestFixture, RangesSize_DoesNotCompile_WithNoExtentArray) + { + using ArrayNoExtentType = int[]; + static_assert(!AZStd::invocable); + } + + TEST_F(RangesTestFixture, RangesSize_Compiles_WithMemberOverload) + { + AZStd::string_view strView; + EXPECT_EQ(strView.size(), AZStd::ranges::size(strView)); + } + + + TEST_F(RangesTestFixture, RangesSize_Compiles_WithADL) + { + RangeLikeCustomizationPoint rangeLike; + + EXPECT_EQ(0, AZStd::ranges::size(rangeLike)); + } + + TEST_F(RangesTestFixture, RangesSSize_Compiles_WithExtentArray) + { + using ArrayExtentType = int[5]; + + constexpr ArrayExtentType extentArray{}; + static_assert(AZStd::signed_integral); + static_assert(5 == AZStd::ranges::ssize(extentArray)); + } + + TEST_F(RangesTestFixture, RangesSSize_DoesNotCompile_WithNoExtentArray) + { + using ArrayNoExtentType = int[]; + static_assert(!AZStd::invocable); + } + + TEST_F(RangesTestFixture, RangesSSize_Compiles_WithMemberOverload) + { + AZStd::string_view strView; + static_assert(AZStd::signed_integral); + EXPECT_EQ(strView.size(), AZStd::ranges::ssize(strView)); + } + + TEST_F(RangesTestFixture, RangesSSize_Compiles_WithADL) + { + RangeLikeCustomizationPoint rangeLike; + static_assert(AZStd::signed_integral); + EXPECT_EQ(0, AZStd::ranges::ssize(rangeLike)); + } + + // range access - empty + TEST_F(RangesTestFixture, RangesEmpty_Compiles_WithExtentArray) + { + using ArrayExtentType = int[5]; + + constexpr ArrayExtentType extentArray{}; + static_assert(!AZStd::ranges::empty(extentArray)); + } + + TEST_F(RangesTestFixture, RangesEmpty_DoesNotCompile_WithNoExtentArray) + { + using ArrayNoExtentType = int[]; + static_assert(!AZStd::invocable); + } + + TEST_F(RangesTestFixture, RangesEmpty_Compiles_WithMemberOverload) + { + constexpr AZStd::string_view strView; + static_assert(AZStd::ranges::empty(strView)); + } + + TEST_F(RangesTestFixture, RangesEmpty_Compiles_WithADL) + { + constexpr RangeLikeCustomizationPoint rangeLike; + + static_assert(AZStd::ranges::empty(rangeLike)); + } + + // range access - data + TEST_F(RangesTestFixture, RangesData_Compiles_WithExtentArray) + { + using ArrayExtentType = int[5]; + + constexpr ArrayExtentType extentArray{}; + EXPECT_EQ(extentArray, AZStd::ranges::data(extentArray)); + } + + TEST_F(RangesTestFixture, RangesData_DoesNotCompile_WithNoExtentArray) + { + using ArrayNoExtentType = int[]; + static_assert(!AZStd::invocable); + } + + TEST_F(RangesTestFixture, RangesData_Compiles_WithMemberOverload) + { + constexpr AZStd::string_view strView; + EXPECT_EQ(strView.data(), AZStd::ranges::data(strView)); + } + + TEST_F(RangesTestFixture, RangesData_Compiles_WithADL) + { + RangeLikeCustomizationPoint rangeLike; + + EXPECT_EQ(&rangeLike, AZStd::ranges::data(rangeLike)); + } + + // range access - cdata + TEST_F(RangesTestFixture, RangesCData_Compiles_WithExtentArray) + { + using ArrayExtentType = int[5]; + + constexpr ArrayExtentType extentArray{}; + EXPECT_EQ(extentArray, AZStd::ranges::cdata(extentArray)); + } + + TEST_F(RangesTestFixture, RangesCData_DoesNotCompile_WithNoExtentArray) + { + using ArrayNoExtentType = int[]; + static_assert(!AZStd::invocable); + } + + TEST_F(RangesTestFixture, RangesCData_Compiles_WithMemberOverload) + { + constexpr AZStd::string_view strView; + EXPECT_EQ(strView.data(), AZStd::ranges::cdata(strView)); + } + + TEST_F(RangesTestFixture, RangesCData_Compiles_WithADL) + { + RangeLikeCustomizationPoint rangeLike; + + EXPECT_EQ(&rangeLike, AZStd::ranges::cdata(rangeLike)); + } + + // Ranges TypeTraits Test + TEST_F(RangesTestFixture, RangesTypeTraits_Compiles) + { + // string_view + static_assert(AZStd::same_as, const char*>); + static_assert(AZStd::same_as, const char*>); + static_assert(AZStd::same_as, ptrdiff_t>); + static_assert(AZStd::same_as, size_t>); + static_assert(AZStd::same_as, char>); + static_assert(AZStd::same_as, const char&>); + static_assert(AZStd::same_as, const char&&>); + + // string + static_assert(AZStd::same_as, char*>); + static_assert(AZStd::same_as, char*>); + static_assert(AZStd::same_as, ptrdiff_t>); + static_assert(AZStd::same_as, size_t>); + static_assert(AZStd::same_as, char>); + static_assert(AZStd::same_as, char&>); + static_assert(AZStd::same_as, char&&>); + + // int array type + using ArrayExtentType = int[5]; + static_assert(AZStd::same_as, int*>); + static_assert(AZStd::same_as, int*>); + static_assert(AZStd::same_as, ptrdiff_t>); + static_assert(AZStd::same_as, size_t>); + static_assert(AZStd::same_as, int>); + static_assert(AZStd::same_as, int&>); + static_assert(AZStd::same_as, int&&>); + + // RangeLikeCustomizationPoint type which specializes several range functions + static_assert(AZStd::same_as, RangeLikeCustomizationPoint*>); + static_assert(AZStd::same_as, RangeLikeCustomizationPoint*>); + static_assert(AZStd::same_as, ptrdiff_t>); + static_assert(AZStd::same_as, size_t>); + static_assert(AZStd::same_as, RangeLikeCustomizationPoint>); + static_assert(AZStd::same_as, RangeLikeCustomizationPoint&>); + static_assert(AZStd::same_as, RangeLikeCustomizationPoint&&>); + } + + // Ranges Concepts Test + TEST_F(RangesTestFixture, RangesConcepts_Compiles) + { + using ArrayExtentType = int[5]; + // concept - range + static_assert(AZStd::ranges::range); + static_assert(AZStd::ranges::range); + static_assert(AZStd::ranges::range); + static_assert(!AZStd::ranges::range); + + // concept - sized_range + static_assert(AZStd::ranges::sized_range); + static_assert(AZStd::ranges::sized_range); + // Path classes do not have a size() function so they are not a sized_range + static_assert(!AZStd::ranges::sized_range); + + // concept - borrowed_range + static_assert(AZStd::ranges::borrowed_range); + static_assert(AZStd::ranges::borrowed_range); + static_assert(!AZStd::ranges::borrowed_range); + + // concept - output_range + static_assert(AZStd::ranges::output_range); + static_assert(!AZStd::ranges::output_range); + + // concept - input_range + static_assert(AZStd::ranges::input_range>); + static_assert(AZStd::ranges::input_range); + static_assert(AZStd::ranges::input_range); + + // concept - forward_range + static_assert(AZStd::ranges::forward_range>); + static_assert(AZStd::ranges::forward_range); + static_assert(AZStd::ranges::forward_range); + + // concept - bidirectional_range + static_assert(AZStd::ranges::bidirectional_range>); + static_assert(AZStd::ranges::bidirectional_range); + static_assert(AZStd::ranges::bidirectional_range); + + // concept - random_access_range + static_assert(!AZStd::ranges::random_access_range>); + static_assert(AZStd::ranges::random_access_range>); + static_assert(AZStd::ranges::random_access_range); + static_assert(AZStd::ranges::random_access_range); + + // concept - contiguous_range + static_assert(!AZStd::ranges::contiguous_range>); + static_assert(AZStd::ranges::contiguous_range); + static_assert(AZStd::ranges::contiguous_range); + + // concept - common_range + static_assert(AZStd::ranges::common_range); + static_assert(AZStd::ranges::common_range>); + static_assert(AZStd::ranges::common_range>); + static_assert(AZStd::ranges::common_range); + static_assert(AZStd::ranges::common_range); + + // concept - view + static_assert(AZStd::ranges::view); + static_assert(!AZStd::ranges::view); + + // concept - viewable_range + static_assert(AZStd::ranges::viewable_range); + static_assert(AZStd::ranges::viewable_range); + static_assert(!AZStd::ranges::viewable_range); + } + + // Ranges iterator operations + TEST_F(RangesTestFixture, RangesAdvance_PositiveDifference_Succeeds) + { + AZStd::string_view testString{ "Hello World" }; + auto strIter = testString.begin(); + + // difference overload + AZStd::ranges::advance(strIter, 5); + ASSERT_NE(testString.end(), strIter); + EXPECT_EQ(' ', *strIter); + + // bound overload + AZStd::ranges::advance(strIter, testString.end()); + EXPECT_EQ(testString.end(), strIter); + + // difference + bound overload + strIter = testString.begin(); + ptrdiff_t charactersToTraverse = 20; + EXPECT_EQ(charactersToTraverse - testString.size(), AZStd::ranges::advance(strIter, charactersToTraverse, testString.end())); + EXPECT_EQ(testString.end(), strIter); + + strIter = testString.begin(); + charactersToTraverse = 5; + EXPECT_EQ(0, AZStd::ranges::advance(strIter, charactersToTraverse, testString.end())); + ASSERT_NE(testString.end(), strIter); + EXPECT_EQ(' ', *strIter); + } + + TEST_F(RangesTestFixture, RangesAdvance_NegativeDifference_Succeeds) + { + AZStd::string_view testString{ "Hello World" }; + auto strIter = testString.end(); + + // difference overload + AZStd::ranges::advance(strIter, -5); + ASSERT_NE(testString.end(), strIter); + EXPECT_EQ('W', *strIter); + + // difference + bound overload + strIter = testString.end(); + ptrdiff_t charactersToTraverse = -20; + EXPECT_EQ(charactersToTraverse + testString.size(), AZStd::ranges::advance(strIter, charactersToTraverse, testString.begin())); + EXPECT_EQ(testString.begin(), strIter); + + strIter = testString.end(); + charactersToTraverse = -5; + EXPECT_EQ(0, AZStd::ranges::advance(strIter, charactersToTraverse, testString.begin())); + ASSERT_NE(testString.end(), strIter); + ASSERT_NE(testString.begin(), strIter); + EXPECT_EQ('W', *strIter); + } + + TEST_F(RangesTestFixture, RangesDistance_Succeeds) + { + AZStd::string_view testString{ "Hello World" }; + EXPECT_EQ(testString.size(), AZStd::ranges::distance(testString)); + EXPECT_EQ(testString.size(), AZStd::ranges::distance(testString.begin(), testString.end())); + + AZStd::list testList{ 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' }; + EXPECT_EQ(testList.size(), AZStd::ranges::distance(testList)); + EXPECT_EQ(testList.size(), AZStd::ranges::distance(testList.begin(), testList.end())); + } + + TEST_F(RangesTestFixture, RangesNext_Succeeds) + { + AZStd::string_view testString{ "Hello World" }; + auto strIter = testString.begin(); + auto boundIter = testString.begin() + 5; + // single increment + EXPECT_EQ(testString.begin() + 1, AZStd::ranges::next(strIter)); + // increment by value + strIter = testString.begin(); + EXPECT_EQ(testString.begin() + 5, AZStd::ranges::next(strIter, 5)); + // increment until bound + strIter = testString.begin(); + EXPECT_EQ(testString.begin() + 5, AZStd::ranges::next(strIter, boundIter)); + // increment by value up until bound + strIter = testString.begin(); + EXPECT_EQ(testString.begin() + 5, AZStd::ranges::next(strIter, 10, boundIter)); + strIter = testString.begin(); + EXPECT_EQ(testString.begin() + 4, AZStd::ranges::next(strIter, 4, boundIter)); + } + + TEST_F(RangesTestFixture, RangesPrev_Succeeds) + { + AZStd::string_view testString{ "Hello World" }; + auto strIter = testString.end(); + auto boundIter = testString.end() - 5; + // single decrement + EXPECT_EQ(testString.end() - 1, AZStd::ranges::prev(strIter)); + // decrement by value + strIter = testString.end(); + EXPECT_EQ(testString.end() - 5, AZStd::ranges::prev(strIter, 5)); + // decrement by value up until bound + strIter = testString.end(); + EXPECT_EQ(testString.end() - 5, AZStd::ranges::prev(strIter, 10, boundIter)); + strIter = testString.end(); + EXPECT_EQ(testString.end() - 4, AZStd::ranges::prev(strIter, 4, boundIter)); + } +} diff --git a/Code/Framework/AzCore/Tests/AZStd/SpanTests.cpp b/Code/Framework/AzCore/Tests/AZStd/SpanTests.cpp new file mode 100644 index 0000000000..f9861ae1f2 --- /dev/null +++ b/Code/Framework/AzCore/Tests/AZStd/SpanTests.cpp @@ -0,0 +1,249 @@ +/* + * 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 +#include + +namespace UnitTest +{ + class SpanTestFixture + : public ScopedAllocatorSetupFixture + {}; + + // range access + TEST_F(SpanTestFixture, IsConstructibleWithContiguousRangeLikeContainers) + { + constexpr AZStd::string_view testStringView{ "Foo" }; + AZStd::string testString{ "Foo" }; + AZStd::vector testVector{ 'F', 'o', 'o' }; + AZStd::fixed_vector testFixedVector{ 'F', 'o', 'o' }; + AZStd::array testStdArray{ 'F', 'o', 'o' }; + const char testCArray[]{ 'F', 'o', 'o' }; + + constexpr AZStd::span stringViewSpan(testStringView); + static_assert(stringViewSpan.data() == testStringView.data()); + + AZStd::span testStringSpan(testString); + EXPECT_EQ(testStringSpan.data(), testString.data()); + + AZStd::span testVectorSpan(testVector); + EXPECT_EQ(testVectorSpan.data(), testVector.data()); + + AZStd::span testFixedVectorSpan(testFixedVector); + EXPECT_EQ(testFixedVectorSpan.data(), testFixedVector.data()); + + AZStd::span testStdArraySpan(testStdArray); + EXPECT_EQ(testStdArraySpan.data(), testStdArray.data()); + + AZStd::span testCArraySpan(testCArray); + EXPECT_EQ(AZStd::data(testCArray), testCArraySpan.data()); + } + + TEST_F(SpanTestFixture, IsConstructibleWithContiguousIterators) + { + constexpr AZStd::string_view testStringView{ "Foo" }; + AZStd::string testString{ "Foo" }; + AZStd::vector testVector{ 'F', 'o', 'o' }; + AZStd::fixed_vector testFixedVector{ 'F', 'o', 'o' }; + AZStd::array testStdArray{ 'F', 'o', 'o' }; + const char testCArray[]{ 'F', 'o', 'o' }; + + constexpr AZStd::span stringViewSpan(testStringView.begin(), testStringView.end()); + static_assert(stringViewSpan.data() == testStringView.data()); + + AZStd::span testStringSpan(testString.begin(), testString.end()); + EXPECT_EQ(testStringSpan.data(), testString.data()); + + AZStd::span testVectorSpan(testVector.begin(), testVector.end()); + EXPECT_EQ(testVectorSpan.data(), testVector.data()); + + AZStd::span testFixedVectorSpan(testFixedVector.begin(), testFixedVector.end()); + EXPECT_EQ(testFixedVectorSpan.data(), testFixedVector.data()); + + AZStd::span testStdArraySpan(testStdArray.begin(), testStdArray.end()); + EXPECT_EQ(testStdArraySpan.data(), testStdArray.data()); + + AZStd::span testCArraySpan(AZStd::begin(testCArray), AZStd::end(testCArray)); + EXPECT_EQ(AZStd::data(testCArray), testCArraySpan.data()); + } + + TEST_F(SpanTestFixture, ObserverMethods_ReturnsCorrectValues) + { + AZStd::vector intVector{ 4, 5, 6, 1, 7 }; + + AZStd::span intSpan(intVector); + + EXPECT_FALSE(intSpan.empty()); + EXPECT_EQ(intVector.size(), intSpan.size()); + EXPECT_EQ(intSpan.size() * sizeof(int), intSpan.size_bytes()); + + intSpan = {}; + + EXPECT_TRUE(intSpan.empty()); + EXPECT_EQ(0, intSpan.size()); + EXPECT_EQ(0, intSpan.size_bytes()); + } + + TEST_F(SpanTestFixture, ElementAccessorMethods_Succeeds) + { + AZStd::vector intVector{ 4, 5, 6, 1, 7 }; + + AZStd::span intSpan(intVector); + + EXPECT_EQ(intVector.data(), intSpan.data()); + EXPECT_EQ(4, intSpan.front()); + EXPECT_EQ(7, intSpan.back()); + EXPECT_EQ(6, intSpan[2]); + + // Create subspan from elements 1 .. end - 1 + intSpan = intSpan.subspan(1, intSpan.size() - 2); + EXPECT_NE(intVector.data(), intSpan.data()); + EXPECT_EQ(5, intSpan.front()); + EXPECT_EQ(1, intSpan.back()); + EXPECT_EQ(6, intSpan[1]); + } + + TEST_F(SpanTestFixture, Supspan_Returns_Subview_Succeeds) + { + AZStd::vector intVector{ 4, 5, 6, 1, 7 }; + + AZStd::span intSpan(intVector); + + // dynamic_extent subspan with count + auto dynamicIntSubSpan = intSpan.subspan(1, 2); + ASSERT_EQ(2, dynamicIntSubSpan.size()); + EXPECT_EQ(5, dynamicIntSubSpan[0]); + EXPECT_EQ(6, dynamicIntSubSpan[1]); + + // dynamic_extent subspan without count + dynamicIntSubSpan = intSpan.subspan(1); + ASSERT_EQ(4, dynamicIntSubSpan.size()); + EXPECT_EQ(5, dynamicIntSubSpan[0]); + EXPECT_EQ(6, dynamicIntSubSpan[1]); + EXPECT_EQ(1, dynamicIntSubSpan[2]); + EXPECT_EQ(7, dynamicIntSubSpan[3]); + + // template subspan with count + auto templateIntSubSpan1 = intSpan.subspan<1, 3>(); + static_assert(decltype(templateIntSubSpan1)::extent == 3); + ASSERT_EQ(3, templateIntSubSpan1.size()); + EXPECT_EQ(5, templateIntSubSpan1[0]); + EXPECT_EQ(6, templateIntSubSpan1[1]); + EXPECT_EQ(1, templateIntSubSpan1[2]); + + // template subspan without count + auto templateIntSubSpan2 = intSpan.subspan<1>(); + static_assert(decltype(templateIntSubSpan2)::extent == AZStd::dynamic_extent); + ASSERT_EQ(4, templateIntSubSpan2.size()); + EXPECT_EQ(5, templateIntSubSpan2[0]); + EXPECT_EQ(6, templateIntSubSpan2[1]); + EXPECT_EQ(1, templateIntSubSpan2[2]); + EXPECT_EQ(7, templateIntSubSpan2[3]); + + // get subspan of fixed extent span without count + auto subSpanOfSubSpan = templateIntSubSpan1.subspan<1>(); + static_assert(decltype(subSpanOfSubSpan)::extent == 2); + ASSERT_EQ(2, subSpanOfSubSpan.size()); + EXPECT_EQ(6, subSpanOfSubSpan[0]); + EXPECT_EQ(1, subSpanOfSubSpan[1]); + } + + TEST_F(SpanTestFixture, FirstMethod_Returns_FirstCountElementsOfSpan) + { + constexpr size_t vectorElementCount = 5; + AZStd::vector intVector{ 4, 5, 6, 1, 7 }; + + AZStd::span intSpan(intVector); + + { + // No templated first function + auto prefixSpan = intSpan.first(3); + ASSERT_EQ(3, prefixSpan.size()); + EXPECT_EQ(4, prefixSpan[0]); + EXPECT_EQ(5, prefixSpan[1]); + EXPECT_EQ(6, prefixSpan[2]); + + auto prefixSpanRedux = prefixSpan.first(1); + ASSERT_EQ(1, prefixSpanRedux.size()); + EXPECT_EQ(4, prefixSpanRedux[0]); + + // Test failure of preconditions by requesting more + // elements thant stored in the span + AZ_TEST_START_TRACE_SUPPRESSION; + intSpan.first(intSpan.size() + 1); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + } + + { + // templated first function + auto prefixSpan = intSpan.first<3>(); + static_assert(decltype(prefixSpan)::extent == 3); + ASSERT_EQ(3, prefixSpan.size()); + EXPECT_EQ(4, prefixSpan[0]); + EXPECT_EQ(5, prefixSpan[1]); + EXPECT_EQ(6, prefixSpan[2]); + + auto prefixSpanRedux = prefixSpan.first<1>(); + ASSERT_EQ(1, prefixSpanRedux.size()); + EXPECT_EQ(4, prefixSpanRedux[0]); + + // Test failure of preconditions by requesting more + // elements thant stored in the span + AZ_TEST_START_TRACE_SUPPRESSION; + intSpan.first(); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + } + } + + TEST_F(SpanTestFixture, LastCountElementsOfSpan) + { + constexpr size_t vectorElementCount = 5; + AZStd::vector intVector{ 4, 5, 6, 1, 7 }; + + AZStd::span intSpan(intVector); + + { + // No templated last function + auto suffixSpan = intSpan.last(3); + ASSERT_EQ(3, suffixSpan.size()); + EXPECT_EQ(6, suffixSpan[0]); + EXPECT_EQ(1, suffixSpan[1]); + EXPECT_EQ(7, suffixSpan[2]); + + auto suffixSpanRedux = suffixSpan.last(1); + ASSERT_EQ(1, suffixSpanRedux.size()); + EXPECT_EQ(7, suffixSpanRedux[0]); + + // Test failure of preconditions by requesting more + // elements thant stored in the span + AZ_TEST_START_TRACE_SUPPRESSION; + intSpan.last(intSpan.size() + 1); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + } + + { + // templated last function + auto suffixSpan = intSpan.last<3>(); + static_assert(decltype(suffixSpan)::extent == 3); + ASSERT_EQ(3, suffixSpan.size()); + EXPECT_EQ(6, suffixSpan[0]); + EXPECT_EQ(1, suffixSpan[1]); + EXPECT_EQ(7, suffixSpan[2]); + + auto suffixSpanRedux = suffixSpan.last<1>(); + ASSERT_EQ(1, suffixSpanRedux.size()); + EXPECT_EQ(7, suffixSpanRedux[0]); + + // Test failure of preconditions by requesting more + // elements thant stored in the span + AZ_TEST_START_TRACE_SUPPRESSION; + intSpan.last(); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + } + } +} diff --git a/Code/Framework/AzCore/Tests/AZStd/TypeTraits.cpp b/Code/Framework/AzCore/Tests/AZStd/TypeTraits.cpp index aeba048d69..d0f34d14a0 100644 --- a/Code/Framework/AzCore/Tests/AZStd/TypeTraits.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/TypeTraits.cpp @@ -45,197 +45,197 @@ namespace UnitTest // Primary type categories: // alignment_of and align_to - AZ_TEST_STATIC_ASSERT(alignment_of::value == 4); - AZ_TEST_STATIC_ASSERT(alignment_of::value == 1); + static_assert(alignment_of::value == 4); + static_assert(alignment_of::value == 1); - AZ_TEST_STATIC_ASSERT(alignment_of::value == 16); - aligned_storage::type alignedArray; + static_assert(alignment_of::value == 16); + aligned_storage::type alignedArray; AZ_TEST_ASSERT((((AZStd::size_t)&alignedArray) & 15) == 0); - AZ_TEST_STATIC_ASSERT((alignment_of< aligned_storage::type >::value) == 8); - AZ_TEST_STATIC_ASSERT(sizeof(aligned_storage::type) == 16); + static_assert((alignment_of< aligned_storage::type >::value) == 8); + static_assert(sizeof(aligned_storage::type) == 16); // is_void - AZ_TEST_STATIC_ASSERT(is_void::value == false); - AZ_TEST_STATIC_ASSERT(is_void::value == true); - AZ_TEST_STATIC_ASSERT(is_void::value == true); - AZ_TEST_STATIC_ASSERT(is_void::value == true); - AZ_TEST_STATIC_ASSERT(is_void::value == true); + static_assert(is_void::value == false); + static_assert(is_void::value == true); + static_assert(is_void::value == true); + static_assert(is_void::value == true); + static_assert(is_void::value == true); // is_integral - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - //AZ_TEST_STATIC_ASSERT(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + //static_assert(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); + static_assert(is_integral::value == true); - AZ_TEST_STATIC_ASSERT(is_integral::value == false); - AZ_TEST_STATIC_ASSERT(is_integral::value == false); - AZ_TEST_STATIC_ASSERT(is_integral::value == false); + static_assert(is_integral::value == false); + static_assert(is_integral::value == false); + static_assert(is_integral::value == false); // is_floating_point - AZ_TEST_STATIC_ASSERT(is_floating_point::value == false); - AZ_TEST_STATIC_ASSERT(is_floating_point::value == true); - AZ_TEST_STATIC_ASSERT(is_floating_point::value == true); - AZ_TEST_STATIC_ASSERT(is_floating_point::value == true); - AZ_TEST_STATIC_ASSERT(is_floating_point::value == true); + static_assert(is_floating_point::value == false); + static_assert(is_floating_point::value == true); + static_assert(is_floating_point::value == true); + static_assert(is_floating_point::value == true); + static_assert(is_floating_point::value == true); // is_array - AZ_TEST_STATIC_ASSERT(is_array::value == false); - AZ_TEST_STATIC_ASSERT(is_array::value == true); - AZ_TEST_STATIC_ASSERT(is_array::value == true); - AZ_TEST_STATIC_ASSERT(is_array::value == true); - AZ_TEST_STATIC_ASSERT(is_array::value == true); + static_assert(is_array::value == false); + static_assert(is_array::value == true); + static_assert(is_array::value == true); + static_assert(is_array::value == true); + static_assert(is_array::value == true); - AZ_TEST_STATIC_ASSERT(is_array::value == true); - AZ_TEST_STATIC_ASSERT(is_array::value == true); - AZ_TEST_STATIC_ASSERT(is_array::value == true); - AZ_TEST_STATIC_ASSERT(is_array::value == true); + static_assert(is_array::value == true); + static_assert(is_array::value == true); + static_assert(is_array::value == true); + static_assert(is_array::value == true); // is_pointer - AZ_TEST_STATIC_ASSERT(is_pointer::value == false); - AZ_TEST_STATIC_ASSERT(is_pointer::value == true); - AZ_TEST_STATIC_ASSERT(is_pointer::value == true); - AZ_TEST_STATIC_ASSERT(is_pointer::value == true); - AZ_TEST_STATIC_ASSERT(is_pointer::value == true); + static_assert(is_pointer::value == false); + static_assert(is_pointer::value == true); + static_assert(is_pointer::value == true); + static_assert(is_pointer::value == true); + static_assert(is_pointer::value == true); // is_reference - AZ_TEST_STATIC_ASSERT(is_reference::value == false); - AZ_TEST_STATIC_ASSERT(is_reference::value == true); - AZ_TEST_STATIC_ASSERT(is_reference::value == true); - AZ_TEST_STATIC_ASSERT(is_reference::value == true); - AZ_TEST_STATIC_ASSERT(is_reference::value == true); + static_assert(is_reference::value == false); + static_assert(is_reference::value == true); + static_assert(is_reference::value == true); + static_assert(is_reference::value == true); + static_assert(is_reference::value == true); // is_member_object_pointer - AZ_TEST_STATIC_ASSERT(is_member_object_pointer::value == false); - AZ_TEST_STATIC_ASSERT(is_member_object_pointer::value == false); - AZ_TEST_STATIC_ASSERT(is_member_object_pointer::value == true); + static_assert(is_member_object_pointer::value == false); + static_assert(is_member_object_pointer::value == false); + static_assert(is_member_object_pointer::value == true); // is_member_function_pointer - AZ_TEST_STATIC_ASSERT(is_member_function_pointer::value == false); - AZ_TEST_STATIC_ASSERT(is_member_function_pointer::value == true); - AZ_TEST_STATIC_ASSERT(is_member_function_pointer::value == false); - AZ_TEST_STATIC_ASSERT((is_member_function_pointer::value)); - AZ_TEST_STATIC_ASSERT((is_member_function_pointer::value)); - AZ_TEST_STATIC_ASSERT((is_member_function_pointer::value)); - AZ_TEST_STATIC_ASSERT((is_member_function_pointer::value)); - AZ_TEST_STATIC_ASSERT((is_member_function_pointer::value)); - AZ_TEST_STATIC_ASSERT((is_member_function_pointer::value)); - AZ_TEST_STATIC_ASSERT((is_member_function_pointer::value)); - AZ_TEST_STATIC_ASSERT((is_member_function_pointer::value)); - AZ_TEST_STATIC_ASSERT((is_member_function_pointer::value)); + static_assert(is_member_function_pointer::value == false); + static_assert(is_member_function_pointer::value == true); + static_assert(is_member_function_pointer::value == false); + static_assert((is_member_function_pointer::value)); + static_assert((is_member_function_pointer::value)); + static_assert((is_member_function_pointer::value)); + static_assert((is_member_function_pointer::value)); + static_assert((is_member_function_pointer::value)); + static_assert((is_member_function_pointer::value)); + static_assert((is_member_function_pointer::value)); + static_assert((is_member_function_pointer::value)); + static_assert((is_member_function_pointer::value)); // is_enum - AZ_TEST_STATIC_ASSERT(is_enum::value == false); - AZ_TEST_STATIC_ASSERT(is_enum::value == false); - AZ_TEST_STATIC_ASSERT(is_enum::value == true); + static_assert(is_enum::value == false); + static_assert(is_enum::value == false); + static_assert(is_enum::value == true); // is_union - AZ_TEST_STATIC_ASSERT(is_union::value == false); - AZ_TEST_STATIC_ASSERT(is_union::value == false); - AZ_TEST_STATIC_ASSERT(is_union::value == true); + static_assert(is_union::value == false); + static_assert(is_union::value == false); + static_assert(is_union::value == true); // is_class - AZ_TEST_STATIC_ASSERT(is_class::value == false); - AZ_TEST_STATIC_ASSERT(is_class::value == true); - AZ_TEST_STATIC_ASSERT(is_class::value == true); + static_assert(is_class::value == false); + static_assert(is_class::value == true); + static_assert(is_class::value == true); // is_function - AZ_TEST_STATIC_ASSERT(is_function::value == false); - AZ_TEST_STATIC_ASSERT(is_function::value == false); - AZ_TEST_STATIC_ASSERT(is_function::value == true); + static_assert(is_function::value == false); + static_assert(is_function::value == false); + static_assert(is_function::value == true); ////////////////////////////////////////////////////////////////////////// // composite type categories: // is_arithmetic - AZ_TEST_STATIC_ASSERT(is_arithmetic::value == false); - AZ_TEST_STATIC_ASSERT(is_arithmetic::value == true); - AZ_TEST_STATIC_ASSERT(is_arithmetic::value == true); + static_assert(is_arithmetic::value == false); + static_assert(is_arithmetic::value == true); + static_assert(is_arithmetic::value == true); // is_fundamental - AZ_TEST_STATIC_ASSERT(is_fundamental::value == false); - AZ_TEST_STATIC_ASSERT(is_fundamental::value == true); - AZ_TEST_STATIC_ASSERT(is_fundamental::value == true); - AZ_TEST_STATIC_ASSERT(is_fundamental::value == true); + static_assert(is_fundamental::value == false); + static_assert(is_fundamental::value == true); + static_assert(is_fundamental::value == true); + static_assert(is_fundamental::value == true); // is_object - AZ_TEST_STATIC_ASSERT(is_object::value == true); - AZ_TEST_STATIC_ASSERT(is_object::value == false); - AZ_TEST_STATIC_ASSERT(is_object::value == false); - AZ_TEST_STATIC_ASSERT(is_object::value == false); + static_assert(is_object::value == true); + static_assert(is_object::value == false); + static_assert(is_object::value == false); + static_assert(is_object::value == false); // is_scalar - AZ_TEST_STATIC_ASSERT(is_scalar::value == false); - AZ_TEST_STATIC_ASSERT(is_scalar::value == true); - AZ_TEST_STATIC_ASSERT(is_scalar::value == true); - AZ_TEST_STATIC_ASSERT(is_scalar::value == true); + static_assert(is_scalar::value == false); + static_assert(is_scalar::value == true); + static_assert(is_scalar::value == true); + static_assert(is_scalar::value == true); // is_compound - AZ_TEST_STATIC_ASSERT(is_compound::value == false); - AZ_TEST_STATIC_ASSERT(is_compound::value == true); - AZ_TEST_STATIC_ASSERT(is_compound::value == true); - AZ_TEST_STATIC_ASSERT(is_compound::value == true); - AZ_TEST_STATIC_ASSERT(is_compound::value == true); - AZ_TEST_STATIC_ASSERT(is_compound::value == true); + static_assert(is_compound::value == false); + static_assert(is_compound::value == true); + static_assert(is_compound::value == true); + static_assert(is_compound::value == true); + static_assert(is_compound::value == true); + static_assert(is_compound::value == true); // is_member_pointer - AZ_TEST_STATIC_ASSERT(is_member_pointer::value == false); - AZ_TEST_STATIC_ASSERT(is_member_pointer::value == true); - AZ_TEST_STATIC_ASSERT(is_member_pointer::value == true); + static_assert(is_member_pointer::value == false); + static_assert(is_member_pointer::value == true); + static_assert(is_member_pointer::value == true); ////////////////////////////////////////////////////////////////////////// // type properties: // is_const - AZ_TEST_STATIC_ASSERT(is_const::value == false); - AZ_TEST_STATIC_ASSERT(is_const::value == false); - AZ_TEST_STATIC_ASSERT(is_const::value == true); - AZ_TEST_STATIC_ASSERT(is_const::value == true); + static_assert(is_const::value == false); + static_assert(is_const::value == false); + static_assert(is_const::value == true); + static_assert(is_const::value == true); // is_volatile - AZ_TEST_STATIC_ASSERT(is_volatile::value == false); - AZ_TEST_STATIC_ASSERT(is_volatile::value == false); - AZ_TEST_STATIC_ASSERT(is_volatile::value == true); - AZ_TEST_STATIC_ASSERT(is_volatile::value == true); + static_assert(is_volatile::value == false); + static_assert(is_volatile::value == false); + static_assert(is_volatile::value == true); + static_assert(is_volatile::value == true); // is_pod - AZ_TEST_STATIC_ASSERT(is_pod::value == true); - AZ_TEST_STATIC_ASSERT(is_pod::value == true); - AZ_TEST_STATIC_ASSERT(is_pod::value == false); - AZ_TEST_STATIC_ASSERT((is_pod< aligned_storage<30, 32>::type >::value) == true); + static_assert(is_pod::value == true); + static_assert(is_pod::value == true); + static_assert(is_pod::value == false); + static_assert((is_pod< aligned_storage<30, 32>::type >::value) == true); // is_empty - AZ_TEST_STATIC_ASSERT(is_empty::value == false); - AZ_TEST_STATIC_ASSERT(is_empty::value == true); - AZ_TEST_STATIC_ASSERT(is_empty::value == false); + static_assert(is_empty::value == false); + static_assert(is_empty::value == true); + static_assert(is_empty::value == false); // is_polymorphic - AZ_TEST_STATIC_ASSERT(is_polymorphic::value == false); - AZ_TEST_STATIC_ASSERT(is_polymorphic::value == true); + static_assert(is_polymorphic::value == false); + static_assert(is_polymorphic::value == true); // is_abstract - AZ_TEST_STATIC_ASSERT(is_abstract::value == false); - AZ_TEST_STATIC_ASSERT(is_abstract::value == true); + static_assert(is_abstract::value == false); + static_assert(is_abstract::value == true); // has_trivial_constructor static_assert(is_trivially_constructible_v); @@ -264,20 +264,20 @@ namespace UnitTest // has_nothrow_assign // is_signed - AZ_TEST_STATIC_ASSERT(is_signed::value == true); - AZ_TEST_STATIC_ASSERT(is_signed::value == false); - AZ_TEST_STATIC_ASSERT(is_signed::value == false); + static_assert(is_signed::value == true); + static_assert(is_signed::value == false); + static_assert(is_signed::value == false); static_assert(is_signed::value); // is_unsigned - AZ_TEST_STATIC_ASSERT(is_unsigned::value == false); - AZ_TEST_STATIC_ASSERT(is_unsigned::value == false); - AZ_TEST_STATIC_ASSERT(is_unsigned::value == true); - AZ_TEST_STATIC_ASSERT(is_unsigned::value == false); + static_assert(is_unsigned::value == false); + static_assert(is_unsigned::value == false); + static_assert(is_unsigned::value == true); + static_assert(is_unsigned::value == false); // true and false types - AZ_TEST_STATIC_ASSERT(true_type::value == true); - AZ_TEST_STATIC_ASSERT(false_type::value == false); + static_assert(true_type::value == true); + static_assert(false_type::value == false); //! function traits tests struct NotMyStruct @@ -290,7 +290,7 @@ namespace UnitTest { bool operator()(FunctionTestStruct&) const { return true; }; }; - + using PrimitiveFunctionPtr = int(*)(bool, float, double, AZ::u8, AZ::s8, AZ::u16, AZ::s16, AZ::u32, AZ::s32, AZ::u64, AZ::s64); using NotMyStructMemberPtr = int(NotMyStruct::*)(); using ComplexFunctionPtr = float(*)(MyEmptyStruct&, NotMyStructMemberPtr, MyUnion*); @@ -298,27 +298,27 @@ namespace UnitTest using MemberFunctionPtr = void(MyInterface::*)(int); using ConstMemberFunctionPtr = bool(FunctionTestStruct::*)(FunctionTestStruct&) const; - AZ_TEST_STATIC_ASSERT((AZStd::is_same::result_type, int>::value)); - AZ_TEST_STATIC_ASSERT((AZStd::is_same::get_arg_t<10>, AZ::s64>::value)); - AZ_TEST_STATIC_ASSERT((AZStd::is_same, AZ::u16>::value)); - AZ_TEST_STATIC_ASSERT((AZStd::function_traits::arity == 11)); - - AZ_TEST_STATIC_ASSERT((AZStd::is_same, float>::value)); - AZ_TEST_STATIC_ASSERT((AZStd::is_same, int(NotMyStruct::*)()>::value)); - AZ_TEST_STATIC_ASSERT((AZStd::function_traits::arity == 3)); + static_assert((AZStd::is_same::result_type, int>::value)); + static_assert((AZStd::is_same::get_arg_t<10>, AZ::s64>::value)); + static_assert((AZStd::is_same, AZ::u16>::value)); + static_assert((AZStd::function_traits::arity == 11)); - AZ_TEST_STATIC_ASSERT((AZStd::is_same::class_fp_type, void(MyInterface::*)(int)>::value)); - AZ_TEST_STATIC_ASSERT((AZStd::is_same::raw_fp_type, void(*)(int)>::value)); - AZ_TEST_STATIC_ASSERT((AZStd::is_same::class_type, MyInterface>::value)); - AZ_TEST_STATIC_ASSERT((AZStd::function_traits::arity == 1)); + static_assert((AZStd::is_same, float>::value)); + static_assert((AZStd::is_same, int(NotMyStruct::*)()>::value)); + static_assert((AZStd::function_traits::arity == 3)); - AZ_TEST_STATIC_ASSERT((AZStd::is_same::class_fp_type, bool(FunctionTestStruct::*)(FunctionTestStruct&) const> ::value)); - AZ_TEST_STATIC_ASSERT((AZStd::is_same::raw_fp_type, bool(*)(FunctionTestStruct&)> ::value)); - AZ_TEST_STATIC_ASSERT((AZStd::is_same::class_type, FunctionTestStruct>::value)); - AZ_TEST_STATIC_ASSERT((AZStd::is_same, FunctionTestStruct&>::value)); - AZ_TEST_STATIC_ASSERT((AZStd::function_traits::arity == 1)); - - AZ_TEST_STATIC_ASSERT((AZStd::is_same::class_fp_type, bool(FunctionTestStruct::*)(FunctionTestStruct&) const>::value)); + static_assert((AZStd::is_same::class_fp_type, void(MyInterface::*)(int)>::value)); + static_assert((AZStd::is_same::raw_fp_type, void(*)(int)>::value)); + static_assert((AZStd::is_same::class_type, MyInterface>::value)); + static_assert((AZStd::function_traits::arity == 1)); + + static_assert((AZStd::is_same::class_fp_type, bool(FunctionTestStruct::*)(FunctionTestStruct&) const> ::value)); + static_assert((AZStd::is_same::raw_fp_type, bool(*)(FunctionTestStruct&)> ::value)); + static_assert((AZStd::is_same::class_type, FunctionTestStruct>::value)); + static_assert((AZStd::is_same, FunctionTestStruct&>::value)); + static_assert((AZStd::function_traits::arity == 1)); + + static_assert((AZStd::is_same::class_fp_type, bool(FunctionTestStruct::*)(FunctionTestStruct&) const>::value)); auto lambdaFunction = [](FunctionTestStruct, int) -> bool { @@ -326,16 +326,16 @@ namespace UnitTest }; using LambdaType = decltype(lambdaFunction); - AZ_TEST_STATIC_ASSERT((AZStd::is_same::raw_fp_type, bool(*)(FunctionTestStruct, int)>::value)); - AZ_TEST_STATIC_ASSERT((AZStd::is_same::class_fp_type, bool(LambdaType::*)(FunctionTestStruct, int) const>::value)); - AZ_TEST_STATIC_ASSERT((AZStd::function_traits::arity == 2)); + static_assert((AZStd::is_same::raw_fp_type, bool(*)(FunctionTestStruct, int)>::value)); + static_assert((AZStd::is_same::class_fp_type, bool(LambdaType::*)(FunctionTestStruct, int) const>::value)); + static_assert((AZStd::function_traits::arity == 2)); static_assert(AZStd::is_same::return_type, bool>::value, "Lambda result type should be bool"); AZStd::function stdFunction; using StdFunctionType = decay_t; - AZ_TEST_STATIC_ASSERT((AZStd::is_same::raw_fp_type, void(*)(LambdaType*, ComplexFunction&)>::value)); - AZ_TEST_STATIC_ASSERT((AZStd::function_traits::arity == 2)); - } + static_assert((AZStd::is_same::raw_fp_type, void(*)(LambdaType*, ComplexFunction&)>::value)); + static_assert((AZStd::function_traits::arity == 2)); + } struct ConstMethodTestStruct { @@ -343,145 +343,264 @@ namespace UnitTest void NonConstMethod() { } }; - AZ_TEST_STATIC_ASSERT((static_cast(function_traits::qual_flags) & static_cast(Internal::qualifier_flags::const_)) != 0); - AZ_TEST_STATIC_ASSERT((static_cast(function_traits::qual_flags) & static_cast(Internal::qualifier_flags::const_)) == 0); -} + static_assert((static_cast(function_traits::qual_flags)& static_cast(Internal::qualifier_flags::const_)) != 0); + static_assert((static_cast(function_traits::qual_flags)& static_cast(Internal::qualifier_flags::const_)) == 0); -TEST(TypeTraits, StdRemoveConstCompiles) -{ - static_assert(AZStd::is_same_v>, "C++11 std::remove_const_t has failed"); - static_assert(AZStd::is_same_v>, "C++11 std::remove_const_t has failed"); - static_assert(AZStd::is_same_v>, "C++11 std::remove_const_t has failed"); - static_assert(AZStd::is_same_v>, "C++11 std::remove_const_t has failed"); - static_assert(AZStd::is_same_v>, "C++11 std::remove_const_t has failed"); - static_assert(AZStd::is_same_v>>, "C++11 std::remove_const_t has failed"); -} - -TEST(TypeTraits, StdRemoveVolatileCompiles) -{ - static_assert(AZStd::is_same_v>, "C++11 std::remove_volatile_t has failed"); - static_assert(AZStd::is_same_v>, "C++11 std::remove_volatile_t has failed"); - static_assert(AZStd::is_same_v>, "C++11 std::remove_volatile_t has failed"); - static_assert(AZStd::is_same_v>, "C++11 std::remove_volatile_t has failed"); - static_assert(AZStd::is_same_v>, "C++11 std::remove_volatile_t has failed"); - static_assert(AZStd::is_same_v>, "C++11 std::remove_volatile_t has failed"); - static_assert(AZStd::is_same_v>>, "C++11 std::remove_volatile_t has failed"); -} - -TEST(TypeTraits, StdIsConstCompiles) -{ - static_assert(!AZStd::is_const_v, "C++11 std::is_const has failed"); - static_assert(AZStd::is_const_v, "C++11 std::is_const has failed"); - // references are never const - static_assert(!AZStd::is_const_v, "C++11 std::is_const has failed"); - // pointer checks for constness - static_assert(!AZStd::is_const_v, "C++11 std::is_const has failed"); - static_assert(AZStd::is_const_v, "C++11 std::is_const has failed"); - static_assert(AZStd::is_const_v, "C++11 std::is_const has failed"); -} - -TEST(TypeTraits, StdIsVolatileCompiles) -{ - static_assert(!AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); - static_assert(AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); - // references are never volatile - static_assert(!AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); - // pointer checks for volatile - static_assert(!AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); - static_assert(AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); - static_assert(!AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); - static_assert(AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); -} - -TEST(TypeTraits, TemplateIsCopyConstructible_WithCopyConstructibleValueType_ReturnsTrue) -{ - static_assert(AZStd::Internal::template_is_copy_constructible>::value, ""); - static_assert(AZStd::Internal::template_is_copy_constructible>::value, ""); - static_assert(AZStd::Internal::template_is_copy_constructible>::value, ""); - static_assert(AZStd::Internal::template_is_copy_constructible>::value, ""); - static_assert(AZStd::Internal::template_is_copy_constructible>::value, ""); - static_assert(AZStd::Internal::template_is_copy_constructible>::value, ""); - static_assert(AZStd::Internal::template_is_copy_constructible>::value, ""); - static_assert(AZStd::Internal::template_is_copy_constructible>::value, ""); - static_assert(AZStd::Internal::template_is_copy_constructible>::value, ""); - static_assert(AZStd::Internal::template_is_copy_constructible>::value, ""); - static_assert(AZStd::Internal::template_is_copy_constructible>::value, ""); - static_assert(AZStd::Internal::template_is_copy_constructible>::value, ""); - - struct CopyableType + TEST(TypeTraits, StdRemoveConstCompiles) { - CopyableType() = default; - CopyableType(const CopyableType&) = default; - }; - static_assert(AZStd::Internal::template_is_copy_constructible::value, ""); -} + static_assert(AZStd::is_same_v>, "C++11 std::remove_const_t has failed"); + static_assert(AZStd::is_same_v>, "C++11 std::remove_const_t has failed"); + static_assert(AZStd::is_same_v>, "C++11 std::remove_const_t has failed"); + static_assert(AZStd::is_same_v>, "C++11 std::remove_const_t has failed"); + static_assert(AZStd::is_same_v>, "C++11 std::remove_const_t has failed"); + static_assert(AZStd::is_same_v>>, "C++11 std::remove_const_t has failed"); + } -TEST(TypeTraits, TemplateIsCopyConstructible_WithOutCopyConstructibleValueType_ReturnsFalse) -{ - static_assert(!AZStd::Internal::template_is_copy_constructible>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible, int>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible, int>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible, int>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible, int>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible, int>>::value, ""); - static_assert(!AZStd::Internal::template_is_copy_constructible>>::value, ""); - - struct MoveOnly + TEST(TypeTraits, StdRemoveVolatileCompiles) { - MoveOnly() = default; - MoveOnly(const MoveOnly&) = delete; - MoveOnly(MoveOnly&&) = default; - }; - static_assert(!AZStd::Internal::template_is_copy_constructible::value, ""); -} + static_assert(AZStd::is_same_v>, "C++11 std::remove_volatile_t has failed"); + static_assert(AZStd::is_same_v>, "C++11 std::remove_volatile_t has failed"); + static_assert(AZStd::is_same_v>, "C++11 std::remove_volatile_t has failed"); + static_assert(AZStd::is_same_v>, "C++11 std::remove_volatile_t has failed"); + static_assert(AZStd::is_same_v>, "C++11 std::remove_volatile_t has failed"); + static_assert(AZStd::is_same_v>, "C++11 std::remove_volatile_t has failed"); + static_assert(AZStd::is_same_v>>, "C++11 std::remove_volatile_t has failed"); + } -TEST(TypeTraits, MakeSignedCompiles) -{ - static_assert(AZStd::is_same_v::type, AZ::s8>); - static_assert(AZStd::is_same_v, AZ::s8>); - static_assert(AZStd::is_same_v, AZ::s16>); - static_assert(AZStd::is_same_v, AZ::s16>); - static_assert(AZStd::is_same_v, AZ::s32>); - static_assert(AZStd::is_same_v, AZ::s32>); - static_assert(AZStd::is_same_v, AZ::s64>); - static_assert(AZStd::is_same_v, AZ::s64>); -} - -TEST(TypeTraits, MakeUnsignedCompiles) -{ - static_assert(AZStd::is_same_v::type, AZ::u8>); - static_assert(AZStd::is_same_v, AZ::u8>); - static_assert(AZStd::is_same_v, AZ::u16>); - static_assert(AZStd::is_same_v, AZ::u16>); - static_assert(AZStd::is_same_v, AZ::u32>); - static_assert(AZStd::is_same_v, AZ::u32>); - static_assert(AZStd::is_same_v, AZ::u64>); - static_assert(AZStd::is_same_v, AZ::u64>); -} - -// VS2017 workaround, calling decltype directly on the fully specialized aznumeric_cast template -// function fails with error C3556: 'aznumeric_cast': incorrect argument to 'decltype' -// So invoke the attempt to invoke function in a non-evaluated context and SFINAE to prevent a compile -// error -template -constexpr bool NumericCastInvocable = false; -template -constexpr bool NumericCastInvocable(AZStd::declval()))>> = true; -TEST(TypeTraits, NumericCastConversionOperatorCompiles) -{ - struct AzNumericCastConvertibleCompileTest + TEST(TypeTraits, StdIsConstCompiles) { - constexpr operator int() { return {}; }; - }; - static_assert(NumericCastInvocable, "aznumeric_cast conversion operator overload is should be compilable"); + static_assert(!AZStd::is_const_v, "C++11 std::is_const has failed"); + static_assert(AZStd::is_const_v, "C++11 std::is_const has failed"); + // references are never const + static_assert(!AZStd::is_const_v, "C++11 std::is_const has failed"); + // pointer checks for constness + static_assert(!AZStd::is_const_v, "C++11 std::is_const has failed"); + static_assert(AZStd::is_const_v, "C++11 std::is_const has failed"); + static_assert(AZStd::is_const_v, "C++11 std::is_const has failed"); + } + + TEST(TypeTraits, StdIsVolatileCompiles) + { + static_assert(!AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); + static_assert(AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); + // references are never volatile + static_assert(!AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); + // pointer checks for volatile + static_assert(!AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); + static_assert(AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); + static_assert(!AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); + static_assert(AZStd::is_volatile_v, "C++11 std::is_volatile has failed"); + } + + + struct CommonReferenceSpecializationTest + {}; +} + +namespace AZStd +{ + template