From 32d70a076412942a5b93f2524a6a744de4809d98 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Mon, 1 Apr 2013 20:40:43 +0000 Subject: [PATCH] Merge from trunk: * Fix stream related issues with libc++ and clang (fixes #7704, fixes #8267) * Change runtime assert to compile time when converting to pointer (fixes #8267) [SVN r83689] --- doc/lexical_cast.qbk | 4 +- include/boost/lexical_cast.hpp | 150 +++++++++++++++++++------- lexical_cast_test.cpp | 10 -- test/Jamfile.v2 | 2 + test/lexical_cast_filesystem_test.cpp | 46 ++++++++ test/lexical_cast_to_pointer_test.cpp | 21 ++++ 6 files changed, 184 insertions(+), 49 deletions(-) create mode 100644 test/lexical_cast_filesystem_test.cpp create mode 100644 test/lexical_cast_to_pointer_test.cpp diff --git a/doc/lexical_cast.qbk b/doc/lexical_cast.qbk index ea31181..78137ce 100644 --- a/doc/lexical_cast.qbk +++ b/doc/lexical_cast.qbk @@ -278,7 +278,9 @@ limitation of compiler options that you use. * [*boost 1.54.0 :] - * Added code to convert boost::int128_type and boost::uint128_type (requires GCC 4.7 or higher). + * Added code to convert `boost::int128_type` and `boost::uint128_type` types (requires GCC 4.7 or higher). + * Conversions to pointers will now fail to compile, instead of throwing at runtime. + * Restored ability to get pointers to `lexical_cast` function (was broken in 1.53.0). * [*boost 1.53.0 :] diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index cca8844..2562b62 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -173,6 +173,7 @@ namespace boost #include #include #include +#include #ifndef BOOST_NO_CWCHAR # include #endif @@ -1414,23 +1415,97 @@ namespace boost { #endif } - namespace detail // stl_buf_unlocker + namespace detail // parser_buf { - template< class BufferType, class CharT > - class stl_buf_unlocker: public BufferType{ + // + // class parser_buf: + // acts as a stream buffer which wraps around a pair of pointers + // + // This class is copied (and slightly changed) from + // boost/regex/v4/cpp_regex_traits.hpp + // Thanks John Maddock for it! (previous version had some + // problems with libc++ and some other STL implementations) + template + class parser_buf : public BufferType { + typedef BufferType base_type; + typedef typename base_type::int_type int_type; + typedef typename base_type::char_type char_type; + typedef typename base_type::pos_type pos_type; + typedef ::std::streamsize streamsize; + typedef typename base_type::off_type off_type; + public: - typedef BufferType base_class; + parser_buf() : base_type() { setbuf(0, 0); } + const charT* getnext() { return this->gptr(); } #ifndef BOOST_NO_USING_TEMPLATE - using base_class::pptr; - using base_class::pbase; - using base_class::setg; - using base_class::setp; + using base_type::pptr; + using base_type::pbase; #else - CharT* pptr() const { return base_class::pptr(); } - CharT* pbase() const { return base_class::pbase(); } - void setg(CharT* gbeg, CharT* gnext, CharT* gend){ return base_class::setg(gbeg, gnext, gend); } - void setp(CharT* pbeg, CharT* pend) { return setp(pbeg, pend); } + charT* pptr() const { return base_type::pptr(); } + charT* pbase() const { return base_type::pbase(); } #endif + base_type* setbuf(char_type* s, streamsize n) { + this->setg(s, s, s + n); + return this; + } + + pos_type seekpos(pos_type sp, ::std::ios_base::openmode which) { + if(which & ::std::ios_base::out) + return pos_type(off_type(-1)); + off_type size = static_cast(this->egptr() - this->eback()); + charT* g = this->eback(); + if(off_type(sp) <= size) + { + this->setg(g, g + off_type(sp), g + size); + } + return pos_type(off_type(-1)); + } + + pos_type seekoff(off_type off, ::std::ios_base::seekdir way, ::std::ios_base::openmode which) { + typedef typename boost::int_t::least cast_type; + + if(which & ::std::ios_base::out) + return pos_type(off_type(-1)); + std::ptrdiff_t size = this->egptr() - this->eback(); + std::ptrdiff_t pos = this->gptr() - this->eback(); + charT* g = this->eback(); + switch(static_cast(way)) + { + case ::std::ios_base::beg: + if((off < 0) || (off > size)) + return pos_type(off_type(-1)); + else + this->setg(g, g + off, g + size); + break; + case ::std::ios_base::end: + if((off < 0) || (off > size)) + return pos_type(off_type(-1)); + else + this->setg(g, g + size - off, g + size); + break; + case ::std::ios_base::cur: + { + std::ptrdiff_t newpos = static_cast(pos + off); + if((newpos < 0) || (newpos > size)) + return pos_type(off_type(-1)); + else + this->setg(g, g + newpos, g + size); + break; + } + default: ; + } +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4244) +#endif + return static_cast(this->gptr() - this->eback()); +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif + } + private: + parser_buf& operator=(const parser_buf&); + parser_buf(const parser_buf&); }; } @@ -1451,13 +1526,12 @@ namespace boost { #if defined(BOOST_NO_STRINGSTREAM) typedef std::ostrstream out_stream_t; - typedef stl_buf_unlocker unlocked_but_t; #elif defined(BOOST_NO_STD_LOCALE) typedef std::ostringstream out_stream_t; - typedef stl_buf_unlocker unlocked_but_t; + typedef parser_buf buffer_t; #else - typedef std::basic_ostringstream out_stream_t; - typedef stl_buf_unlocker, CharT> unlocked_but_t; + typedef std::basic_ostringstream out_stream_t; + typedef parser_buf, CharT> buffer_t; #endif typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c< RequiresStringbuffer, @@ -1471,7 +1545,7 @@ namespace boost { deduced_out_stream_t out_stream; public: - lexical_stream_limited_src(CharT* sta, CharT* fin) + lexical_stream_limited_src(CharT* sta, CharT* fin) BOOST_NOEXCEPT : start(sta) , finish(fin) {} @@ -1540,8 +1614,9 @@ namespace boost { BOOST_STATIC_ASSERT((boost::is_same::value)); #endif bool const result = !(out_stream << input).fail(); - const unlocked_but_t* const p - = static_cast(out_stream.rdbuf()) ; + const buffer_t* const p = static_cast( + static_cast*>(out_stream.rdbuf()) + ); start = p->pbase(); finish = p->pptr(); return result; @@ -1893,35 +1968,34 @@ namespace boost { template bool shr_using_base_class(InputStreamable& output) { -#if (defined _MSC_VER) -# pragma warning( push ) - // conditional expression is constant -# pragma warning( disable : 4127 ) -#endif - if(is_pointer::value) - return false; + BOOST_STATIC_ASSERT_MSG( + (!boost::is_pointer::value), + "boost::lexical_cast can not convert to pointers" + ); #if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_LOCALE) - // If you have compilation error at this point, than your STL library - // unsupports such conversions. Try updating it. - BOOST_STATIC_ASSERT((boost::is_same::value)); + BOOST_STATIC_ASSERT_MSG((boost::is_same::value), + "boost::lexical_cast can not convert, because your STL library does not " + "support such conversions. Try updating it." + ); #endif #if defined(BOOST_NO_STRINGSTREAM) std::istrstream stream(start, finish - start); -#elif defined(BOOST_NO_STD_LOCALE) - std::istringstream stream; #else - std::basic_istringstream stream; -#endif - static_cast(stream.rdbuf()) - ->setg(start, start, finish); + + buffer_t buf; + buf.setbuf(start, finish - start); +#if defined(BOOST_NO_STD_LOCALE) + std::istream stream(&buf); +#else + std::basic_istream stream(&buf); +#endif // BOOST_NO_STD_LOCALE +#endif // BOOST_NO_STRINGSTREAM stream.unsetf(std::ios::skipws); lcast_set_precision(stream, static_cast(0)); -#if (defined _MSC_VER) -# pragma warning( pop ) -#endif + return stream >> output && stream.get() == #if defined(__GNUC__) && (__GNUC__<3) && defined(BOOST_NO_STD_WSTRING) diff --git a/lexical_cast_test.cpp b/lexical_cast_test.cpp index b7b7fb3..875d820 100644 --- a/lexical_cast_test.cpp +++ b/lexical_cast_test.cpp @@ -67,7 +67,6 @@ void test_conversion_to_bool(); void test_conversion_with_nonconst_char(); void test_conversion_to_string(); void test_conversion_from_to_wchar_t_alias(); -void test_conversion_to_pointer(); void test_conversion_from_wchar_t(); void test_conversion_to_wchar_t(); void test_conversion_from_wstring(); @@ -100,7 +99,6 @@ unit_test::test_suite *init_unit_test_suite(int, char *[]) suite->add(BOOST_TEST_CASE(test_conversion_to_double)); suite->add(BOOST_TEST_CASE(test_conversion_to_bool)); suite->add(BOOST_TEST_CASE(test_conversion_from_to_wchar_t_alias)); - suite->add(BOOST_TEST_CASE(test_conversion_to_pointer)); suite->add(BOOST_TEST_CASE(test_conversion_to_string)); suite->add(BOOST_TEST_CASE(test_conversion_with_nonconst_char)); #ifndef BOOST_LCAST_NO_WCHAR_T @@ -312,14 +310,6 @@ void test_conversion_from_to_wchar_t_alias() BOOST_CHECK_EQUAL(std::string("123"), lexical_cast(123ul)); } -void test_conversion_to_pointer() -{ - BOOST_CHECK_THROW(lexical_cast("Test"), bad_lexical_cast); -#ifndef BOOST_LCAST_NO_WCHAR_T - BOOST_CHECK_THROW(lexical_cast("Test"), bad_lexical_cast); -#endif -} - void test_conversion_from_wchar_t() { #ifndef BOOST_LCAST_NO_WCHAR_T diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 5b1447f..25980bb 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -55,5 +55,7 @@ test-suite conversion [ run lexical_cast_integral_types_test.cpp ] [ run lexical_cast_stream_detection_test.cpp ] [ run lexical_cast_stream_traits_test.cpp ] + [ compile-fail lexical_cast_to_pointer_test.cpp ] + [ run lexical_cast_filesystem_test.cpp ../../filesystem/build//boost_filesystem/static ] ; diff --git a/test/lexical_cast_filesystem_test.cpp b/test/lexical_cast_filesystem_test.cpp new file mode 100644 index 0000000..154d107 --- /dev/null +++ b/test/lexical_cast_filesystem_test.cpp @@ -0,0 +1,46 @@ +// Unit test for boost::lexical_cast. +// +// See http://www.boost.org for most recent version, including documentation. +// +// Copyright Antony Polukhin, 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). +// +// Test lexical_cast usage with long filesystem::path. Bug 7704. + +#include + +#include +#include +#include + +using namespace boost; + +void test_filesystem(); + +unit_test::test_suite *init_unit_test_suite(int, char *[]) +{ + unit_test::test_suite *suite = + BOOST_TEST_SUITE("lexical_cast unit test"); + suite->add(BOOST_TEST_CASE(&test_filesystem)); + + return suite; +} + +void test_filesystem() +{ + boost::filesystem::path p; + std::string s1 = "aaaaaaaaaaaaaaaaaaaaaaa"; + p = boost::lexical_cast(s1); + BOOST_CHECK(!p.empty()); + BOOST_CHECK_EQUAL(p, s1); + p.clear(); + + const char ab[] = "aaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; + p = boost::lexical_cast(ab); + BOOST_CHECK(!p.empty()); + BOOST_CHECK_EQUAL(p, ab); +} + diff --git a/test/lexical_cast_to_pointer_test.cpp b/test/lexical_cast_to_pointer_test.cpp new file mode 100644 index 0000000..6756146 --- /dev/null +++ b/test/lexical_cast_to_pointer_test.cpp @@ -0,0 +1,21 @@ +// // Unit test for boost::lexical_cast. +// +// See http://www.boost.org for most recent version, including documentation. +// +// Copyright Antony Polukhin, 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). + +#include +#include + +#define BOOST_INCLUDE_MAIN +#include + +int test_main(int, char*[]) +{ + boost::lexical_cast("Hello"); + return 0; +}