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 diff --git a/include/boost/static_string/config.hpp b/include/boost/static_string/config.hpp index 5af2b13..e7c155a 100644 --- a/include/boost/static_string/config.hpp +++ b/include/boost/static_string/config.hpp @@ -279,4 +279,21 @@ 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 + +// GCC 9 incorrectly rejects the pointer equality comparison in +// ptr_in_range() in constant expressions. GCC 10 and later handle +// it correctly. +// +// 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 + #endif diff --git a/include/boost/static_string/static_string.hpp b/include/boost/static_string/static_string.hpp index bde7b7a..6b4d3d0 100644 --- a/include/boost/static_string/static_string.hpp +++ b/include/boost/static_string/static_string.hpp @@ -314,103 +314,129 @@ 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 - { - return data_; - } +BOOST_STATIC_STRING_GCC_NESTED_CLASS_WORKAROUND - BOOST_STATIC_STRING_CPP14_CONSTEXPR - const_pointer - data_impl() const noexcept + struct size { - return data_; - } + class basic_static_string + { + friend derived_type; - BOOST_STATIC_STRING_CPP11_CONSTEXPR - std::size_t - size_impl() const noexcept - { - return size_; - } + BOOST_STATIC_STRING_CPP11_CONSTEXPR + size_type + size_impl() const noexcept + { + return size; + } - 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); - } + 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 - void - term_impl() noexcept + public: + size_type size = 0; + }; + }; + + struct data { - Traits::assign(data_[size_], value_type()); - } + class basic_static_string + { + friend derived_type; + + BOOST_STATIC_STRING_CPP14_CONSTEXPR + pointer + data_impl() noexcept + { + return data; + } - size_type size_ = 0; + BOOST_STATIC_STRING_CPP11_CONSTEXPR + const_pointer + data_impl() const noexcept + { + return data; + } - value_type data_[N + 1]{}; + 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 - { - return const_cast(&null_); - } +BOOST_STATIC_STRING_GCC_NESTED_CLASS_WORKAROUND - BOOST_STATIC_STRING_CPP11_CONSTEXPR - std::size_t - size_impl() const noexcept + struct size { - return 0; - } + class basic_static_string + { + friend derived_type; - BOOST_STATIC_STRING_CPP11_CONSTEXPR - std::size_t - set_size(std::size_t) const noexcept + BOOST_STATIC_STRING_CPP11_CONSTEXPR + size_type + size_impl() const noexcept + { + return 0; + } + + 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 +445,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 +1087,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 +2376,7 @@ class basic_static_string void clear() noexcept { - this->set_size(0); + this->size_impl(0); term(); } @@ -2931,7 +2968,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 +3144,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 +5621,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 +5644,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 +5718,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 +6639,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 +6656,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 +6678,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 +6706,10 @@ 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); +#if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ == 7 + term(); +#endif return &curr_data[index]; } @@ -6711,7 +6762,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 +6786,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 +6802,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 +6818,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 +6836,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 +6854,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 +6870,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 +6890,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 +6902,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 +6923,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 +6947,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 +7008,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 +7040,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 +7212,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 +7234,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..71ca3af 100644 --- a/test/constexpr_tests.hpp +++ b/test/constexpr_tests.hpp @@ -22,38 +22,77 @@ 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; + // 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); } + 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 @@ -216,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); @@ -404,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); @@ -576,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); @@ -623,5 +665,28 @@ testConstantEvaluation() cstatic_string().empty(); #endif } + +#if __cpp_nontype_template_args >= 201911L + +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