From 38bbebe9cdf33bdda7e4c0b56e2659e9b644bb8c Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski Date: Wed, 10 Jan 2024 04:28:25 -0500 Subject: [PATCH 1/8] Add build directory and CMake preset files to .gitignore --- .gitignore | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 74f33d3..9006ccd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,15 @@ bin/ bin64/ +build/ # Because of CMake and VS2017 Win32/ x64/ .vs/ -out/ \ No newline at end of file +out/ + +# VS CMake settings +/CMakeSettings.json +# CMake presets +/CMakePresets.json +/CMakeUserPresets.json From 7e720a959baa3d612f810102086d659d1e5f32c7 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski Date: Wed, 10 Jan 2024 07:29:27 -0500 Subject: [PATCH 2/8] Make basic_static_string usable as a NTTP --- include/boost/static_string/static_string.hpp | 232 +++++++++++------- test/compile_fail.hpp | 10 - test/constexpr_tests.hpp | 99 ++++++-- 3 files changed, 216 insertions(+), 125 deletions(-) diff --git a/include/boost/static_string/static_string.hpp b/include/boost/static_string/static_string.hpp index bde7b7a..b350a98 100644 --- a/include/boost/static_string/static_string.hpp +++ b/include/boost/static_string/static_string.hpp @@ -314,103 +314,125 @@ copy_with_traits( template class static_string_base { -private: + using derived_type = basic_static_string; + friend derived_type; + using size_type = smallest_width; using value_type = typename Traits::char_type; using pointer = value_type*; using const_pointer = const value_type*; -public: - BOOST_STATIC_STRING_CPP11_CONSTEXPR - static_string_base() noexcept { }; - BOOST_STATIC_STRING_CPP14_CONSTEXPR - pointer - data_impl() noexcept + struct size { - return data_; - } + class basic_static_string + { + friend derived_type; - BOOST_STATIC_STRING_CPP14_CONSTEXPR - const_pointer - data_impl() const noexcept - { - return data_; - } + BOOST_STATIC_STRING_CPP11_CONSTEXPR + size_type + size_impl() const noexcept + { + return size; + } - BOOST_STATIC_STRING_CPP11_CONSTEXPR - std::size_t - size_impl() const noexcept - { - return size_; - } + BOOST_STATIC_STRING_CPP14_CONSTEXPR + size_type + size_impl(std::size_t n) noexcept + { + // Functions that set size will throw + // if the new size would exceed max_size() + // therefore we can guarantee that this will + // not lose data. + return size = static_cast(n); + } - BOOST_STATIC_STRING_CPP14_CONSTEXPR - std::size_t - set_size(std::size_t n) noexcept - { - // Functions that set size will throw - // if the new size would exceed max_size() - // therefore we can guarantee that this will - // not lose data. - return size_ = size_type(n); - } + public: + size_type size = 0; + }; + }; - BOOST_STATIC_STRING_CPP14_CONSTEXPR - void - term_impl() noexcept + struct data { - Traits::assign(data_[size_], value_type()); - } + class basic_static_string + { + friend derived_type; - size_type size_ = 0; + BOOST_STATIC_STRING_CPP14_CONSTEXPR + pointer + data_impl() noexcept + { + return data; + } - value_type data_[N + 1]{}; + BOOST_STATIC_STRING_CPP11_CONSTEXPR + const_pointer + data_impl() const noexcept + { + return data; + } + + public: + value_type data[N + 1]{}; + }; + }; }; // Optimization for when the size is 0 template class static_string_base<0, CharT, Traits> { -private: + using derived_type = basic_static_string<0, CharT, Traits>; + friend derived_type; + + using size_type = std::size_t; using value_type = typename Traits::char_type; using pointer = value_type*; -public: - BOOST_STATIC_STRING_CPP11_CONSTEXPR - static_string_base() noexcept { } - // Modifying the null terminator is UB - BOOST_STATIC_STRING_CPP11_CONSTEXPR - pointer - data_impl() const noexcept + struct size { - return const_cast(&null_); - } + class basic_static_string + { + friend derived_type; - BOOST_STATIC_STRING_CPP11_CONSTEXPR - std::size_t - size_impl() const noexcept - { - return 0; - } + BOOST_STATIC_STRING_CPP11_CONSTEXPR + size_type + size_impl() const noexcept + { + return 0; + } - BOOST_STATIC_STRING_CPP11_CONSTEXPR - std::size_t - set_size(std::size_t) const noexcept + BOOST_STATIC_STRING_CPP11_CONSTEXPR + size_type + size_impl(std::size_t) const noexcept + { + return 0; + } + }; + }; + + struct data { - return 0; - } + class basic_static_string + { + friend derived_type; - BOOST_STATIC_STRING_CPP14_CONSTEXPR - void - term_impl() const noexcept { } + BOOST_STATIC_STRING_CPP11_CONSTEXPR + pointer + data_impl() const noexcept + { + return const_cast(&data); + } -private: - static constexpr const value_type null_{}; + public: + static constexpr value_type data{}; + }; + }; }; // This is only needed in C++14 and lower. // see http://eel.is/c++draft/depr.static.constexpr #ifndef BOOST_STATIC_STRING_CPP17 +#if 0 template constexpr const @@ -419,6 +441,13 @@ static_string_base<0, CharT, Traits>:: null_; #endif +template +constexpr +typename static_string_base<0, CharT, Traits>::value_type +static_string_base<0, CharT, Traits>::data::basic_static_string::data; +#endif + + template BOOST_STATIC_STRING_CPP14_CONSTEXPR inline @@ -1054,7 +1083,11 @@ template> class basic_static_string #ifndef BOOST_STATIC_STRING_DOCS - : private detail::static_string_base + // : public detail::static_string_base + : public detail::static_string_base< + N, CharT, Traits>::size::basic_static_string + , public detail::static_string_base< + N, CharT, Traits>::data::basic_static_string #endif { private: @@ -2339,7 +2372,7 @@ class basic_static_string void clear() noexcept { - this->set_size(0); + this->size_impl(0); term(); } @@ -2931,7 +2964,7 @@ class basic_static_string pop_back() noexcept { BOOST_STATIC_STRING_ASSERT(!empty()); - this->set_size(size() - 1); + this->size_impl(size() - 1); term(); } @@ -3107,7 +3140,7 @@ class basic_static_string InputIterator first, InputIterator last) { - this->set_size(size() + read_back(true, first, last)); + this->size_impl(size() + read_back(true, first, last)); return term(); } @@ -5584,11 +5617,22 @@ class basic_static_string } private: + BOOST_STATIC_STRING_CPP14_CONSTEXPR + void term_impl(std::true_type) noexcept + { + traits_type::assign(data()[size()], value_type()); + } + + BOOST_STATIC_STRING_CPP14_CONSTEXPR + void term_impl(std::false_type) noexcept + { + } + BOOST_STATIC_STRING_CPP14_CONSTEXPR basic_static_string& term() noexcept { - this->term_impl(); + term_impl(std::integral_constant()); return *this; } @@ -5596,7 +5640,7 @@ class basic_static_string basic_static_string& assign_char(value_type ch, std::true_type) noexcept { - this->set_size(1); + this->size_impl(1); traits_type::assign(data()[0], ch); return term(); } @@ -5670,7 +5714,7 @@ class basic_static_string const_pointer s, size_type count) noexcept { - this->set_size(count); + this->size_impl(count); traits_type::copy(data(), s, size() + 1); return *this; } @@ -6591,7 +6635,7 @@ assign( if (count > max_size()) detail::throw_exception( "count > max_size()"); - this->set_size(count); + this->size_impl(count); traits_type::assign(data(), size(), ch); return term(); } @@ -6608,7 +6652,7 @@ assign( if (count > max_size()) detail::throw_exception( "count > max_size()"); - this->set_size(count); + this->size_impl(count); traits_type::move(data(), s, size()); return term(); } @@ -6630,13 +6674,13 @@ assign( { if (i >= max_size()) { - this->set_size(i); + this->size_impl(i); term(); detail::throw_exception("n > max_size()"); } traits_type::assign(*ptr, *first); } - this->set_size(ptr - data()); + this->size_impl(ptr - data()); return term(); } @@ -6658,7 +6702,7 @@ insert( const auto index = pos - curr_data; traits_type::move(&curr_data[index + count], &curr_data[index], curr_size - index + 1); traits_type::assign(&curr_data[index], count, ch); - this->set_size(curr_size + count); + this->size_impl(curr_size + count); return &curr_data[index]; } @@ -6711,7 +6755,7 @@ insert( traits_type::copy(dest, src, count); } } - this->set_size(curr_size + count); + this->size_impl(curr_size + count); return curr_data + index; } @@ -6735,7 +6779,7 @@ insert( const auto count = read_back(false, first, last); const std::size_t index = pos - curr_data; std::rotate(&curr_data[index], &curr_data[curr_size + 1], &curr_data[curr_size + count + 1]); - this->set_size(curr_size + count); + this->size_impl(curr_size + count); return curr_data + index; } @@ -6751,7 +6795,7 @@ erase( const auto curr_data = data(); const std::size_t index = first - curr_data; traits_type::move(&curr_data[index], last, (end() - last) + 1); - this->set_size(size() - std::size_t(last - first)); + this->size_impl(size() - std::size_t(last - first)); return curr_data + index; } @@ -6767,7 +6811,7 @@ push_back( detail::throw_exception( "curr_size >= max_size()"); traits_type::assign(data()[curr_size], ch); - this->set_size(curr_size + 1); + this->size_impl(curr_size + 1); term(); } @@ -6785,7 +6829,7 @@ append( detail::throw_exception( "count > max_size() - size()"); traits_type::assign(end(), count, ch); - this->set_size(curr_size + count); + this->size_impl(curr_size + count); return term(); } @@ -6803,7 +6847,7 @@ append( detail::throw_exception( "count > max_size() - size()"); traits_type::copy(end(), s, count); - this->set_size(curr_size + count); + this->size_impl(curr_size + count); return term(); } @@ -6819,7 +6863,7 @@ resize(size_type n, value_type c) const auto curr_size = size(); if(n > curr_size) traits_type::assign(data() + curr_size, n - curr_size, c); - this->set_size(n); + this->size_impl(n); term(); } @@ -6839,7 +6883,7 @@ resize_and_overwrite( CharT* p = data(); const auto new_size = std::move(op)(p, n); BOOST_STATIC_STRING_ASSERT(new_size >= 0 && size_type(new_size) <= n); - this->set_size(size_type(new_size)); + this->size_impl(size_type(new_size)); term(); } @@ -6851,9 +6895,9 @@ swap(basic_static_string& s) noexcept { const auto curr_size = size(); basic_static_string tmp(s); - s.set_size(curr_size); + s.size_impl(curr_size); traits_type::copy(&s.data()[0], data(), curr_size + 1); - this->set_size(tmp.size()); + this->size_impl(tmp.size()); traits_type::copy(data(), tmp.data(), size() + 1); } @@ -6872,10 +6916,8 @@ swap(basic_static_string& s) detail::throw_exception( "s.size() > max_size()"); basic_static_string tmp(s); - s.set_size(curr_size); - traits_type::copy(&s.data()[0], data(), curr_size + 1); - this->set_size(tmp.size()); - traits_type::copy(data(), &tmp.data()[0], size() + 1); + s.assign_unchecked(data(), curr_size); + assign_unchecked(tmp.data(), tmp.size()); } template @@ -6898,7 +6940,7 @@ replace( const auto pos = i1 - curr_data; traits_type::move(&curr_data[pos + n], i2, (end() - i2) + 1); traits_type::assign(&curr_data[pos], n, c); - this->set_size((curr_size - n1) + n); + this->size_impl((curr_size - n1) + n); return *this; } @@ -6959,7 +7001,7 @@ replace( traits_type::move(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1); } } - this->set_size((curr_size - n1) + n2); + this->size_impl((curr_size - n1) + n2); return *this; } @@ -6991,7 +7033,7 @@ replace( // Move everything from the end of the splice point to the end of the rotated string to // the begining of the splice point traits_type::move(&curr_data[pos + n2], &curr_data[pos + n2 + n1], ((curr_size - n1) + n2) - pos); - this->set_size((curr_size - n1) + n2); + this->size_impl((curr_size - n1) + n2); return *this; } @@ -7163,7 +7205,7 @@ replace_unchecked( "replaced string exceeds max_size()"); traits_type::move(&curr_data[pos + n2], i2, (end() - i2) + 1); traits_type::copy(&curr_data[pos], s, n2); - this->set_size((curr_size - n1) + n2); + this->size_impl((curr_size - n1) + n2); return *this; } @@ -7185,7 +7227,7 @@ insert_unchecked( const std::size_t index = pos - curr_data; traits_type::move(&curr_data[index + count], pos, (end() - pos) + 1); traits_type::copy(&curr_data[index], s, count); - this->set_size(curr_size + count); + this->size_impl(curr_size + count); return curr_data + index; } diff --git a/test/compile_fail.hpp b/test/compile_fail.hpp index d0d0dfd..03510f1 100644 --- a/test/compile_fail.hpp +++ b/test/compile_fail.hpp @@ -4,16 +4,6 @@ namespace boost { namespace static_strings { -static_assert(std::is_base_of< - detail::static_string_base<0, char, std::char_traits>, - static_string<0>>::value, - "the zero size optimization shall be used for N = 0"); - -static_assert(std::is_base_of< - detail::static_string_base<(std::numeric_limits::max)() + 1, char, std::char_traits>, - static_string<(std::numeric_limits::max)() + 1>>::value, - "the minimum size type optimization shall be used for N > 0"); - static_assert(!detail::is_input_iterator::value, "is_input_iterator is incorrect"); static_assert(!detail::is_input_iterator::value, "is_input_iterator is incorrect"); static_assert(detail::is_input_iterator::value, "is_input_iterator is incorrect"); diff --git a/test/constexpr_tests.hpp b/test/constexpr_tests.hpp index 7c6ab12..c748ec7 100644 --- a/test/constexpr_tests.hpp +++ b/test/constexpr_tests.hpp @@ -22,38 +22,74 @@ struct cxper_char_traits using int_type = int; using state_type = std::mbstate_t; - static constexpr void assign(char_type& a, const char_type& b) noexcept { a = b; } - static constexpr bool eq(char_type a, char_type b) noexcept { return a == b; } - static constexpr bool lt(char_type a, char_type b) noexcept { return a < b; } + static constexpr void assign(char_type& a, const char_type& b) noexcept + { + a = b; + } + + static constexpr bool eq(char_type a, char_type b) noexcept + { + return a == b; + } + + static constexpr bool lt(char_type a, char_type b) noexcept + { + return a < b; + } + + static constexpr int compare(const char_type* a, const char_type* b, std::size_t n) + { + for (; n--; ++a, ++b) + { + if(lt(*a, *b)) + return 1; + else if(lt(*b, *a)) + return -1; + } + return 0; + } - static constexpr int compare(const char_type*, const char_type*, std::size_t) { return 0; } static constexpr std::size_t length(const char_type* s) { - std::size_t n = 0; - while (*(s++)); - return n; + auto ptr = s; + while (!eq(*ptr, char_type())) + ++ptr; + return ptr - s; } - static constexpr const char_type* find(const char_type*, std::size_t, const char_type&){ return 0; } + + static constexpr const char_type* find(const char_type* s, std::size_t n, const char_type& ch) + { + for (; n--; ++s) + { + if (eq(*s, ch)) + return s; + } + return nullptr; + } + static constexpr char_type* move(char_type* dest, const char_type* src, std::size_t n) { - const auto temp = dest; - while (n--) - *(dest++) = *(src++); - return temp; + if (detail::ptr_in_range(src, src + n, dest)) + { + while (n--) + assign(dest[n], src[n]); + return dest; + } + return copy(dest, src, n); } + static constexpr char_type* copy(char_type* dest, const char_type* src, std::size_t n) { - const auto temp = dest; - while (n--) - *(dest++) = *(src++); - return temp; + for (auto ptr = dest; n--;) + assign(*ptr++, *src++); + return dest; } + static constexpr char_type* assign(char_type* dest, std::size_t n, char_type ch) { - const auto temp = dest; - while (n--) - *(dest++) = ch; - return temp; + for (auto ptr = dest; n--;) + assign(*ptr++, ch); + return dest; } }; #else @@ -623,5 +659,28 @@ testConstantEvaluation() cstatic_string().empty(); #endif } + +#ifdef BOOST_STATIC_STRING_CPP20 + +template X> +struct nttp_primary +{ + static constexpr bool value = false; +}; + +template<> +struct nttp_primary<"test string"> +{ + static constexpr bool value = true; +}; + +static_assert(!nttp_primary<"random string">::value, + "structural equality broken"); + +static_assert(nttp_primary<"test string">::value, + "structural equality broken"); + +#endif + } // static_strings } // boost From 094337be46510470c6d3cbfa84eeafe6e0c84cd6 Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Wed, 17 Dec 2025 12:21:18 +0100 Subject: [PATCH 3/8] Work around a bug in GCC 5-10 GCC 5-10 incorrectly complain about our nested classes being private. So, make them public. --- include/boost/static_string/config.hpp | 7 +++++++ include/boost/static_string/static_string.hpp | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/include/boost/static_string/config.hpp b/include/boost/static_string/config.hpp index 5af2b13..35dcdec 100644 --- a/include/boost/static_string/config.hpp +++ b/include/boost/static_string/config.hpp @@ -279,4 +279,11 @@ using basic_string_view = #define BOOST_STATIC_STRING_USE_STD_FORMAT #endif +#if defined(__GNUC__) && (__GNUC__ >= 5) && (__GNUC__ <= 10) && !defined(__clang__) +// Workaround for GCC complaining about nested classes being private. +#define BOOST_STATIC_STRING_GCC_NESTED_CLASS_WORKAROUND public: +#else +#define BOOST_STATIC_STRING_GCC_NESTED_CLASS_WORKAROUND +#endif + #endif diff --git a/include/boost/static_string/static_string.hpp b/include/boost/static_string/static_string.hpp index b350a98..75556d3 100644 --- a/include/boost/static_string/static_string.hpp +++ b/include/boost/static_string/static_string.hpp @@ -322,6 +322,8 @@ class static_string_base using pointer = value_type*; using const_pointer = const value_type*; +BOOST_STATIC_STRING_GCC_NESTED_CLASS_WORKAROUND + struct size { class basic_static_string @@ -388,6 +390,8 @@ class static_string_base<0, CharT, Traits> using value_type = typename Traits::char_type; using pointer = value_type*; +BOOST_STATIC_STRING_GCC_NESTED_CLASS_WORKAROUND + struct size { class basic_static_string From f18c0b25609b09bc4febaa73cbbc0b7df4f2a13b Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Wed, 17 Dec 2025 12:47:20 +0100 Subject: [PATCH 4/8] Replace the implementation of cxper_char_traits::move() with a simpler one Reason: See the new code comment. --- test/constexpr_tests.hpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/constexpr_tests.hpp b/test/constexpr_tests.hpp index c748ec7..ca72ccc 100644 --- a/test/constexpr_tests.hpp +++ b/test/constexpr_tests.hpp @@ -69,12 +69,15 @@ struct cxper_char_traits static constexpr char_type* move(char_type* dest, const char_type* src, std::size_t n) { - if (detail::ptr_in_range(src, src + n, dest)) - { - while (n--) - assign(dest[n], src[n]); - return dest; - } + // This implementation does not handle overlapping ranges where + // dest > src. A correct implementation would need to detect this + // case and copy backwards, but detecting overlap requires pointer + // comparisons that many of the tested compiles (incorrectly) refuse + // in constant expressions. + // + // Since cxper_char_traits is only used for testing constexpr + // functionality and the tests do not exercise overlapping moves + // where dest > src, this simple forward copy is sufficient. return copy(dest, src, n); } From 4c312fc049821591eeabcf4e254e5dcff275a4f1 Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Wed, 17 Dec 2025 14:51:19 +0100 Subject: [PATCH 5/8] Work around GCC 9 rejecting a legitimate pointer comparison in a constexpr context --- include/boost/static_string/config.hpp | 7 +++++++ test/constexpr_tests.hpp | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/include/boost/static_string/config.hpp b/include/boost/static_string/config.hpp index 35dcdec..c9570cb 100644 --- a/include/boost/static_string/config.hpp +++ b/include/boost/static_string/config.hpp @@ -286,4 +286,11 @@ using basic_string_view = #define BOOST_STATIC_STRING_GCC_NESTED_CLASS_WORKAROUND #endif +// GCC 9 incorrectly rejects the pointer equality comparison in +// ptr_in_range() in constant expressions. GCC 10 and later handle +// it correctly. +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ == 9) +#define BOOST_STATIC_STRING_CONSTEXPR_PTR_CMP_BROKEN +#endif + #endif diff --git a/test/constexpr_tests.hpp b/test/constexpr_tests.hpp index ca72ccc..f541183 100644 --- a/test/constexpr_tests.hpp +++ b/test/constexpr_tests.hpp @@ -255,7 +255,8 @@ testConstantEvaluation() a.replace(a.begin(), a.end(), a.begin(), a.end()); a.replace(a.begin(), a.end(), {'a'}); -#ifdef BOOST_STATIC_STRING_IS_CONST_EVAL +#if defined(BOOST_STATIC_STRING_IS_CONST_EVAL) \ + && !defined(BOOST_STATIC_STRING_CONSTEXPR_PTR_CMP_BROKEN) a.clear(); a.replace(a.begin(), a.end(), "a"); a.replace(a.begin(), a.end(), "a", 1); @@ -443,7 +444,8 @@ testConstantEvaluation() a.replace(a.begin(), a.end(), a.begin(), a.end()); a.replace(a.begin(), a.end(), {'a'}); -#ifdef BOOST_STATIC_STRING_IS_CONST_EVAL +#if defined(BOOST_STATIC_STRING_IS_CONST_EVAL) \ + && !defined(BOOST_STATIC_STRING_CONSTEXPR_PTR_CMP_BROKEN) a.clear(); a.replace(a.begin(), a.end(), "a"); a.replace(a.begin(), a.end(), "a", 1); From 6d2e7c98abad4055e4fb0ad7600537ab6e7d974c Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Wed, 17 Dec 2025 16:05:21 +0100 Subject: [PATCH 6/8] Apply the workaround in the previous commit to Clang 3.7 and 9-19, too Reason: They have the same issue as GCC 9. --- include/boost/static_string/config.hpp | 5 ++++- test/constexpr_tests.hpp | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/boost/static_string/config.hpp b/include/boost/static_string/config.hpp index c9570cb..e7c155a 100644 --- a/include/boost/static_string/config.hpp +++ b/include/boost/static_string/config.hpp @@ -289,7 +289,10 @@ using basic_string_view = // GCC 9 incorrectly rejects the pointer equality comparison in // ptr_in_range() in constant expressions. GCC 10 and later handle // it correctly. -#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ == 9) +// +// Clang 3.7 and 9-19 have the same issue. +#if (defined(__GNUC__) && !defined(__clang__) && (__GNUC__ == 9)) \ + || (defined(__clang__) && ((__clang_major__ == 3 && __clang_minor__ == 7) || ((__clang_major__ >= 9) && (__clang_major__ <= 19)))) #define BOOST_STATIC_STRING_CONSTEXPR_PTR_CMP_BROKEN #endif diff --git a/test/constexpr_tests.hpp b/test/constexpr_tests.hpp index f541183..536a463 100644 --- a/test/constexpr_tests.hpp +++ b/test/constexpr_tests.hpp @@ -617,7 +617,8 @@ testConstantEvaluation() a.replace(a.begin(), a.end(), a.begin(), a.end()); a.replace(a.begin(), a.end(), {'a'}); -#ifdef BOOST_STATIC_STRING_IS_CONST_EVAL +#if defined(BOOST_STATIC_STRING_IS_CONST_EVAL) \ + && !defined(BOOST_STATIC_STRING_CONSTEXPR_PTR_CMP_BROKEN) a.clear(); a.replace(a.begin(), a.end(), "a"); a.replace(a.begin(), a.end(), "a", 1); From 328752ed8ce4de16eddba6ee1ac00a4e8a9d7eba Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Wed, 17 Dec 2025 19:30:59 +0100 Subject: [PATCH 7/8] Condition the NTTP tests on __cpp_nontype_template_args They were conditioned on detection of C++20 via __cplusplus, but Clang 10 and 11 don't support class types as NTTP, even though they report C++20 via __cplusplus when -std=c++20 is used. --- test/constexpr_tests.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/constexpr_tests.hpp b/test/constexpr_tests.hpp index 536a463..71ca3af 100644 --- a/test/constexpr_tests.hpp +++ b/test/constexpr_tests.hpp @@ -666,7 +666,7 @@ testConstantEvaluation() #endif } -#ifdef BOOST_STATIC_STRING_CPP20 +#if __cpp_nontype_template_args >= 201911L template X> struct nttp_primary From 321145675c4b3b9ab44d9e92449c4daa6bdfe926 Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Thu, 18 Dec 2025 10:35:43 +0100 Subject: [PATCH 8/8] Work around a Clang 3.7 bug affecting constexpr insert() The iterator-based insert(const_iterator, size_type, value_type) function relies on traits_type::move() to shift the existing null terminator to its new position. Clang 3.7's constexpr evaluator does not handle this correctly, causing the following test to fail: static_string<3>{"ab"}.insert(2, 1, 'c') == "abc" Add an explicit term() call, guarded by a preprocessor conditional for Clang 3.7, to ensure proper null termination. --- include/boost/static_string/static_string.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/boost/static_string/static_string.hpp b/include/boost/static_string/static_string.hpp index 75556d3..6b4d3d0 100644 --- a/include/boost/static_string/static_string.hpp +++ b/include/boost/static_string/static_string.hpp @@ -6707,6 +6707,9 @@ insert( traits_type::move(&curr_data[index + count], &curr_data[index], curr_size - index + 1); traits_type::assign(&curr_data[index], count, ch); this->size_impl(curr_size + count); +#if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ == 7 + term(); +#endif return &curr_data[index]; }