// // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Official repository: https://github.com/boostorg/fixed_string // #ifndef BOOST_FIXED_STRING_FIXED_STRING_HPP #define BOOST_FIXED_STRING_FIXED_STRING_HPP #include #include #include #include #include #include #include #include #include namespace boost { namespace fixed_string { /** A fixed-capacity string. These objects behave like `std::string` except that the storage is not dynamically allocated but rather fixed in size, and stored in the object itself. These strings offer performance advantages when an algorithm can execute with a reasonable upper limit on the size of a value. @see to_fixed_string */ template< std::size_t N, typename CharT = char, typename Traits = std::char_traits> class fixed_string { template friend class fixed_string; void term() { Traits::assign(s_[n_], 0); } std::size_t n_; CharT s_[N+1]; public: //-------------------------------------------------------------------------- // // Member types // //-------------------------------------------------------------------------- using traits_type = Traits; using value_type = typename Traits::char_type; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using pointer = value_type*; using reference = value_type&; using const_pointer = value_type const*; using const_reference = value_type const&; using iterator = value_type*; using const_iterator = value_type const*; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; /// The type of `string_view` returned by the interface using string_view_type = basic_string_view; //-------------------------------------------------------------------------- // // Constants // //-------------------------------------------------------------------------- /// Maximum size of the string excluding any null terminator static std::size_t constexpr static_capacity = N; /// A special index static constexpr size_type npos = size_type(-1); //-------------------------------------------------------------------------- // // Construction // //-------------------------------------------------------------------------- /// Default constructor (empty string). fixed_string(); /** Construct with count copies of character `ch`. The behavior is undefined if `count >= npos` */ fixed_string( size_type count, CharT ch); /// Construct with a substring (pos, other.size()) of `other`. template fixed_string( fixed_string const& other, size_type pos); /// Construct with a substring (pos, count) of `other`. template fixed_string( fixed_string const& other, size_type pos, size_type count); /// Construct with the first `count` characters of `s`, including nulls. fixed_string( CharT const* s, size_type count); /// Construct from a null terminated string. fixed_string( CharT const* s); /// Construct from a range of characters template fixed_string( InputIt first, InputIt last #ifndef GENERATING_DOCUMENTATION , typename std::enable_if< detail::is_input_iterator::value, iterator>::type* = 0 #endif ); /// Copy constructor. fixed_string( fixed_string const& other); /// Copy constructor. template fixed_string( fixed_string const& other); /// Construct from an initializer list fixed_string( std::initializer_list init); /// Construct from a `string_view` explicit fixed_string( string_view_type sv); /** Construct from any object convertible to `string_view_type`. The range (pos, n) is extracted from the value obtained by converting `t` to `string_view_type`, and used to construct the string. */ template::value>::type #endif > fixed_string( T const& t, size_type pos, size_type n); //-------------------------------------------------------------------------- // // Assignment // //-------------------------------------------------------------------------- /// Copy assignment. fixed_string& operator=( fixed_string const& s) { return assign(s); } /// Copy assignment. template fixed_string& operator=( fixed_string const& s) { return assign(s); } /// Assign from null-terminated string. fixed_string& operator=( CharT const* s); /// Assign from single character. fixed_string& operator=( CharT ch) { return assign_char(ch, std::integral_constant0)>{}); } /// Assign from initializer list. fixed_string& operator=( std::initializer_list init) { return assign(init); } /// Assign from `string_view_type`. fixed_string& operator=( string_view_type sv) { return assign(sv); } /// Assign `count` copies of `ch`. fixed_string& assign( size_type count, CharT ch); /// Assign from another `fixed_string` fixed_string& assign( fixed_string const& s); /// Assign from another `fixed_string` template fixed_string& assign( fixed_string const& s) { // VFALCO this could come in two flavors, // N>M and NM return assign(s.data(), s.size()); } /// Assign `count` characters starting at `npos` from `other`. template fixed_string& assign( fixed_string const& s, size_type pos, size_type count = npos); /// Assign the first `count` characters of `s`, including nulls. fixed_string& assign( CharT const* s, size_type count); /// Assign a null terminated string. fixed_string& assign( CharT const* s) { return assign(s, Traits::length(s)); } /// Assign from an iterator range of characters. template #ifdef GENERATING_DOCUMENTATION fixed_string& #else typename std::enable_if< detail::is_input_iterator::value, fixed_string&>::type #endif assign( InputIt first, InputIt last); /// Assign from initializer list. fixed_string& assign( std::initializer_list init) { return assign(init.begin(), init.end()); } /// Assign from `string_view_type`. fixed_string& assign( string_view_type s) { return assign(s.data(), s.size()); } /** Assign from any object convertible to `string_view_type`. The range (pos, n) is extracted from the value obtained by converting `t` to `string_view_type`, and used to assign the string. */ template #if GENERATING_DOCUMENTATION fixed_string& #else typename std::enable_if::value, fixed_string&>::type #endif assign( T const& t, size_type pos, size_type count = npos); //-------------------------------------------------------------------------- // // Element access // //-------------------------------------------------------------------------- /// Access specified character with bounds checking. reference at(size_type pos); /// Access specified character with bounds checking. const_reference at(size_type pos) const; /// Access specified character. reference operator[](size_type pos) { return s_[pos]; } /// Access specified character. const_reference operator[](size_type pos) const { return s_[pos]; } /// Accesses the first character. CharT& front() { return s_[0]; } /// Accesses the first character. CharT const& front() const { return s_[0]; } /// Accesses the last character. CharT& back() { return s_[n_-1]; } /// Accesses the last character. CharT const& back() const { return s_[n_-1]; } /// Returns a pointer to the first character of a string. CharT* data() { return &s_[0]; } /// Returns a pointer to the first character of a string. CharT const* data() const { return &s_[0]; } /// Returns a non-modifiable standard C character array version of the string. CharT const* c_str() const { return data(); } /// Convert a static string to a `string_view_type` operator string_view_type() const { return basic_string_view< CharT, Traits>{data(), size()}; } //-------------------------------------------------------------------------- // // Iterators // //-------------------------------------------------------------------------- /// Returns an iterator to the beginning. iterator begin() { return &s_[0]; } /// Returns an iterator to the beginning. const_iterator begin() const { return &s_[0]; } /// Returns an iterator to the beginning. const_iterator cbegin() const { return &s_[0]; } /// Returns an iterator to the end. iterator end() { return &s_[n_]; } /// Returns an iterator to the end. const_iterator end() const { return &s_[n_]; } /// Returns an iterator to the end. const_iterator cend() const { return &s_[n_]; } /// Returns a reverse iterator to the beginning. reverse_iterator rbegin() { return reverse_iterator{end()}; } /// Returns a reverse iterator to the beginning. const_reverse_iterator rbegin() const { return const_reverse_iterator{cend()}; } /// Returns a reverse iterator to the beginning. const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; } /// Returns a reverse iterator to the end. reverse_iterator rend() { return reverse_iterator{begin()}; } /// Returns a reverse iterator to the end. const_reverse_iterator rend() const { return const_reverse_iterator{cbegin()}; } /// Returns a reverse iterator to the end. const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; } //-------------------------------------------------------------------------- // // Capacity // //-------------------------------------------------------------------------- /// Returns `true` if the string is empty. bool empty() const { return n_ == 0; } /// Returns the number of characters, excluding the null terminator. size_type size() const { return n_; } /// Returns the number of characters, excluding the null terminator. size_type length() const { return size(); } /// Returns the maximum number of characters that can be stored, excluding the null terminator. size_type constexpr max_size() const { return N; } /** Reserves storage. This actually just throws an exception if `n > N`, otherwise does nothing since the storage is fixed. */ void reserve(std::size_t n); /// Returns the number of characters that can be held in currently allocated storage. size_type constexpr capacity() const { return max_size(); } /** Reduces memory usage by freeing unused memory. This actually does nothing, since the storage is fixed. */ void shrink_to_fit() { } //-------------------------------------------------------------------------- // // Operations // //-------------------------------------------------------------------------- /// Clears the contents. void clear(); fixed_string& insert( size_type index, size_type count, CharT ch); fixed_string& insert( size_type index, CharT const* s) { return insert(index, s, Traits::length(s)); } fixed_string& insert( size_type index, CharT const* s, size_type count); template fixed_string& insert( size_type index, fixed_string const& s) { return insert(index, s.data(), s.size()); } template fixed_string& insert( size_type index, fixed_string const& s, size_type index_str, size_type count = npos); iterator insert( const_iterator pos, CharT ch) { return insert(pos, 1, ch); } iterator insert( const_iterator pos, size_type count, CharT ch); template #if GENERATING_DOCUMENTATION iterator #else typename std::enable_if< detail::is_input_iterator::value, iterator>::type #endif insert( const_iterator pos, InputIt first, InputIt last); iterator insert( const_iterator pos, std::initializer_list init) { return insert(pos, init.begin(), init.end()); } fixed_string& insert( size_type index, string_view_type s) { return insert(index, s.data(), s.size()); } template #if GENERATING_DOCUMENTATION fixed_string& #else typename std::enable_if< std::is_convertible::value && ! std::is_convertible::value, fixed_string&>::type #endif insert( size_type index, T const& t, size_type index_str, size_type count = npos); fixed_string& erase( size_type index = 0, size_type count = npos); iterator erase( const_iterator pos); iterator erase( const_iterator first, const_iterator last); void push_back( CharT ch); void pop_back() { Traits::assign(s_[--n_], 0); } fixed_string& append( size_type count, CharT ch) { insert(end(), count, ch); return *this; } template fixed_string& append( fixed_string const& s) { insert(size(), s); return *this; } template fixed_string& append( fixed_string const& s, size_type pos, size_type count = npos); fixed_string& append( CharT const* s, size_type count) { insert(size(), s, count); return *this; } fixed_string& append( CharT const* s) { insert(size(), s); return *this; } template #if GENERATING_DOCUMENTATION fixed_string& #else typename std::enable_if< detail::is_input_iterator::value, fixed_string&>::type #endif append( InputIt first, InputIt last) { insert(end(), first, last); return *this; } fixed_string& append( std::initializer_list init) { insert(end(), init); return *this; } fixed_string& append( string_view_type sv) { insert(size(), sv); return *this; } template #if GENERATING_DOCUMENTATION fixed_string& #else typename std::enable_if< std::is_convertible::value && ! std::is_convertible::value, fixed_string&>::type #endif append( T const& t, size_type pos, size_type count = npos) { insert(size(), t, pos, count); return *this; } template fixed_string& operator+=( fixed_string const& s) { return append(s.data(), s.size()); } fixed_string& operator+=( CharT ch) { push_back(ch); return *this; } fixed_string& operator+=( CharT const* s) { return append(s); } fixed_string& operator+=( std::initializer_list init) { return append(init); } fixed_string& operator+=( string_view_type const& s) { return append(s); } template int compare( fixed_string const& s) const { return detail::lexicographical_compare( &s_[0], n_, &s.s_[0], s.n_); } template int compare( size_type pos1, size_type count1, fixed_string const& s) const { return detail::lexicographical_compare( substr(pos1, count1), s.data(), s.size()); } template int compare( size_type pos1, size_type count1, fixed_string const& s, size_type pos2, size_type count2 = npos) const { return detail::lexicographical_compare( substr(pos1, count1), s.substr(pos2, count2)); } int compare( CharT const* s) const { return detail::lexicographical_compare( &s_[0], n_, s, Traits::length(s)); } int compare( size_type pos1, size_type count1, CharT const* s) const { return detail::lexicographical_compare( substr(pos1, count1), s, Traits::length(s)); } int compare( size_type pos1, size_type count1, CharT const*s, size_type count2) const { return detail::lexicographical_compare( substr(pos1, count1), s, count2); } int compare( string_view_type s) const { return detail::lexicographical_compare( &s_[0], n_, s.data(), s.size()); } int compare( size_type pos1, size_type count1, string_view_type s) const { return detail::lexicographical_compare( substr(pos1, count1), s); } template #if GENERATING_DOCUMENTATION int #else typename std::enable_if< std::is_convertible::value && ! std::is_convertible::value, int>::type #endif compare( size_type pos1, size_type count1, T const& t, size_type pos2, size_type count2 = npos) const { return compare(pos1, count1, string_view_type(t).substr(pos2, count2)); } string_view_type substr( size_type pos = 0, size_type count = npos) const; /// Copy a substring (pos, pos+count) to character string pointed to by `dest`. size_type copy( CharT* dest, size_type count, size_type pos = 0) const; /** Changes the number of characters stored. If the resulting string is larger, the new characters are uninitialized. */ void resize( std::size_t n); /** Changes the number of characters stored. If the resulting string is larger, the new characters are initialized to the value of `c`. */ void resize( std::size_t n, CharT c); /// Exchange the contents of this string with another. void swap( fixed_string& s); /// Exchange the contents of this string with another. template void swap( fixed_string& s); //-------------------------------------------------------------------------- // // Search // //-------------------------------------------------------------------------- private: fixed_string& assign_char(CharT ch, std::true_type); fixed_string& assign_char(CharT ch, std::false_type); }; //------------------------------------------------------------------------------ // // Disallowed operations // //------------------------------------------------------------------------------ // These operations are explicitly deleted since // there is no reasonable implementation possible. template void operator+( fixed_stringconst& lhs, fixed_stringconst& rhs) = delete; template void operator+( CharT const* lhs, fixed_stringconst& rhs) = delete; template void operator+( CharT lhs, fixed_string const& rhs) = delete; template void operator+( fixed_string const& lhs, CharT const* rhs) = delete; template void operator+( fixed_string const& lhs, CharT rhs) = delete; //------------------------------------------------------------------------------ // // Non-member functions // //------------------------------------------------------------------------------ template< std::size_t N, std::size_t M, typename CharT, typename Traits> bool operator==( fixed_string const& lhs, fixed_string const& rhs) { return lhs.compare(rhs) == 0; } template< std::size_t N, std::size_t M, typename CharT, typename Traits> bool operator!=( fixed_string const& lhs, fixed_string const& rhs) { return lhs.compare(rhs) != 0; } template< std::size_t N, std::size_t M, typename CharT, typename Traits> bool operator<( fixed_string const& lhs, fixed_string const& rhs) { return lhs.compare(rhs) < 0; } template< std::size_t N, std::size_t M, typename CharT, typename Traits> bool operator<=( fixed_string const& lhs, fixed_string const& rhs) { return lhs.compare(rhs) <= 0; } template< std::size_t N, std::size_t M, typename CharT, typename Traits> bool operator>( fixed_string const& lhs, fixed_string const& rhs) { return lhs.compare(rhs) > 0; } template< std::size_t N, std::size_t M, typename CharT, typename Traits> bool operator>=( fixed_string const& lhs, fixed_string const& rhs) { return lhs.compare(rhs) >= 0; } template bool operator==( CharT const* lhs, fixed_string const& rhs) { return detail::lexicographical_compare( lhs, Traits::length(lhs), rhs.data(), rhs.size()) == 0; } template bool operator==( fixed_string const& lhs, CharT const* rhs) { return detail::lexicographical_compare( lhs.data(), lhs.size(), rhs, Traits::length(rhs)) == 0; } template bool operator!=( CharT const* lhs, fixed_string const& rhs) { return detail::lexicographical_compare( lhs, Traits::length(lhs), rhs.data(), rhs.size()) != 0; } template bool operator!=( fixed_string const& lhs, CharT const* rhs) { return detail::lexicographical_compare( lhs.data(), lhs.size(), rhs, Traits::length(rhs)) != 0; } template bool operator<( CharT const* lhs, fixed_string const& rhs) { return detail::lexicographical_compare( lhs, Traits::length(lhs), rhs.data(), rhs.size()) < 0; } template bool operator<( fixed_string const& lhs, CharT const* rhs) { return detail::lexicographical_compare( lhs.data(), lhs.size(), rhs, Traits::length(rhs)) < 0; } template bool operator<=( CharT const* lhs, fixed_string const& rhs) { return detail::lexicographical_compare( lhs, Traits::length(lhs), rhs.data(), rhs.size()) <= 0; } template bool operator<=( fixed_string const& lhs, CharT const* rhs) { return detail::lexicographical_compare( lhs.data(), lhs.size(), rhs, Traits::length(rhs)) <= 0; } template bool operator>( CharT const* lhs, fixed_string const& rhs) { return detail::lexicographical_compare( lhs, Traits::length(lhs), rhs.data(), rhs.size()) > 0; } template bool operator>( fixed_string const& lhs, CharT const* rhs) { return detail::lexicographical_compare( lhs.data(), lhs.size(), rhs, Traits::length(rhs)) > 0; } template bool operator>=( CharT const* lhs, fixed_string const& rhs) { return detail::lexicographical_compare( lhs, Traits::length(lhs), rhs.data(), rhs.size()) >= 0; } template bool operator>=( fixed_string const& lhs, CharT const* rhs) { return detail::lexicographical_compare( lhs.data(), lhs.size(), rhs, Traits::length(rhs)) >= 0; } //------------------------------------------------------------------------------ // // swap // //------------------------------------------------------------------------------ template void swap( fixed_string& lhs, fixed_string& rhs) { lhs.swap(rhs); } template< std::size_t N, std::size_t M, typename CharT, typename Traits> void swap( fixed_string& lhs, fixed_string& rhs) { lhs.swap(rhs); } //------------------------------------------------------------------------------ // // Input/Output // //------------------------------------------------------------------------------ template std::basic_ostream& operator<<(std::basic_ostream& os, fixed_string const& s) { return os << static_cast< basic_string_view>(s); } //------------------------------------------------------------------------------ // // Numeric conversions // //------------------------------------------------------------------------------ /** Returns a static string representing an integer as a decimal. @param x The signed or unsigned integer to convert. This must be an integral type. @return A @ref fixed_string with an implementation defined maximum size at least as large enough to hold the longest possible decimal representation of any integer of the given type. */ template< class Integer #ifndef GENERATING_DOCUMENTATION , class = typename std::enable_if< std::is_integral::value>::type #endif > fixed_string to_fixed_string(Integer x); } // fixed_string } // boost #include #endif