diff --git a/BUILD.bazel b/BUILD.bazel index ebb68f63..90983bff 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -624,6 +624,7 @@ cc_library( strip_include_prefix = strip_include_prefix_config(), deps = [ ":assert_or_abort", + ":compiler_compat", ":concepts", ":fixed_vector", ":preconditions", @@ -640,6 +641,7 @@ cc_library( strip_include_prefix = strip_include_prefix_config(), deps = [ ":algorithm", + ":compiler_compat", ":concepts", ":iterator_utils", ":memory", @@ -789,6 +791,9 @@ cc_library( hdrs = ["include/fixed_containers/memory.hpp"], includes = includes_config(), strip_include_prefix = strip_include_prefix_config(), + deps = [ + ":compiler_compat", + ], copts = ["-std=c++20"], ) @@ -1071,6 +1076,14 @@ cc_library( copts = ["-std=c++20"], ) +cc_library( + name = "compiler_compat", + hdrs = ["include/fixed_containers/compiler_compat.hpp"], + includes = includes_config(), + strip_include_prefix = strip_include_prefix_config(), + copts = ["-std=c++20"], +) + cc_library( name = "enums_test_common", hdrs = ["test/enums_test_common.hpp"], diff --git a/README.md b/README.md index d25f1764..fb510134 100644 --- a/README.md +++ b/README.md @@ -420,13 +420,15 @@ ctest -C Debug ### bazel #### clang +- clang++-20 and above [support function side effect verification](https://clang.llvm.org/docs/FunctionEffectAnalysis.html) + 1) Build separately (optional) ``` CC=clang++-13 bazel build --config=clang ... ``` 2) Run tests ``` -CC=clang++-13 bazel test --config=clang :all_tests +CC=clang++-13 bazel test --config=clang --build_tests_only --nocache_test_results --runs_per_test=1 :all_tests ``` #### clang with libc++ @@ -458,7 +460,7 @@ The macro is needed to avoid analysis on some particularly slow places. ## Tested Compilers -- Clang 13 +- Clang 13 (and 20) - GCC 11 - MSVC++ 14.29 / Visual Studio 2019 diff --git a/include/fixed_containers/compiler_compat.hpp b/include/fixed_containers/compiler_compat.hpp new file mode 100644 index 00000000..5b1d8d5b --- /dev/null +++ b/include/fixed_containers/compiler_compat.hpp @@ -0,0 +1,50 @@ +#pragma once + +/** + * @file compiler_compat.hpp + * @brief Compiler compatibility utilities and attributes. + */ + +/** + * @brief FIXED_CONTAINERS_NONALLOCATING expands to [[clang::nonallocating]] when supported. + * + * This attribute helps clang's function effects analysis detect potentially allocating + * functions. It's only applied when: + * 1. Using clang compiler + * 2. Clang version supports the nonallocating attribute (clang 20+) + * - https://releases.llvm.org/20.1.0/tools/clang/docs/ReleaseNotes.html#static-analyzer + * 3. _GLIBCXX_ASSERTIONS is not enabled (to avoid false positives from debug assertions) + * + * @note _GLIBCXX_ASSERTIONS enables debug assertions in the standard library which contain calls + * to __builtin_printf that clang's function effects analysis considers potentially allocating, + * even though the code under test may not allocate in normal operation. + */ +#if defined(__clang__) && __clang_major__ >= 20 && !defined(_GLIBCXX_ASSERTIONS) +#define FIXED_CONTAINERS_NONALLOCATING [[clang::nonallocating]] +#else +#define FIXED_CONTAINERS_NONALLOCATING +#endif + +/** + * @brief FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS disables nonallocating and nonblocking checks. + * + * This macro applies diagnostic pragmas to suppress function effects warnings for code + * that is expected to be non-allocating but may trigger false positives in the compiler's + * conservative analysis. + * + * Usage: + * ```cpp + * FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS( + * some_function_that_might_trigger_false_positive(); + * ) + * ``` + */ +#if defined(__clang__) +#define FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(...) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunknown-warning-option\"") \ + _Pragma("clang diagnostic ignored \"-Wfunction-effects\"") \ + __VA_ARGS__ _Pragma("clang diagnostic pop") +#else +#define FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(...) __VA_ARGS__ +#endif diff --git a/include/fixed_containers/fixed_string.hpp b/include/fixed_containers/fixed_string.hpp index 680b717f..89d97615 100644 --- a/include/fixed_containers/fixed_string.hpp +++ b/include/fixed_containers/fixed_string.hpp @@ -1,6 +1,7 @@ #pragma once #include "fixed_containers/assert_or_abort.hpp" +#include "fixed_containers/compiler_compat.hpp" #include "fixed_containers/concepts.hpp" #include "fixed_containers/fixed_vector.hpp" #include "fixed_containers/preconditions.hpp" @@ -54,7 +55,7 @@ class FixedString using const_reverse_iterator = typename FixedVecStorage::const_reverse_iterator; public: - [[nodiscard]] static constexpr std::size_t static_max_size() noexcept { return MAXIMUM_LENGTH; } + [[nodiscard]] static constexpr std::size_t static_max_size() noexcept FIXED_CONTAINERS_NONALLOCATING { return MAXIMUM_LENGTH; } static constexpr size_type npos = // NOLINT(readability-identifier-naming) std::string_view::npos; @@ -64,7 +65,7 @@ class FixedString public: constexpr FixedString(const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : IMPLEMENTATION_DETAIL_DO_NOT_USE_data_{} { null_terminate_at_max_length(); @@ -74,7 +75,7 @@ class FixedString constexpr FixedString( size_type count, CharT character, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : IMPLEMENTATION_DETAIL_DO_NOT_USE_data_{count, character, loc} { null_terminate_at_max_length(); @@ -83,14 +84,14 @@ class FixedString constexpr FixedString( const CharT* char_ptr, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : FixedString(std::string_view{char_ptr}, loc) { } constexpr FixedString( std::initializer_list ilist, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : IMPLEMENTATION_DETAIL_DO_NOT_USE_data_{ilist, loc} { null_terminate_at_max_length(); @@ -99,7 +100,7 @@ class FixedString explicit(false) constexpr FixedString(const std::string_view& view, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : IMPLEMENTATION_DETAIL_DO_NOT_USE_data_{view.begin(), view.end(), loc} { null_terminate_at_max_length(); @@ -109,7 +110,7 @@ class FixedString constexpr FixedString& assign( size_type count, CharT character, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { vec().assign(count, character, loc); null_terminate(loc); @@ -119,7 +120,7 @@ class FixedString constexpr FixedString& assign( InputIt first, InputIt last, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { vec().assign(first, last, loc); null_terminate(loc); @@ -127,7 +128,7 @@ class FixedString } constexpr FixedString& assign( std::initializer_list ilist, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { vec().assign(ilist, loc); null_terminate(loc); @@ -135,20 +136,20 @@ class FixedString } constexpr FixedString& assign( const std::string_view& view, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { vec().assign(view.begin(), view.end(), loc); null_terminate(loc); return *this; } - [[nodiscard]] constexpr reference operator[](size_type index) noexcept + [[nodiscard]] constexpr reference operator[](size_type index) noexcept FIXED_CONTAINERS_NONALLOCATING { // Cannot capture real source_location for operator[] // This operator should not range-check according to the spec, but we want the extra safety. return vec().at(index, std_transition::source_location::current()); } - [[nodiscard]] constexpr const_reference operator[](size_type index) const noexcept + [[nodiscard]] constexpr const_reference operator[](size_type index) const noexcept FIXED_CONTAINERS_NONALLOCATING { // Cannot capture real source_location for operator[] // This operator should not range-check according to the spec, but we want the extra safety. @@ -157,74 +158,74 @@ class FixedString [[nodiscard]] constexpr reference at(size_type index, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().at(index, loc); } [[nodiscard]] constexpr const_reference at( size_type index, const std_transition::source_location& loc = - std_transition::source_location::current()) const noexcept + std_transition::source_location::current()) const noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().at(index, loc); } constexpr reference front( - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().front(loc); } [[nodiscard]] constexpr const_reference front( const std_transition::source_location& loc = - std_transition::source_location::current()) const + std_transition::source_location::current()) const noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().front(loc); } constexpr reference back( - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().back(loc); } [[nodiscard]] constexpr const_reference back( const std_transition::source_location& loc = - std_transition::source_location::current()) const + std_transition::source_location::current()) const noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().back(loc); } - [[nodiscard]] constexpr const char* data() const noexcept { return vec().data(); } - [[nodiscard]] constexpr char* data() noexcept { return vec().data(); } - [[nodiscard]] constexpr const CharT* c_str() const noexcept { return data(); } + [[nodiscard]] constexpr const char* data() const noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().data(); } + [[nodiscard]] constexpr char* data() noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().data(); } + [[nodiscard]] constexpr const CharT* c_str() const noexcept FIXED_CONTAINERS_NONALLOCATING { return data(); } explicit(false) constexpr operator std::string_view() const { return std::string_view(data(), length()); } - constexpr iterator begin() noexcept { return vec().begin(); } - [[nodiscard]] constexpr const_iterator begin() const noexcept { return cbegin(); } - [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return vec().cbegin(); } - constexpr iterator end() noexcept { return vec().end(); } - [[nodiscard]] constexpr const_iterator end() const noexcept { return cend(); } - [[nodiscard]] constexpr const_iterator cend() const noexcept { return vec().cend(); } + constexpr iterator begin() noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().begin(); } + [[nodiscard]] constexpr const_iterator begin() const noexcept FIXED_CONTAINERS_NONALLOCATING { return cbegin(); } + [[nodiscard]] constexpr const_iterator cbegin() const noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().cbegin(); } + constexpr iterator end() noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().end(); } + [[nodiscard]] constexpr const_iterator end() const noexcept FIXED_CONTAINERS_NONALLOCATING { return cend(); } + [[nodiscard]] constexpr const_iterator cend() const noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().cend(); } - constexpr reverse_iterator rbegin() noexcept { return vec().rbegin(); } - [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return crbegin(); } - [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept + constexpr reverse_iterator rbegin() noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().rbegin(); } + [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept FIXED_CONTAINERS_NONALLOCATING { return crbegin(); } + [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().crbegin(); } - constexpr reverse_iterator rend() noexcept { return vec().rend(); } - [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return crend(); } - [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return vec().crend(); } + constexpr reverse_iterator rend() noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().rend(); } + [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept FIXED_CONTAINERS_NONALLOCATING { return crend(); } + [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().crend(); } - [[nodiscard]] constexpr bool empty() const noexcept { return length() == 0; } - [[nodiscard]] constexpr std::size_t length() const noexcept { return vec().size(); } - [[nodiscard]] constexpr std::size_t size() const noexcept { return length(); } - [[nodiscard]] constexpr std::size_t max_size() const noexcept { return static_max_size(); } + [[nodiscard]] constexpr bool empty() const noexcept FIXED_CONTAINERS_NONALLOCATING { return length() == 0; } + [[nodiscard]] constexpr std::size_t length() const noexcept FIXED_CONTAINERS_NONALLOCATING { return vec().size(); } + [[nodiscard]] constexpr std::size_t size() const noexcept FIXED_CONTAINERS_NONALLOCATING { return length(); } + [[nodiscard]] constexpr std::size_t max_size() const noexcept FIXED_CONTAINERS_NONALLOCATING { return static_max_size(); } constexpr void reserve(const std::size_t new_capacity, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { if (preconditions::test(new_capacity <= MAXIMUM_LENGTH)) { @@ -232,9 +233,9 @@ class FixedString } // Do nothing } - [[nodiscard]] constexpr std::size_t capacity() const noexcept { return max_size(); } + [[nodiscard]] constexpr std::size_t capacity() const noexcept FIXED_CONTAINERS_NONALLOCATING { return max_size(); } - constexpr void clear() noexcept + constexpr void clear() noexcept FIXED_CONTAINERS_NONALLOCATING { vec().clear(); null_terminate(std_transition::source_location::current()); @@ -243,7 +244,7 @@ class FixedString constexpr iterator insert( const_iterator pos, CharT character, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { const ScopedNullTermination guard{this, loc}; return vec().insert(pos, character, loc); @@ -253,7 +254,7 @@ class FixedString const_iterator pos, InputIt first, InputIt last, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { const ScopedNullTermination guard{this, loc}; return vec().insert(pos, first, last, loc); @@ -261,7 +262,7 @@ class FixedString constexpr iterator insert( const_iterator pos, std::initializer_list ilist, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { const ScopedNullTermination guard{this, loc}; return vec().insert(pos, ilist, loc); @@ -269,7 +270,7 @@ class FixedString constexpr iterator insert( const_iterator pos, std::string_view view, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { const ScopedNullTermination guard{this, loc}; return vec().insert(pos, view.begin(), view.end(), loc); @@ -277,7 +278,7 @@ class FixedString constexpr iterator erase( const_iterator position, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { const ScopedNullTermination guard{this, loc}; return vec().erase(position, loc); @@ -285,7 +286,7 @@ class FixedString constexpr iterator erase( const_iterator first, const_iterator last, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { const ScopedNullTermination guard{this, loc}; return vec().erase(first, last, loc); @@ -293,14 +294,14 @@ class FixedString constexpr void push_back( CharT character, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { vec().push_back(character, loc); null_terminate(loc); } constexpr void pop_back( - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { vec().pop_back(loc); null_terminate(loc); @@ -309,7 +310,7 @@ class FixedString template constexpr FixedString& append( const CharT* char_ptr, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { return append(std::string_view{char_ptr}, loc); } @@ -317,7 +318,7 @@ class FixedString constexpr FixedString& append( InputIt first, InputIt last, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { vec().insert(vec().cend(), first, last, loc); null_terminate(loc); @@ -325,7 +326,7 @@ class FixedString } constexpr FixedString& append( std::initializer_list ilist, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { vec().insert(vec().cend(), ilist, loc); null_terminate(loc); @@ -333,76 +334,76 @@ class FixedString } constexpr FixedString& append( const std::string_view& view, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { vec().insert(vec().cend(), view.begin(), view.end(), loc); null_terminate(loc); return *this; } - constexpr FixedString& operator+=(CharT character) + constexpr FixedString& operator+=(CharT character) noexcept FIXED_CONTAINERS_NONALLOCATING { return append(character, std_transition::source_location::current()); } - constexpr FixedString& operator+=(const CharT* char_ptr) + constexpr FixedString& operator+=(const CharT* char_ptr) noexcept FIXED_CONTAINERS_NONALLOCATING { return append(char_ptr, std_transition::source_location::current()); } - constexpr FixedString& operator+=(std::initializer_list ilist) + constexpr FixedString& operator+=(std::initializer_list ilist) noexcept FIXED_CONTAINERS_NONALLOCATING { return append(ilist, std_transition::source_location::current()); } - constexpr FixedString& operator+=(const std::string_view& view) + constexpr FixedString& operator+=(const std::string_view& view) noexcept FIXED_CONTAINERS_NONALLOCATING { return append(view, std_transition::source_location::current()); } template [[nodiscard]] constexpr size_type find(const FixedString& str, - const size_type pos = 0) const + const size_type pos = 0) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find(str, pos); } [[nodiscard]] constexpr size_type find(const CharT* char_ptr, size_type pos, - size_type count) const + size_type count) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find(char_ptr, pos, count); } - [[nodiscard]] constexpr size_type find(const CharT* const str, const size_type pos = 0) const + [[nodiscard]] constexpr size_type find(const CharT* const str, const size_type pos = 0) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find(str, pos); } - [[nodiscard]] constexpr size_type find(const CharT character, const size_type pos = 0) const + [[nodiscard]] constexpr size_type find(const CharT character, const size_type pos = 0) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find(character, pos); } template requires(std::is_convertible_v and not std::is_convertible_v) - [[nodiscard]] constexpr size_type find(const StringViewLike& str, const size_type pos = 0) const + [[nodiscard]] constexpr size_type find(const StringViewLike& str, const size_type pos = 0) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find(str, pos); } template [[nodiscard]] constexpr size_type rfind(const FixedString& str, - const size_type pos = npos) const + const size_type pos = npos) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().rfind(str, pos); } [[nodiscard]] constexpr size_type rfind(const CharT* char_ptr, size_type pos, - size_type count) const + size_type count) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().rfind(char_ptr, pos, count); } [[nodiscard]] constexpr size_type rfind(const CharT* const str, - const size_type pos = npos) const + const size_type pos = npos) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().rfind(str, pos); } - [[nodiscard]] constexpr size_type rfind(const CharT character, const size_type pos = npos) const + [[nodiscard]] constexpr size_type rfind(const CharT character, const size_type pos = npos) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().rfind(character, pos); } @@ -410,30 +411,30 @@ class FixedString requires(std::is_convertible_v and not std::is_convertible_v) [[nodiscard]] constexpr size_type rfind(const StringViewLike& str, - const size_type pos = npos) const + const size_type pos = npos) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().rfind(str, pos); } template [[nodiscard]] constexpr size_type find_first_of( - const FixedString& str, const size_type pos = 0) const + const FixedString& str, const size_type pos = 0) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_first_of(str, pos); } [[nodiscard]] constexpr size_type find_first_of(const CharT* char_ptr, size_type pos, - size_type count) const + size_type count) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_first_of(char_ptr, pos, count); } [[nodiscard]] constexpr size_type find_first_of(const CharT* const str, - const size_type pos = 0) const + const size_type pos = 0) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_first_of(str, pos); } [[nodiscard]] constexpr size_type find_first_of(const CharT character, - const size_type pos = 0) const + const size_type pos = 0) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_first_of(character, pos); } @@ -441,30 +442,30 @@ class FixedString requires(std::is_convertible_v and not std::is_convertible_v) [[nodiscard]] constexpr size_type find_first_of(const StringViewLike& str, - const size_type pos = 0) const + const size_type pos = 0) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_first_of(str, pos); } template [[nodiscard]] constexpr size_type find_first_not_of( - const FixedString& str, const size_type pos = 0) const + const FixedString& str, const size_type pos = 0) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_first_not_of(str, pos); } [[nodiscard]] constexpr size_type find_first_not_of(const CharT* char_ptr, size_type pos, - size_type count) const + size_type count) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_first_not_of(char_ptr, pos, count); } [[nodiscard]] constexpr size_type find_first_not_of(const CharT* const str, - const size_type pos = 0) const + const size_type pos = 0) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_first_not_of(str, pos); } [[nodiscard]] constexpr size_type find_first_not_of(const CharT character, - const size_type pos = 0) const + const size_type pos = 0) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_first_not_of(character, pos); } @@ -472,30 +473,30 @@ class FixedString requires(std::is_convertible_v and not std::is_convertible_v) [[nodiscard]] constexpr size_type find_first_not_of(const StringViewLike& str, - const size_type pos = 0) const + const size_type pos = 0) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_first_not_of(str, pos); } template [[nodiscard]] constexpr size_type find_last_of( - const FixedString& str, const size_type pos = npos) const + const FixedString& str, const size_type pos = npos) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_last_of(str, pos); } [[nodiscard]] constexpr size_type find_last_of(const CharT* char_ptr, size_type pos, - size_type count) const + size_type count) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_last_of(char_ptr, pos, count); } [[nodiscard]] constexpr size_type find_last_of(const CharT* const str, - const size_type pos = npos) const + const size_type pos = npos) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_last_of(str, pos); } [[nodiscard]] constexpr size_type find_last_of(const CharT character, - const size_type pos = npos) const + const size_type pos = npos) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_last_of(character, pos); } @@ -503,30 +504,30 @@ class FixedString requires(std::is_convertible_v and not std::is_convertible_v) [[nodiscard]] constexpr size_type find_last_of(const StringViewLike& str, - const size_type pos = npos) const + const size_type pos = npos) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_last_of(str, pos); } template [[nodiscard]] constexpr size_type find_last_not_of( - const FixedString& str, const size_type pos = npos) const + const FixedString& str, const size_type pos = npos) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_last_not_of(str, pos); } [[nodiscard]] constexpr size_type find_last_not_of(const CharT* char_ptr, size_type pos, - size_type count) const + size_type count) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_last_not_of(char_ptr, pos, count); } [[nodiscard]] constexpr size_type find_last_not_of(const CharT* const str, - const size_type pos = npos) const + const size_type pos = npos) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_last_not_of(str, pos); } [[nodiscard]] constexpr size_type find_last_not_of(const CharT character, - const size_type pos = npos) const + const size_type pos = npos) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_last_not_of(character, pos); } @@ -534,65 +535,72 @@ class FixedString requires(std::is_convertible_v and not std::is_convertible_v) [[nodiscard]] constexpr size_type find_last_not_of(const StringViewLike& str, - const size_type pos = npos) const + const size_type pos = npos) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().find_last_not_of(str, pos); } - [[nodiscard]] constexpr int compare(std::string_view view) const + [[nodiscard]] constexpr int compare(std::string_view view) const noexcept FIXED_CONTAINERS_NONALLOCATING { return std::string_view(*this).compare(view); } template - constexpr bool operator==(const FixedString& other) const + constexpr bool operator==(const FixedString& other) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view() == std::string_view{other}; } - constexpr bool operator==(const CharT* other) const + constexpr bool operator==(const CharT* other) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view() == std::string_view{other}; } - constexpr bool operator==(std::string_view view) const noexcept { return as_view() == view; } + constexpr bool operator==(std::string_view view) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view() == view; } template constexpr std::strong_ordering operator<=>( - const FixedString& other) const noexcept + const FixedString& other) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view() <=> other; } - constexpr std::strong_ordering operator<=>(const CharT* other) const noexcept + constexpr std::strong_ordering operator<=>(const CharT* other) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view() <=> std::string_view{other}; } - constexpr std::strong_ordering operator<=>(const std::string_view& other) const noexcept + constexpr std::strong_ordering operator<=>(const std::string_view& other) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view() <=> other; } - [[nodiscard]] constexpr bool starts_with(const std::string_view& prefix) const noexcept + [[nodiscard]] constexpr bool starts_with(const std::string_view& prefix) const noexcept FIXED_CONTAINERS_NONALLOCATING { - return as_view().starts_with(prefix); + // Suppress function effects: std::string_view methods may call exception-throwing functions + // in their implementation, but we compile without exceptions, so no allocation occurs + // ... + // c++/12/bits/functexcept.h:81:3 declaration cannot be inferred 'nonallocating' (...) + // __throw_out_of_range_fmt(const char*, ...) __attribute__((__noreturn__)) + return FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(as_view().starts_with(prefix)); } - [[nodiscard]] constexpr bool starts_with(char character) const noexcept + [[nodiscard]] constexpr bool starts_with(char character) const noexcept FIXED_CONTAINERS_NONALLOCATING { - return as_view().starts_with(character); + // Same note as above in starts_with(std::string_view) + return FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(as_view().starts_with(character)); } - [[nodiscard]] constexpr bool starts_with(const char* char_ptr) const noexcept + [[nodiscard]] constexpr bool starts_with(const char* char_ptr) const noexcept FIXED_CONTAINERS_NONALLOCATING { - return as_view().starts_with(char_ptr); + // Same note as above in starts_with(std::string_view) + return FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(as_view().starts_with(char_ptr)); } - [[nodiscard]] constexpr bool ends_with(const std::string_view& suffix) const noexcept + [[nodiscard]] constexpr bool ends_with(const std::string_view& suffix) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().ends_with(suffix); } - [[nodiscard]] constexpr bool ends_with(char character) const noexcept + [[nodiscard]] constexpr bool ends_with(char character) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().ends_with(character); } - [[nodiscard]] constexpr bool ends_with(const char* char_ptr) const noexcept + [[nodiscard]] constexpr bool ends_with(const char* char_ptr) const noexcept FIXED_CONTAINERS_NONALLOCATING { return as_view().ends_with(char_ptr); } @@ -601,19 +609,24 @@ class FixedString size_type pos = 0, size_t len = MAXIMUM_LENGTH, const std_transition::source_location& loc = - std_transition::source_location::current()) const + std_transition::source_location::current()) const noexcept FIXED_CONTAINERS_NONALLOCATING { if (preconditions::test(pos < length())) { Checking::out_of_range(pos, length(), loc); } - return std::string_view(*this).substr(pos, len); + // Suppress function effects: std::string_view methods may call exception-throwing functions + // in their implementation, but we compile without exceptions, so no allocation occurs + // ... + // c++/12/bits/functexcept.h:81:3 declaration cannot be inferred 'nonallocating' (...) + // __throw_out_of_range_fmt(const char*, ...) __attribute__((__noreturn__)) + return FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(std::string_view(*this).substr(pos, len)); } constexpr void resize( size_type count, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { resize(count, CharT{}, loc); } @@ -621,20 +634,20 @@ class FixedString constexpr void resize( size_type count, CharT character, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { vec().resize(count, character, loc); null_terminate(loc); } private: - constexpr void null_terminate(std::size_t n) + constexpr void null_terminate(std::size_t n) noexcept FIXED_CONTAINERS_NONALLOCATING { // This bypasses the vector's bounds check // This keeps the vector's size equal to the length of the string *std::next(vec().data(), static_cast(n)) = '\0'; } - constexpr void null_terminate(const std_transition::source_location& loc) + constexpr void null_terminate(const std_transition::source_location& loc) noexcept FIXED_CONTAINERS_NONALLOCATING { const std::size_t len = length(); if (preconditions::test(len <= MAXIMUM_LENGTH)) @@ -644,11 +657,19 @@ class FixedString null_terminate(length()); } - constexpr void null_terminate_at_max_length() { null_terminate(MAXIMUM_LENGTH); } + constexpr void null_terminate_at_max_length() noexcept FIXED_CONTAINERS_NONALLOCATING { null_terminate(MAXIMUM_LENGTH); } - [[nodiscard]] constexpr std::string_view as_view() const { return *this; } + [[nodiscard]] constexpr std::string_view as_view() const noexcept FIXED_CONTAINERS_NONALLOCATING + { + // Suppress function effects: std::string_view methods may call exception-throwing functions + // in their implementation, but we compile without exceptions, so no allocation occurs + // ... + // c++/12/bits/functexcept.h:81:3 declaration cannot be inferred 'nonallocating' (...) + // __throw_out_of_range_fmt(const char*, ...) __attribute__((__noreturn__)) + return FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(*this); + } - [[nodiscard]] constexpr const FixedVecStorage& vec() const + [[nodiscard]] constexpr const FixedVecStorage& vec() const noexcept FIXED_CONTAINERS_NONALLOCATING { return IMPLEMENTATION_DETAIL_DO_NOT_USE_data_; } @@ -656,32 +677,34 @@ class FixedString }; template -std::istream& operator>>(std::istream& stream, FixedString& str) +std::istream& operator>>(std::istream& stream, FixedString& str) noexcept FIXED_CONTAINERS_NONALLOCATING { static constexpr std::size_t MAXIMUM_LENGTH_WITH_NULL_TERMINATOR = MAXIMUM_LENGTH + 1; str.clear(); // Skip leading whitespace (`std::istream >> std::string` behaves the same way) - stream >> std::ws; + // c++/12/istream:316:7 declaration cannot be inferred 'nonallocating' because it has no definition in this translation unit + // 316 | get(char_type& __c); + FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(stream) >> std::ws; char character{}; - stream.get(character); + FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(stream.get(character)); // If EOF/error, put the character back and return if (stream.eof() || stream.fail()) { - return stream.putback(character); + return FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(stream.putback(character)); } for (; !std::isspace(character) && !is_full(str) && !stream.eof() && !stream.fail(); - stream.get(character)) + FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(stream.get(character))) { str.push_back(character); } if (stream.fail()) { - stream.setstate(std::ios::failbit); + FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(stream.setstate(std::ios::failbit)); return stream; } @@ -697,13 +720,15 @@ std::istream& operator>>(std::istream& stream, FixedString -std::ostream& operator<<(std::ostream& stream, const FixedString& str) +std::ostream& operator<<(std::ostream& stream, const FixedString& str) noexcept FIXED_CONTAINERS_NONALLOCATING { - return stream << std::string_view{str}; + // c++/12/bits/ostream_insert.h:77:5: note: declaration cannot be inferred 'nonallocating' because it has no definition in this translation unit + // 77 | __ostream_insert(basic_ostream<_CharT, _Traits>& __out, + return FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(stream) << std::string_view{str}; } template -[[nodiscard]] constexpr bool is_full(const FixedString& container) +[[nodiscard]] constexpr bool is_full(const FixedString& container) noexcept FIXED_CONTAINERS_NONALLOCATING { return container.size() >= container.max_size(); } @@ -720,7 +745,7 @@ template < [[nodiscard]] constexpr FixedStringType make_fixed_string( const char (&list)[MAXIMUM_LENGTH_WITH_NULL_TERMINATOR], const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { constexpr std::size_t MAXIMUM_LENGTH = MAXIMUM_LENGTH_WITH_NULL_TERMINATOR - 1; assert_or_abort(*std::next(list, MAXIMUM_LENGTH) == '\0'); @@ -731,7 +756,7 @@ template [[nodiscard]] constexpr auto make_fixed_string( const char (&list)[MAXIMUM_LENGTH_WITH_NULL_TERMINATOR], const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { constexpr std::size_t MAXIMUM_LENGTH = MAXIMUM_LENGTH_WITH_NULL_TERMINATOR - 1; using CheckingType = customize::SequenceContainerAbortChecking; diff --git a/include/fixed_containers/fixed_vector.hpp b/include/fixed_containers/fixed_vector.hpp index 542db56f..a2d27888 100644 --- a/include/fixed_containers/fixed_vector.hpp +++ b/include/fixed_containers/fixed_vector.hpp @@ -1,6 +1,7 @@ #pragma once #include "fixed_containers/algorithm.hpp" +#include "fixed_containers/compiler_compat.hpp" #include "fixed_containers/concepts.hpp" #include "fixed_containers/iterator_utils.hpp" #include "fixed_containers/memory.hpp" @@ -26,52 +27,52 @@ class FixedVectorBuilder public: constexpr FixedVectorBuilder() = default; - constexpr FixedVectorBuilder& push_back(const T& key) & noexcept + constexpr FixedVectorBuilder& push_back(const T& key) & noexcept FIXED_CONTAINERS_NONALLOCATING { vector_.push_back(key); return *this; } - constexpr FixedVectorBuilder&& push_back(const T& key) && noexcept + constexpr FixedVectorBuilder&& push_back(const T& key) && noexcept FIXED_CONTAINERS_NONALLOCATING { return std::move(push_back(key)); } - constexpr FixedVectorBuilder& push_back_all(std::initializer_list list) & noexcept + constexpr FixedVectorBuilder& push_back_all(std::initializer_list list) & noexcept FIXED_CONTAINERS_NONALLOCATING { push_back_all(list.begin(), list.end()); return *this; } - constexpr FixedVectorBuilder&& push_back_all(std::initializer_list list) && noexcept + constexpr FixedVectorBuilder&& push_back_all(std::initializer_list list) && noexcept FIXED_CONTAINERS_NONALLOCATING { return std::move(push_back_all(list)); } template - constexpr FixedVectorBuilder& push_back_all(InputIt first, InputIt last) & noexcept + constexpr FixedVectorBuilder& push_back_all(InputIt first, InputIt last) & noexcept FIXED_CONTAINERS_NONALLOCATING { vector_.insert(vector_.end(), first, last); return *this; } template - constexpr FixedVectorBuilder&& push_back_all(InputIt first, InputIt last) && noexcept + constexpr FixedVectorBuilder&& push_back_all(InputIt first, InputIt last) && noexcept FIXED_CONTAINERS_NONALLOCATING { return std::move(push_back_all(first, last)); } template - constexpr FixedVectorBuilder& push_back_all(const Container& container) & noexcept + constexpr FixedVectorBuilder& push_back_all(const Container& container) & noexcept FIXED_CONTAINERS_NONALLOCATING { push_back_all(container.cbegin(), container.cend()); return *this; } template - constexpr FixedVectorBuilder&& push_back_all(const Container& container) && noexcept + constexpr FixedVectorBuilder&& push_back_all(const Container& container) && noexcept FIXED_CONTAINERS_NONALLOCATING { return std::move(push_back_all(container.cbegin(), container.cend())); } - [[nodiscard]] constexpr FixedVectorType build() const& { return vector_; } - constexpr FixedVectorType&& build() && { return std::move(vector_); } + [[nodiscard]] constexpr FixedVectorType build() const& noexcept FIXED_CONTAINERS_NONALLOCATING { return vector_; } + constexpr FixedVectorType&& build() && noexcept FIXED_CONTAINERS_NONALLOCATING { return std::move(vector_); } private: FixedVectorType vector_; @@ -105,11 +106,11 @@ class FixedVectorBase struct Mapper { - constexpr T& operator()(OptionalT& opt_storage) const noexcept + constexpr T& operator()(OptionalT& opt_storage) const noexcept FIXED_CONTAINERS_NONALLOCATING { return optional_storage_detail::get(opt_storage); } - constexpr const T& operator()(const OptionalT& opt_storage) const noexcept + constexpr const T& operator()(const OptionalT& opt_storage) const noexcept FIXED_CONTAINERS_NONALLOCATING // requires(not std::is_const_v) { return optional_storage_detail::get(opt_storage); @@ -137,11 +138,11 @@ class FixedVectorBase using const_reverse_iterator = std::reverse_iterator; public: - [[nodiscard]] static constexpr std::size_t static_max_size() noexcept { return MAXIMUM_SIZE; } + [[nodiscard]] static constexpr std::size_t static_max_size() noexcept FIXED_CONTAINERS_NONALLOCATING { return MAXIMUM_SIZE; } private: static constexpr void check_target_size(size_type target_size, - const std_transition::source_location& loc) + const std_transition::source_location& loc) noexcept FIXED_CONTAINERS_NONALLOCATING { if (preconditions::test(target_size <= MAXIMUM_SIZE)) { @@ -154,7 +155,7 @@ class FixedVectorBase Array IMPLEMENTATION_DETAIL_DO_NOT_USE_array_; public: - constexpr FixedVectorBase() noexcept + constexpr FixedVectorBase() noexcept FIXED_CONTAINERS_NONALLOCATING requires(MAXIMUM_SIZE > 0) : IMPLEMENTATION_DETAIL_DO_NOT_USE_size_{0} // Don't initialize the array @@ -175,7 +176,7 @@ class FixedVectorBase // Special constructor that initializes the array for 0 size. // Needed by libc++17 and lower, unit tests for vector will NOT fail, // but their usage in reflection will. - constexpr FixedVectorBase() noexcept + constexpr FixedVectorBase() noexcept FIXED_CONTAINERS_NONALLOCATING requires(MAXIMUM_SIZE == 0) : IMPLEMENTATION_DETAIL_DO_NOT_USE_size_{0} , IMPLEMENTATION_DETAIL_DO_NOT_USE_array_{} @@ -185,7 +186,7 @@ class FixedVectorBase constexpr FixedVectorBase(std::size_t count, const T& value, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : FixedVectorBase() { check_target_size(count, loc); @@ -198,7 +199,7 @@ class FixedVectorBase constexpr explicit FixedVectorBase(std::size_t count, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : FixedVectorBase(count, T(), loc) { } @@ -207,7 +208,7 @@ class FixedVectorBase constexpr FixedVectorBase(InputIt first, InputIt last, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : FixedVectorBase() { insert(cend(), first, last, loc); @@ -215,7 +216,7 @@ class FixedVectorBase constexpr FixedVectorBase(std::initializer_list list, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : FixedVectorBase(list.begin(), list.end(), loc) { } @@ -228,14 +229,14 @@ class FixedVectorBase */ constexpr void resize( size_type count, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { this->resize(count, T{}, loc); } constexpr void resize( size_type count, const value_type& value, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { check_target_size(count, loc); @@ -259,14 +260,14 @@ class FixedVectorBase */ constexpr void push_back( const value_type& value, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { check_not_full(loc); this->push_back_internal(value); } constexpr void push_back( value_type&& value, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { check_not_full(loc); this->push_back_internal(std::move(value)); @@ -276,7 +277,7 @@ class FixedVectorBase * Calling emplace_back on a full container is undefined. */ template - constexpr reference emplace_back(Args&&... args) + constexpr reference emplace_back(Args&&... args) noexcept FIXED_CONTAINERS_NONALLOCATING { check_not_full(std_transition::source_location::current()); emplace_at(end_index(), std::forward(args)...); @@ -289,7 +290,7 @@ class FixedVectorBase * Calling pop_back on an empty container is undefined. */ constexpr void pop_back( - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { check_not_empty(loc); destroy_at(back_index()); @@ -303,7 +304,7 @@ class FixedVectorBase constexpr iterator insert( const_iterator pos, const value_type& value, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { check_not_full(loc); auto entry_it = advance_all_after_iterator_by_n(pos, 1); @@ -313,7 +314,7 @@ class FixedVectorBase constexpr iterator insert( const_iterator pos, value_type&& value, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { check_not_full(loc); auto entry_it = advance_all_after_iterator_by_n(pos, 1); @@ -325,7 +326,7 @@ class FixedVectorBase const_iterator pos, InputIt first, InputIt last, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { return insert_internal( typename std::iterator_traits::iterator_category{}, pos, first, last, loc); @@ -333,7 +334,7 @@ class FixedVectorBase constexpr iterator insert( const_iterator pos, std::initializer_list ilist, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { return insert_internal( std::random_access_iterator_tag{}, pos, ilist.begin(), ilist.end(), loc); @@ -344,7 +345,7 @@ class FixedVectorBase * Calling emplace on a full container is undefined. */ template - constexpr iterator emplace(const_iterator pos, Args&&... args) + constexpr iterator emplace(const_iterator pos, Args&&... args) noexcept FIXED_CONTAINERS_NONALLOCATING { check_not_full(std_transition::source_location::current()); auto entry_it = advance_all_after_iterator_by_n(pos, 1); @@ -358,7 +359,7 @@ class FixedVectorBase constexpr void assign( size_type count, const value_type& value, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { check_target_size(count, loc); this->clear(); @@ -372,7 +373,7 @@ class FixedVectorBase constexpr void assign( InputIt first, InputIt last, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { this->clear(); this->insert(cend(), first, last, loc); @@ -380,7 +381,7 @@ class FixedVectorBase constexpr void assign( std::initializer_list ilist, - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { this->clear(); this->insert(cend(), ilist, loc); @@ -392,7 +393,7 @@ class FixedVectorBase constexpr iterator erase(const_iterator first, const_iterator last, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { if (preconditions::test(first <= last)) { @@ -424,7 +425,8 @@ class FixedVectorBase else { // Do the move - const iterator write_end_it = std::move(read_start_it, read_end_it, write_start_it); + // Doesn't actually allocate + const iterator write_end_it = FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(std::move(read_start_it, read_end_it, write_start_it)); // Clean out the tail destroy_range(write_end_it, read_end_it); @@ -439,7 +441,7 @@ class FixedVectorBase */ constexpr iterator erase(const_iterator pos, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { return erase(pos, std::next(pos), loc); } @@ -447,7 +449,7 @@ class FixedVectorBase /** * Erases all elements from the container. After this call, size() returns zero. */ - constexpr void clear() noexcept + constexpr void clear() noexcept FIXED_CONTAINERS_NONALLOCATING { destroy_range(begin(), end()); set_size(0); @@ -456,13 +458,13 @@ class FixedVectorBase /** * Regular accessors. */ - constexpr reference operator[](size_type index) noexcept + constexpr reference operator[](size_type index) noexcept FIXED_CONTAINERS_NONALLOCATING { // Cannot capture real source_location for operator[] // This operator should not range-check according to the spec, but we want the extra safety. return at(index, std_transition::source_location::current()); } - constexpr const_reference operator[](size_type index) const noexcept + constexpr const_reference operator[](size_type index) const noexcept FIXED_CONTAINERS_NONALLOCATING { // Cannot capture real source_location for operator[] // This operator should not range-check according to the spec, but we want the extra safety. @@ -471,7 +473,7 @@ class FixedVectorBase constexpr reference at(size_type index, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { if (preconditions::test(index < size())) { @@ -482,7 +484,7 @@ class FixedVectorBase [[nodiscard]] constexpr const_reference at( size_type index, const std_transition::source_location& loc = - std_transition::source_location::current()) const noexcept + std_transition::source_location::current()) const noexcept FIXED_CONTAINERS_NONALLOCATING { if (preconditions::test(index < size())) { @@ -492,37 +494,37 @@ class FixedVectorBase } constexpr reference front( - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { check_not_empty(loc); return unchecked_at(front_index()); } [[nodiscard]] constexpr const_reference front( const std_transition::source_location& loc = - std_transition::source_location::current()) const + std_transition::source_location::current()) const noexcept FIXED_CONTAINERS_NONALLOCATING { check_not_empty(loc); return unchecked_at(front_index()); } constexpr reference back( - const std_transition::source_location& loc = std_transition::source_location::current()) + const std_transition::source_location& loc = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { check_not_empty(loc); return unchecked_at(back_index()); } [[nodiscard]] constexpr const_reference back( const std_transition::source_location& loc = - std_transition::source_location::current()) const + std_transition::source_location::current()) const noexcept FIXED_CONTAINERS_NONALLOCATING { check_not_empty(loc); return unchecked_at(back_index()); } - constexpr value_type* data() noexcept + constexpr value_type* data() noexcept FIXED_CONTAINERS_NONALLOCATING { return std::addressof(optional_storage_detail::get(*array().data())); } - [[nodiscard]] constexpr const value_type* data() const noexcept + [[nodiscard]] constexpr const value_type* data() const noexcept FIXED_CONTAINERS_NONALLOCATING { return std::addressof(optional_storage_detail::get(*array().data())); } @@ -530,28 +532,28 @@ class FixedVectorBase /** * Iterators */ - constexpr iterator begin() noexcept { return create_iterator(front_index()); } - [[nodiscard]] constexpr const_iterator begin() const noexcept { return cbegin(); } - [[nodiscard]] constexpr const_iterator cbegin() const noexcept + constexpr iterator begin() noexcept FIXED_CONTAINERS_NONALLOCATING { return create_iterator(front_index()); } + [[nodiscard]] constexpr const_iterator begin() const noexcept FIXED_CONTAINERS_NONALLOCATING { return cbegin(); } + [[nodiscard]] constexpr const_iterator cbegin() const noexcept FIXED_CONTAINERS_NONALLOCATING { return create_const_iterator(front_index()); } - constexpr iterator end() noexcept { return create_iterator(end_index()); } - [[nodiscard]] constexpr const_iterator end() const noexcept { return cend(); } - [[nodiscard]] constexpr const_iterator cend() const noexcept + constexpr iterator end() noexcept FIXED_CONTAINERS_NONALLOCATING { return create_iterator(end_index()); } + [[nodiscard]] constexpr const_iterator end() const noexcept FIXED_CONTAINERS_NONALLOCATING { return cend(); } + [[nodiscard]] constexpr const_iterator cend() const noexcept FIXED_CONTAINERS_NONALLOCATING { return create_const_iterator(end_index()); } - constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return crbegin(); } - [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept + constexpr reverse_iterator rbegin() noexcept FIXED_CONTAINERS_NONALLOCATING { return reverse_iterator(end()); } + [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept FIXED_CONTAINERS_NONALLOCATING { return crbegin(); } + [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept FIXED_CONTAINERS_NONALLOCATING { return const_reverse_iterator(cend()); } - constexpr reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return crend(); } - [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept + constexpr reverse_iterator rend() noexcept FIXED_CONTAINERS_NONALLOCATING { return reverse_iterator(begin()); } + [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept FIXED_CONTAINERS_NONALLOCATING { return crend(); } + [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept FIXED_CONTAINERS_NONALLOCATING { return const_reverse_iterator(cbegin()); } @@ -559,11 +561,11 @@ class FixedVectorBase /** * Size */ - [[nodiscard]] constexpr std::size_t max_size() const noexcept { return static_max_size(); } - [[nodiscard]] constexpr std::size_t capacity() const noexcept { return max_size(); } + [[nodiscard]] constexpr std::size_t max_size() const noexcept FIXED_CONTAINERS_NONALLOCATING { return static_max_size(); } + [[nodiscard]] constexpr std::size_t capacity() const noexcept FIXED_CONTAINERS_NONALLOCATING { return max_size(); } constexpr void reserve(const std::size_t new_capacity, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { if (preconditions::test(new_capacity <= MAXIMUM_SIZE)) { @@ -571,17 +573,17 @@ class FixedVectorBase } // Do nothing } - [[nodiscard]] constexpr std::size_t size() const noexcept + [[nodiscard]] constexpr std::size_t size() const noexcept FIXED_CONTAINERS_NONALLOCATING { return IMPLEMENTATION_DETAIL_DO_NOT_USE_size_; } - [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; } + [[nodiscard]] constexpr bool empty() const noexcept FIXED_CONTAINERS_NONALLOCATING { return size() == 0; } /** * Equality. */ template - constexpr bool operator==(const FixedVectorBase& other) const + constexpr bool operator==(const FixedVectorBase& other) const noexcept FIXED_CONTAINERS_NONALLOCATING { if constexpr (MAXIMUM_SIZE == MAXIMUM_SIZE_2) { @@ -595,7 +597,7 @@ class FixedVectorBase } template - constexpr auto operator<=>(const FixedVectorBase& other) const + constexpr auto operator<=>(const FixedVectorBase& other) const noexcept FIXED_CONTAINERS_NONALLOCATING { return algorithm::lexicographical_compare_three_way( cbegin(), cend(), other.cbegin(), other.cend()); @@ -603,7 +605,7 @@ class FixedVectorBase private: constexpr iterator advance_all_after_iterator_by_n(const const_iterator pos, - const std::size_t n) + const std::size_t n) noexcept FIXED_CONTAINERS_NONALLOCATING { const std::ptrdiff_t value_count_to_move = std::distance(pos, cend()); increment_size(n); // Increment now so iterators are all within valid range @@ -622,7 +624,7 @@ class FixedVectorBase const_iterator pos, InputIt first, InputIt last, - const std_transition::source_location& loc) + const std_transition::source_location& loc) noexcept FIXED_CONTAINERS_NONALLOCATING { const auto entry_count_to_add = static_cast(std::distance(first, last)); check_target_size(size() + entry_count_to_add, loc); @@ -640,7 +642,7 @@ class FixedVectorBase const_iterator pos, InputIt first, InputIt last, - const std_transition::source_location& loc) + const std_transition::source_location& loc) noexcept FIXED_CONTAINERS_NONALLOCATING { auto first_it = const_to_mutable_it(pos); auto middle_it = end(); @@ -668,7 +670,7 @@ class FixedVectorBase return first_it; } - constexpr iterator create_iterator(const std::size_t offset_from_start) noexcept + constexpr iterator create_iterator(const std::size_t offset_from_start) noexcept FIXED_CONTAINERS_NONALLOCATING { // Not a pointer in all platforms, e.g msvc auto array_it = // NOLINT(readability-qualified-auto) @@ -677,7 +679,7 @@ class FixedVectorBase } [[nodiscard]] constexpr const_iterator create_const_iterator( - const std::size_t offset_from_start) const noexcept + const std::size_t offset_from_start) const noexcept FIXED_CONTAINERS_NONALLOCATING { // Not a pointer in all platforms, e.g msvc auto array_it = // NOLINT(readability-qualified-auto) @@ -686,19 +688,19 @@ class FixedVectorBase } private: - constexpr iterator const_to_mutable_it(const_iterator const_it) + constexpr iterator const_to_mutable_it(const_iterator const_it) noexcept FIXED_CONTAINERS_NONALLOCATING { return std::next(begin(), std::distance(cbegin(), const_it)); } - constexpr void check_not_full(const std_transition::source_location& loc) const + constexpr void check_not_full(const std_transition::source_location& loc) const noexcept FIXED_CONTAINERS_NONALLOCATING { if (preconditions::test(size() < MAXIMUM_SIZE)) { Checking::length_error(MAXIMUM_SIZE + 1, loc); } } - constexpr void check_not_empty(const std_transition::source_location& loc) const + constexpr void check_not_empty(const std_transition::source_location& loc) const noexcept FIXED_CONTAINERS_NONALLOCATING { if (preconditions::test(!empty())) { @@ -706,53 +708,53 @@ class FixedVectorBase } } - [[nodiscard]] constexpr std::size_t front_index() const { return 0; } - [[nodiscard]] constexpr std::size_t back_index() const { return end_index() - 1; } - [[nodiscard]] constexpr std::size_t end_index() const { return size(); } + [[nodiscard]] constexpr std::size_t front_index() const noexcept FIXED_CONTAINERS_NONALLOCATING { return 0; } + [[nodiscard]] constexpr std::size_t back_index() const noexcept FIXED_CONTAINERS_NONALLOCATING { return end_index() - 1; } + [[nodiscard]] constexpr std::size_t end_index() const noexcept FIXED_CONTAINERS_NONALLOCATING { return size(); } - [[nodiscard]] constexpr const Array& array() const + [[nodiscard]] constexpr const Array& array() const noexcept FIXED_CONTAINERS_NONALLOCATING { return IMPLEMENTATION_DETAIL_DO_NOT_USE_array_; } - constexpr Array& array() { return IMPLEMENTATION_DETAIL_DO_NOT_USE_array_; } + constexpr Array& array() noexcept FIXED_CONTAINERS_NONALLOCATING { return IMPLEMENTATION_DETAIL_DO_NOT_USE_array_; } - constexpr void increment_size(const std::size_t n = 1) + constexpr void increment_size(const std::size_t n = 1) noexcept FIXED_CONTAINERS_NONALLOCATING { IMPLEMENTATION_DETAIL_DO_NOT_USE_size_ += n; } - constexpr void decrement_size(const std::size_t n = 1) + constexpr void decrement_size(const std::size_t n = 1) noexcept FIXED_CONTAINERS_NONALLOCATING { IMPLEMENTATION_DETAIL_DO_NOT_USE_size_ -= n; } - constexpr void set_size(const std::size_t size) + constexpr void set_size(const std::size_t size) noexcept FIXED_CONTAINERS_NONALLOCATING { IMPLEMENTATION_DETAIL_DO_NOT_USE_size_ = size; } - [[nodiscard]] constexpr const T& unchecked_at(const std::size_t index) const + [[nodiscard]] constexpr const T& unchecked_at(const std::size_t index) const noexcept FIXED_CONTAINERS_NONALLOCATING { return optional_storage_detail::get(array()[index]); } - constexpr T& unchecked_at(const std::size_t index) + constexpr T& unchecked_at(const std::size_t index) noexcept FIXED_CONTAINERS_NONALLOCATING { return optional_storage_detail::get(array()[index]); } - constexpr void destroy_at(std::size_t /*index*/) + constexpr void destroy_at(std::size_t /*index*/) noexcept FIXED_CONTAINERS_NONALLOCATING requires TriviallyDestructible { } - constexpr void destroy_at(std::size_t index) + constexpr void destroy_at(std::size_t index) noexcept FIXED_CONTAINERS_NONALLOCATING requires NotTriviallyDestructible { memory::destroy_at_address_of(unchecked_at(index)); } - constexpr void destroy_range(iterator /*first*/, iterator /*last*/) + constexpr void destroy_range(iterator /*first*/, iterator /*last*/) noexcept FIXED_CONTAINERS_NONALLOCATING requires TriviallyDestructible { } - constexpr void destroy_range(iterator first, iterator last) + constexpr void destroy_range(iterator first, iterator last) noexcept FIXED_CONTAINERS_NONALLOCATING requires NotTriviallyDestructible { for (; first != last; ++first) @@ -761,30 +763,30 @@ class FixedVectorBase } } - constexpr void place_at(const std::size_t index, const value_type& value) + constexpr void place_at(const std::size_t index, const value_type& value) noexcept FIXED_CONTAINERS_NONALLOCATING { memory::construct_at_address_of(unchecked_at(index), value); } - constexpr void place_at(const std::size_t index, value_type&& value) + constexpr void place_at(const std::size_t index, value_type&& value) noexcept FIXED_CONTAINERS_NONALLOCATING { memory::construct_at_address_of(unchecked_at(index), std::move(value)); } template - constexpr void emplace_at(const std::size_t index, Args&&... args) + constexpr void emplace_at(const std::size_t index, Args&&... args) noexcept FIXED_CONTAINERS_NONALLOCATING { memory::construct_at_address_of(unchecked_at(index), std::forward(args)...); } // [WORKAROUND-1] - Needed by the non-trivially-copyable flavor of FixedVector protected: - constexpr void push_back_internal(const value_type& value) + constexpr void push_back_internal(const value_type& value) noexcept FIXED_CONTAINERS_NONALLOCATING { place_at(end_index(), value); increment_size(); } - constexpr void push_back_internal(value_type&& value) + constexpr void push_back_internal(value_type&& value) noexcept FIXED_CONTAINERS_NONALLOCATING { place_at(end_index(), std::move(value)); increment_size(); @@ -804,20 +806,20 @@ class FixedVector : public fixed_vector_detail::FixedVectorBase>; - constexpr FixedVector() noexcept + constexpr FixedVector() noexcept FIXED_CONTAINERS_NONALLOCATING : Base() { } constexpr FixedVector(std::size_t count, const T& value, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : Base(count, value, loc) { } constexpr explicit FixedVector(std::size_t count, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : Base(count, loc) { } @@ -825,35 +827,35 @@ class FixedVector : public fixed_vector_detail::FixedVectorBase list, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : Base(list, loc) { } - constexpr FixedVector(const FixedVector& other) + constexpr FixedVector(const FixedVector& other) FIXED_CONTAINERS_NONALLOCATING requires TriviallyCopyConstructible = default; - constexpr FixedVector(FixedVector&& other) noexcept + constexpr FixedVector(FixedVector&& other) noexcept FIXED_CONTAINERS_NONALLOCATING requires TriviallyMoveConstructible = default; - constexpr FixedVector& operator=(const FixedVector& other) + constexpr FixedVector& operator=(const FixedVector& other) noexcept FIXED_CONTAINERS_NONALLOCATING requires TriviallyCopyAssignable = default; - constexpr FixedVector& operator=(FixedVector&& other) noexcept + constexpr FixedVector& operator=(FixedVector&& other) noexcept FIXED_CONTAINERS_NONALLOCATING requires TriviallyMoveAssignable = default; - constexpr FixedVector(const FixedVector& other) + constexpr FixedVector(const FixedVector& other) noexcept FIXED_CONTAINERS_NONALLOCATING : FixedVector(other.begin(), other.end()) { } - constexpr FixedVector(FixedVector&& other) noexcept + constexpr FixedVector(FixedVector&& other) noexcept FIXED_CONTAINERS_NONALLOCATING : FixedVector() { for (T& entry : other) @@ -865,7 +867,7 @@ class FixedVector : public fixed_vector_detail::FixedVectorBaseassign(other.begin(), other.end()); return *this; } - constexpr FixedVector& operator=(FixedVector&& other) noexcept + constexpr FixedVector& operator=(FixedVector&& other) noexcept FIXED_CONTAINERS_NONALLOCATING { if (this == &other) { @@ -909,20 +911,20 @@ class FixedVector using Builder = fixed_vector_detail::FixedVectorBuilder>; - constexpr FixedVector() noexcept + constexpr FixedVector() noexcept FIXED_CONTAINERS_NONALLOCATING : Base() { } constexpr FixedVector(std::size_t count, const T& value, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : Base(count, value, loc) { } constexpr explicit FixedVector(std::size_t count, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : Base(count, loc) { } @@ -930,13 +932,13 @@ class FixedVector constexpr FixedVector(InputIt first, InputIt last, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : Base(first, last, loc) { } constexpr FixedVector(std::initializer_list list, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : Base(list, loc) { } @@ -967,26 +969,26 @@ class FixedVector using Builder = fixed_vector_detail::FixedVectorBuilder>; - constexpr FixedVector() noexcept + constexpr FixedVector() noexcept FIXED_CONTAINERS_NONALLOCATING : Base() { } constexpr FixedVector(std::initializer_list list, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : Base(list, loc) { } constexpr FixedVector(std::size_t count, const T& value, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : Base(count, value, loc) { } constexpr explicit FixedVector(std::size_t count, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : Base(count, loc) { } @@ -994,21 +996,21 @@ class FixedVector constexpr FixedVector(InputIt first, InputIt last, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING : Base(first, last, loc) { } }; template -[[nodiscard]] constexpr bool is_full(const FixedVector& container) +[[nodiscard]] constexpr bool is_full(const FixedVector& container) noexcept FIXED_CONTAINERS_NONALLOCATING { return container.size() >= container.max_size(); } template constexpr typename FixedVector::size_type erase( - FixedVector& container, const U& value) + FixedVector& container, const U& value) noexcept FIXED_CONTAINERS_NONALLOCATING { const auto original_size = container.size(); container.erase(std::remove(container.begin(), container.end(), value), container.end()); @@ -1017,7 +1019,7 @@ constexpr typename FixedVector::size_type erase( template constexpr typename FixedVector::size_type erase_if( - FixedVector& container, Predicate predicate) + FixedVector& container, Predicate predicate) noexcept FIXED_CONTAINERS_NONALLOCATING { const auto original_size = container.size(); container.erase(std::remove_if(container.begin(), container.end(), predicate), container.end()); @@ -1036,7 +1038,7 @@ template /*list*/, const std_transition::source_location& /*loc*/ - = std_transition::source_location::current()) noexcept + = std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { return {}; } @@ -1055,7 +1057,7 @@ template [[nodiscard]] constexpr auto make_fixed_vector( const T (&list)[MAXIMUM_SIZE], const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { using CheckingType = customize::SequenceContainerAbortChecking; using FixedVectorType = FixedVector; @@ -1065,7 +1067,7 @@ template [[nodiscard]] constexpr auto make_fixed_vector( const std::array list, const std_transition::source_location& loc = - std_transition::source_location::current()) noexcept + std_transition::source_location::current()) noexcept FIXED_CONTAINERS_NONALLOCATING { using CheckingType = customize::SequenceContainerAbortChecking; using FixedVectorType = FixedVector; diff --git a/include/fixed_containers/memory.hpp b/include/fixed_containers/memory.hpp index 377da514..d734858c 100644 --- a/include/fixed_containers/memory.hpp +++ b/include/fixed_containers/memory.hpp @@ -1,5 +1,7 @@ #pragma once +#include "fixed_containers/compiler_compat.hpp" + #include namespace fixed_containers::memory @@ -12,35 +14,37 @@ namespace fixed_containers::memory // There appears to be more, to be investigated. // Returning an explicit `T*` also fails in certain cases (msvc). // As a workaround, don't return anything, which is a minor divergence with `std::construct_at()`. +// We know our invocations of std::construct_at are non-allocating. Suppress function effects warnings. template -constexpr void construct_at_address_of(T& ref, Args&&... args) +constexpr void construct_at_address_of(T& ref, Args&&... args) noexcept FIXED_CONTAINERS_NONALLOCATING { - std::construct_at(std::addressof(ref), std::forward(args)...); + FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(std::construct_at(std::addressof(ref), std::forward(args)...)); } // Similar to https://en.cppreference.com/w/cpp/memory/destroy_at // but uses references and correctly handles types that overload operator& +// std::destroy_at is not allocating template -constexpr void destroy_at_address_of(T& ref) +constexpr void destroy_at_address_of(T& ref) noexcept FIXED_CONTAINERS_NONALLOCATING { - std::destroy_at(std::addressof(ref)); + FIXED_CONTAINERS_SUPPRESS_FUNCTION_EFFECTS(std::destroy_at(std::addressof(ref))); } // Preferred over `existing_value = {arg1, arg2};` template -constexpr void destroy_and_construct_at_address_of(T& ref, Args&&... args) +constexpr void destroy_and_construct_at_address_of(T& ref, Args&&... args) noexcept FIXED_CONTAINERS_NONALLOCATING { destroy_at_address_of(ref); construct_at_address_of(ref, std::forward(args)...); } template -const std::byte* addressof_as_const_byte_ptr(T& ref) +const std::byte* addressof_as_const_byte_ptr(T& ref) noexcept FIXED_CONTAINERS_NONALLOCATING { return reinterpret_cast(std::addressof(ref)); } template -std::byte* addressof_as_mutable_byte_ptr(T& ref) +std::byte* addressof_as_mutable_byte_ptr(T& ref) noexcept FIXED_CONTAINERS_NONALLOCATING { return reinterpret_cast(std::addressof(ref)); }