From e02523e2863cc9efc5f404a7df25f9cd5dfdd008 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Fri, 7 Jun 2013 17:33:25 +0000 Subject: [PATCH] Merged changes from trunk. Fixes operator<< handling of width specification. [SVN r84674] --- include/boost/utility/string_ref.hpp | 178 ++++++++++++---------- include/boost/utility/string_ref_fwd.hpp | 37 +++++ test/Jamfile.v2 | 1 + test/string_ref_test_io.cpp | 183 +++++++++++++++++++++++ 4 files changed, 320 insertions(+), 79 deletions(-) create mode 100644 include/boost/utility/string_ref_fwd.hpp create mode 100644 test/string_ref_test_io.cpp diff --git a/include/boost/utility/string_ref.hpp b/include/boost/utility/string_ref.hpp index 6d5cace..9018513 100644 --- a/include/boost/utility/string_ref.hpp +++ b/include/boost/utility/string_ref.hpp @@ -17,11 +17,15 @@ #include #include +#include +#include +#include #include #include -#include +#include #include +#include namespace boost { @@ -36,18 +40,6 @@ namespace boost { }; } - template class basic_string_ref; - typedef basic_string_ref > string_ref; - typedef basic_string_ref > wstring_ref; - -#ifndef BOOST_NO_CXX11_CHAR16_T - typedef basic_string_ref > u16string_ref; -#endif - -#ifndef BOOST_NO_CXX11_CHAR32_T - typedef basic_string_ref > u32string_ref; -#endif - template class basic_string_ref { public: @@ -61,7 +53,7 @@ namespace boost { typedef std::reverse_iterator const_reverse_iterator; typedef const_reverse_iterator reverse_iterator; typedef std::size_t size_type; - typedef ptrdiff_t difference_type; + typedef std::ptrdiff_t difference_type; static BOOST_CONSTEXPR_OR_CONST size_type npos = size_type(-1); // construct/copy @@ -119,7 +111,7 @@ namespace boost { const charT& at(size_t pos) const { if ( pos >= len_ ) - throw std::out_of_range ( "boost::string_ref::at" ); + BOOST_THROW_EXCEPTION( std::out_of_range ( "boost::string_ref::at" ) ); return ptr_[pos]; } @@ -144,18 +136,12 @@ namespace boost { // basic_string_ref string operations - BOOST_CONSTEXPR basic_string_ref substr(size_type pos, size_type n=npos) const { -#if BOOST_WORKAROUND(BOOST_MSVC, <= 1600) - // Looks like msvc 8 and 9 have a codegen bug when one branch of - // a conditional operator is a throw expression. -EAN 2012/12/04 - if ( pos > size()) throw std::out_of_range ( "string_ref::substr" ); - if ( n == npos || pos + n > size()) n = size () - pos; + if ( pos > size()) + BOOST_THROW_EXCEPTION( std::out_of_range ( "string_ref::substr" ) ); + if ( n == npos || pos + n > size()) + n = size () - pos; return basic_string_ref ( data() + pos, n ); -#else - return pos > size() ? throw std::out_of_range ( "string_ref::substr" ) : - basic_string_ref ( data() + pos, n == npos || pos + n > size() ? size() - pos : n ); -#endif } int compare(basic_string_ref x) const { @@ -260,173 +246,207 @@ namespace boost { // Comparison operators // Equality template - bool operator==(basic_string_ref x, basic_string_ref y) { + inline bool operator==(basic_string_ref x, basic_string_ref y) { if ( x.size () != y.size ()) return false; return x.compare(y) == 0; } template - bool operator==(basic_string_ref x, const std::basic_string & y) { + inline bool operator==(basic_string_ref x, const std::basic_string & y) { return x == basic_string_ref(y); } template - bool operator==(const std::basic_string & x, basic_string_ref y) { + inline bool operator==(const std::basic_string & x, basic_string_ref y) { return basic_string_ref(x) == y; } template - bool operator==(basic_string_ref x, const charT * y) { + inline bool operator==(basic_string_ref x, const charT * y) { return x == basic_string_ref(y); } template - bool operator==(const charT * x, basic_string_ref y) { + inline bool operator==(const charT * x, basic_string_ref y) { return basic_string_ref(x) == y; } // Inequality template - bool operator!=(basic_string_ref x, basic_string_ref y) { + inline bool operator!=(basic_string_ref x, basic_string_ref y) { if ( x.size () != y.size ()) return true; return x.compare(y) != 0; } template - bool operator!=(basic_string_ref x, const std::basic_string & y) { + inline bool operator!=(basic_string_ref x, const std::basic_string & y) { return x != basic_string_ref(y); } template - bool operator!=(const std::basic_string & x, basic_string_ref y) { + inline bool operator!=(const std::basic_string & x, basic_string_ref y) { return basic_string_ref(x) != y; } template - bool operator!=(basic_string_ref x, const charT * y) { + inline bool operator!=(basic_string_ref x, const charT * y) { return x != basic_string_ref(y); } template - bool operator!=(const charT * x, basic_string_ref y) { + inline bool operator!=(const charT * x, basic_string_ref y) { return basic_string_ref(x) != y; } // Less than template - bool operator<(basic_string_ref x, basic_string_ref y) { + inline bool operator<(basic_string_ref x, basic_string_ref y) { return x.compare(y) < 0; } template - bool operator<(basic_string_ref x, const std::basic_string & y) { + inline bool operator<(basic_string_ref x, const std::basic_string & y) { return x < basic_string_ref(y); } template - bool operator<(const std::basic_string & x, basic_string_ref y) { + inline bool operator<(const std::basic_string & x, basic_string_ref y) { return basic_string_ref(x) < y; } template - bool operator<(basic_string_ref x, const charT * y) { + inline bool operator<(basic_string_ref x, const charT * y) { return x < basic_string_ref(y); } template - bool operator<(const charT * x, basic_string_ref y) { + inline bool operator<(const charT * x, basic_string_ref y) { return basic_string_ref(x) < y; } // Greater than template - bool operator>(basic_string_ref x, basic_string_ref y) { + inline bool operator>(basic_string_ref x, basic_string_ref y) { return x.compare(y) > 0; } template - bool operator>(basic_string_ref x, const std::basic_string & y) { + inline bool operator>(basic_string_ref x, const std::basic_string & y) { return x > basic_string_ref(y); } template - bool operator>(const std::basic_string & x, basic_string_ref y) { + inline bool operator>(const std::basic_string & x, basic_string_ref y) { return basic_string_ref(x) > y; } template - bool operator>(basic_string_ref x, const charT * y) { + inline bool operator>(basic_string_ref x, const charT * y) { return x > basic_string_ref(y); } template - bool operator>(const charT * x, basic_string_ref y) { + inline bool operator>(const charT * x, basic_string_ref y) { return basic_string_ref(x) > y; } // Less than or equal to template - bool operator<=(basic_string_ref x, basic_string_ref y) { + inline bool operator<=(basic_string_ref x, basic_string_ref y) { return x.compare(y) <= 0; } template - bool operator<=(basic_string_ref x, const std::basic_string & y) { + inline bool operator<=(basic_string_ref x, const std::basic_string & y) { return x <= basic_string_ref(y); } template - bool operator<=(const std::basic_string & x, basic_string_ref y) { + inline bool operator<=(const std::basic_string & x, basic_string_ref y) { return basic_string_ref(x) <= y; } template - bool operator<=(basic_string_ref x, const charT * y) { + inline bool operator<=(basic_string_ref x, const charT * y) { return x <= basic_string_ref(y); } template - bool operator<=(const charT * x, basic_string_ref y) { + inline bool operator<=(const charT * x, basic_string_ref y) { return basic_string_ref(x) <= y; } // Greater than or equal to template - bool operator>=(basic_string_ref x, basic_string_ref y) { + inline bool operator>=(basic_string_ref x, basic_string_ref y) { return x.compare(y) >= 0; } template - bool operator>=(basic_string_ref x, const std::basic_string & y) { + inline bool operator>=(basic_string_ref x, const std::basic_string & y) { return x >= basic_string_ref(y); } template - bool operator>=(const std::basic_string & x, basic_string_ref y) { + inline bool operator>=(const std::basic_string & x, basic_string_ref y) { return basic_string_ref(x) >= y; } template - bool operator>=(basic_string_ref x, const charT * y) { + inline bool operator>=(basic_string_ref x, const charT * y) { return x >= basic_string_ref(y); } template - bool operator>=(const charT * x, basic_string_ref y) { + inline bool operator>=(const charT * x, basic_string_ref y) { return basic_string_ref(x) >= y; } + namespace detail { + + template + inline void insert_fill_chars(std::basic_ostream& os, std::size_t n) { + enum { chunk_size = 8 }; + charT fill_chars[chunk_size]; + std::fill_n(fill_chars, static_cast< std::size_t >(chunk_size), os.fill()); + for (; n >= chunk_size && os.good(); n -= chunk_size) + os.write(fill_chars, static_cast< std::size_t >(chunk_size)); + if (n > 0 && os.good()) + os.write(fill_chars, n); + } + + template + void insert_aligned(std::basic_ostream& os, const basic_string_ref& str) { + const std::size_t size = str.size(); + const std::size_t alignment_size = static_cast< std::size_t >(os.width()) - size; + const bool align_left = (os.flags() & std::basic_ostream::adjustfield) == std::basic_ostream::left; + if (!align_left) { + detail::insert_fill_chars(os, alignment_size); + if (os.good()) + os.write(str.data(), size); + } + else { + os.write(str.data(), size); + if (os.good()) + detail::insert_fill_chars(os, alignment_size); + } + } + + } // namespace detail + // Inserter template - std::basic_ostream& + inline std::basic_ostream& operator<<(std::basic_ostream& os, const basic_string_ref& str) { -#ifdef BOOST_NO_CXX11_RANGE_BASED_FOR - for ( typename basic_string_ref::const_iterator iter = str.begin (); iter != str.end (); ++iter ) - os << *iter; -#else - for ( charT x : str ) - os << x; -#endif + if (os.good()) { + const std::size_t size = str.size(); + const std::size_t w = static_cast< std::size_t >(os.width()); + if (w <= size) + os.write(str.data(), size); + else + detail::insert_aligned(os, str); + os.width(0); + } return os; } @@ -436,67 +456,67 @@ namespace boost { // These are short-term implementations. // In a production environment, I would rather avoid the copying. // - int stoi (string_ref str, size_t* idx=0, int base=10) { + inline int stoi (string_ref str, size_t* idx=0, int base=10) { return std::stoi ( std::string(str), idx, base ); } - long stol (string_ref str, size_t* idx=0, int base=10) { + inline long stol (string_ref str, size_t* idx=0, int base=10) { return std::stol ( std::string(str), idx, base ); } - unsigned long stoul (string_ref str, size_t* idx=0, int base=10) { + inline unsigned long stoul (string_ref str, size_t* idx=0, int base=10) { return std::stoul ( std::string(str), idx, base ); } - long long stoll (string_ref str, size_t* idx=0, int base=10) { + inline long long stoll (string_ref str, size_t* idx=0, int base=10) { return std::stoll ( std::string(str), idx, base ); } - unsigned long long stoull (string_ref str, size_t* idx=0, int base=10) { + inline unsigned long long stoull (string_ref str, size_t* idx=0, int base=10) { return std::stoull ( std::string(str), idx, base ); } - float stof (string_ref str, size_t* idx=0) { + inline float stof (string_ref str, size_t* idx=0) { return std::stof ( std::string(str), idx ); } - double stod (string_ref str, size_t* idx=0) { + inline double stod (string_ref str, size_t* idx=0) { return std::stod ( std::string(str), idx ); } - long double stold (string_ref str, size_t* idx=0) { + inline long double stold (string_ref str, size_t* idx=0) { return std::stold ( std::string(str), idx ); } - int stoi (wstring_ref str, size_t* idx=0, int base=10) { + inline int stoi (wstring_ref str, size_t* idx=0, int base=10) { return std::stoi ( std::wstring(str), idx, base ); } - long stol (wstring_ref str, size_t* idx=0, int base=10) { + inline long stol (wstring_ref str, size_t* idx=0, int base=10) { return std::stol ( std::wstring(str), idx, base ); } - unsigned long stoul (wstring_ref str, size_t* idx=0, int base=10) { + inline unsigned long stoul (wstring_ref str, size_t* idx=0, int base=10) { return std::stoul ( std::wstring(str), idx, base ); } - long long stoll (wstring_ref str, size_t* idx=0, int base=10) { + inline long long stoll (wstring_ref str, size_t* idx=0, int base=10) { return std::stoll ( std::wstring(str), idx, base ); } - unsigned long long stoull (wstring_ref str, size_t* idx=0, int base=10) { + inline unsigned long long stoull (wstring_ref str, size_t* idx=0, int base=10) { return std::stoull ( std::wstring(str), idx, base ); } - float stof (wstring_ref str, size_t* idx=0) { + inline float stof (wstring_ref str, size_t* idx=0) { return std::stof ( std::wstring(str), idx ); } - double stod (wstring_ref str, size_t* idx=0) { + inline double stod (wstring_ref str, size_t* idx=0) { return std::stod ( std::wstring(str), idx ); } - long double stold (wstring_ref str, size_t* idx=0) { + inline long double stold (wstring_ref str, size_t* idx=0) { return std::stold ( std::wstring(str), idx ); } #endif diff --git a/include/boost/utility/string_ref_fwd.hpp b/include/boost/utility/string_ref_fwd.hpp new file mode 100644 index 0000000..f58b98c --- /dev/null +++ b/include/boost/utility/string_ref_fwd.hpp @@ -0,0 +1,37 @@ +/* + Copyright (c) Marshall Clow 2012-2012. + + 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) + + For more information, see http://www.boost.org + + Based on the StringRef implementation in LLVM (http://llvm.org) and + N3422 by Jeffrey Yasskin + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3442.html + +*/ + +#ifndef BOOST_STRING_REF_FWD_HPP +#define BOOST_STRING_REF_FWD_HPP + +#include +#include + +namespace boost { + + template > class basic_string_ref; + typedef basic_string_ref > string_ref; + typedef basic_string_ref > wstring_ref; + +#ifndef BOOST_NO_CXX11_CHAR16_T + typedef basic_string_ref > u16string_ref; +#endif + +#ifndef BOOST_NO_CXX11_CHAR32_T + typedef basic_string_ref > u32string_ref; +#endif + +} + +#endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index d597cb8..cec9727 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -38,6 +38,7 @@ test-suite utility [ run ../shared_iterator_test.cpp ] [ run string_ref_test1.cpp unit_test_framework ] [ run string_ref_test2.cpp unit_test_framework ] + [ run string_ref_test_io.cpp unit_test_framework ] [ run ../value_init_test.cpp ] [ run ../value_init_workaround_test.cpp ] [ run ../initialized_test.cpp ] diff --git a/test/string_ref_test_io.cpp b/test/string_ref_test_io.cpp new file mode 100644 index 0000000..376f695 --- /dev/null +++ b/test/string_ref_test_io.cpp @@ -0,0 +1,183 @@ +/* + * Copyright Andrey Semashev 2013. + * 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) + */ +/*! + * \file string_ref_test_io.cpp + * \author Andrey Semashev + * \date 26.05.2013 + * + * \brief This header contains tests for stream operations of \c basic_string_ref. + */ + +#define BOOST_TEST_MODULE string_ref_test_io + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +typedef boost::mpl::vector< + char +#if !defined(BOOST_NO_STD_WSTRING) && !defined(BOOST_NO_STD_WSTREAMBUF) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) + , wchar_t +#endif +/* Current implementations seem to be missing codecvt facets to convert chars to char16_t and char32_t even though the types are available. +#if !defined(BOOST_NO_CXX11_CHAR16_T) + , char16_t +#endif +#if !defined(BOOST_NO_CXX11_CHAR32_T) + , char32_t +#endif +*/ +>::type char_types; + +static const char* test_strings[] = +{ + "begin", + "abcd", + "end" +}; + +//! The context with test data for particular character type +template< typename CharT > +struct context +{ + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + string_type begin, abcd, end; + + context() + { + boost::string_ref str = test_strings[0]; + std::copy(str.begin(), str.end(), std::back_inserter(begin)); + + str = test_strings[1]; + std::copy(str.begin(), str.end(), std::back_inserter(abcd)); + + str = test_strings[2]; + std::copy(str.begin(), str.end(), std::back_inserter(end)); + } +}; + +// Test regular output +BOOST_AUTO_TEST_CASE_TEMPLATE(string_ref_output, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef boost::basic_string_ref< char_type > string_ref_type; + + context< char_type > ctx; + + ostream_type strm; + strm << string_ref_type(ctx.abcd); + BOOST_CHECK(strm.str() == ctx.abcd); +} + +// Test support for padding +BOOST_AUTO_TEST_CASE_TEMPLATE(padding, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef boost::basic_string_ref< char_type > string_ref_type; + + context< char_type > ctx; + + // Test for padding + { + ostream_type strm_ref; + strm_ref << ctx.begin << std::setw(8) << string_ref_type(ctx.abcd) << ctx.end; + + ostream_type strm_correct; + strm_correct << ctx.begin << std::setw(8) << ctx.abcd << ctx.end; + + BOOST_CHECK(strm_ref.str() == strm_correct.str()); + } + + // Test for long padding + { + ostream_type strm_ref; + strm_ref << ctx.begin << std::setw(100) << string_ref_type(ctx.abcd) << ctx.end; + + ostream_type strm_correct; + strm_correct << ctx.begin << std::setw(100) << ctx.abcd << ctx.end; + + BOOST_CHECK(strm_ref.str() == strm_correct.str()); + } + + // Test that short width does not truncate the string + { + ostream_type strm_ref; + strm_ref << ctx.begin << std::setw(1) << string_ref_type(ctx.abcd) << ctx.end; + + ostream_type strm_correct; + strm_correct << ctx.begin << std::setw(1) << ctx.abcd << ctx.end; + + BOOST_CHECK(strm_ref.str() == strm_correct.str()); + } +} + +// Test support for padding fill +BOOST_AUTO_TEST_CASE_TEMPLATE(padding_fill, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef boost::basic_string_ref< char_type > string_ref_type; + + context< char_type > ctx; + + ostream_type strm_ref; + strm_ref << ctx.begin << std::setfill(static_cast< char_type >('x')) << std::setw(8) << string_ref_type(ctx.abcd) << ctx.end; + + ostream_type strm_correct; + strm_correct << ctx.begin << std::setfill(static_cast< char_type >('x')) << std::setw(8) << ctx.abcd << ctx.end; + + BOOST_CHECK(strm_ref.str() == strm_correct.str()); +} + +// Test support for alignment +BOOST_AUTO_TEST_CASE_TEMPLATE(alignment, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef boost::basic_string_ref< char_type > string_ref_type; + + context< char_type > ctx; + + // Left alignment + { + ostream_type strm_ref; + strm_ref << ctx.begin << std::left << std::setw(8) << string_ref_type(ctx.abcd) << ctx.end; + + ostream_type strm_correct; + strm_correct << ctx.begin << std::left << std::setw(8) << ctx.abcd << ctx.end; + + BOOST_CHECK(strm_ref.str() == strm_correct.str()); + } + + // Right alignment + { + ostream_type strm_ref; + strm_ref << ctx.begin << std::right << std::setw(8) << string_ref_type(ctx.abcd) << ctx.end; + + ostream_type strm_correct; + strm_correct << ctx.begin << std::right << std::setw(8) << ctx.abcd << ctx.end; + + BOOST_CHECK(strm_ref.str() == strm_correct.str()); + } +}