-
Notifications
You must be signed in to change notification settings - Fork 32
Don't use an additional buffer in the arithmetic conversions #67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,6 +39,7 @@ | |
| #include <limits> | ||
| #include <iosfwd> | ||
| #include <type_traits> | ||
| #include <utility> | ||
|
|
||
| namespace boost { | ||
| namespace static_strings { | ||
|
|
@@ -549,11 +550,22 @@ inline | |
| static_string<N> | ||
| to_static_string_int_impl(Integer value) noexcept | ||
| { | ||
| char buffer[N]; | ||
| const auto digits_end = std::end(buffer); | ||
| const auto digits_begin = integer_to_string<std::char_traits<char>, Integer>( | ||
| digits_end, value, std::is_signed<Integer>{}); | ||
| return static_string<N>(digits_begin, std::distance(digits_begin, digits_end)); | ||
| using size_type = typename static_string<N>::size_type; | ||
| static_string<N> result; | ||
| result.resize_and_overwrite( | ||
| N, | ||
| [&](char* buffer, size_type) -> size_type | ||
| { | ||
| char* const digits_end = buffer + N; | ||
| char* const digits_begin = integer_to_string<std::char_traits<char>, Integer>( | ||
| digits_end, value, std::is_signed<Integer>{}); | ||
| const size_type len = digits_end - digits_begin; | ||
| std::char_traits<char>::move(buffer, digits_begin, len); | ||
| return len; | ||
| } | ||
| ); | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| #ifdef BOOST_STATIC_STRING_HAS_WCHAR | ||
|
|
@@ -562,11 +574,21 @@ inline | |
| static_wstring<N> | ||
| to_static_wstring_int_impl(Integer value) noexcept | ||
| { | ||
| wchar_t buffer[N]; | ||
| const auto digits_end = std::end(buffer); | ||
| const auto digits_begin = integer_to_wstring<std::char_traits<wchar_t>, Integer>( | ||
| digits_end, value, std::is_signed<Integer>{}); | ||
| return static_wstring<N>(digits_begin, std::distance(digits_begin, digits_end)); | ||
| using size_type = typename static_wstring<N>::size_type; | ||
| static_wstring<N> result; | ||
| result.resize_and_overwrite( | ||
| N, | ||
| [&](wchar_t* buffer, size_type) -> size_type | ||
| { | ||
| wchar_t* const digits_end = buffer + N; | ||
| wchar_t* const digits_begin = integer_to_wstring<std::char_traits<wchar_t>, Integer>( | ||
| digits_end, value, std::is_signed<Integer>{}); | ||
| const size_type len = digits_end - digits_begin; | ||
| std::char_traits<wchar_t>::move(buffer, digits_begin, len); | ||
| return len; | ||
| } | ||
| ); | ||
| return result; | ||
| } | ||
| #endif | ||
|
|
||
|
|
@@ -591,59 +613,73 @@ inline | |
| static_string<N> | ||
| to_static_string_float_impl(double value) noexcept | ||
| { | ||
| using size_type = typename static_string<N>::size_type; | ||
| // we have to assume here that no reasonable implementation | ||
| // will require more than 2^63 chars to represent a float value. | ||
| const long long narrow = | ||
| static_cast<long long>(N); | ||
| // extra one needed for null terminator | ||
| char buffer[N + 1]; | ||
| // we know that a formatting error will not occur, so | ||
| // we assume that the result is always positive | ||
| if (std::size_t(std::snprintf(buffer, N + 1, "%f", value)) > N) | ||
| { | ||
| // the + 4 is for the decimal, 'e', | ||
| // its sign, and the sign of the integral portion | ||
| const int reserved_count = | ||
| (std::max)(2, count_digits( | ||
| std::numeric_limits<double>::max_exponent10)) + 4; | ||
| const int precision = narrow > reserved_count ? | ||
| N - reserved_count : 0; | ||
| // switch to scientific notation | ||
| std::snprintf(buffer, N + 1, "%.*e", precision, value); | ||
| } | ||
| // this will not throw | ||
| return static_string<N>(buffer); | ||
| static_string<N> result; | ||
| result.resize_and_overwrite( | ||
| N, | ||
| [&](char* buffer, size_type) -> size_type | ||
| { | ||
| // we know that a formatting error will not occur, so | ||
| // we assume that the result is always positive | ||
| std::size_t length = std::snprintf(buffer, N + 1, "%f", value); | ||
| if (length > N) | ||
| { | ||
| // the + 4 is for the decimal, 'e', | ||
| // its sign, and the sign of the integral portion | ||
| const int reserved_count = | ||
| (std::max)(2, count_digits( | ||
| std::numeric_limits<double>::max_exponent10)) + 4; | ||
| const int precision = narrow > reserved_count ? | ||
| N - reserved_count : 0; | ||
| // switch to scientific notation | ||
| length = std::snprintf(buffer, N + 1, "%.*e", precision, value); | ||
| } | ||
| return length; | ||
| } | ||
| ); | ||
| return result; | ||
| } | ||
|
|
||
| template<std::size_t N> | ||
| inline | ||
| static_string<N> | ||
| to_static_string_float_impl(long double value) noexcept | ||
| { | ||
| using size_type = typename static_string<N>::size_type; | ||
| // we have to assume here that no reasonable implementation | ||
| // will require more than 2^63 chars to represent a float value. | ||
| const long long narrow = | ||
| static_cast<long long>(N); | ||
| // extra one needed for null terminator | ||
| char buffer[N + 1]; | ||
| // snprintf returns the number of characters | ||
| // that would have been written | ||
| // we know that a formatting error will not occur, so | ||
| // we assume that the result is always positive | ||
| if (std::size_t(std::snprintf(buffer, N + 1, "%Lf", value)) > N) | ||
| { | ||
| // the + 4 is for the decimal, 'e', | ||
| // its sign, and the sign of the integral portion | ||
| const int reserved_count = | ||
| (std::max)(2, count_digits( | ||
| std::numeric_limits<long double>::max_exponent10)) + 4; | ||
| const int precision = narrow > reserved_count ? | ||
| N - reserved_count : 0; | ||
| // switch to scientific notation | ||
| std::snprintf(buffer, N + 1, "%.*Le", precision, value); | ||
| } | ||
| // this will not throw | ||
| return static_string<N>(buffer); | ||
| static_string<N> result; | ||
| result.resize_and_overwrite( | ||
| N, | ||
| [&](char* buffer, size_type)->size_type | ||
| { | ||
| // snprintf returns the number of characters | ||
| // that would have been written | ||
| // we know that a formatting error will not occur, so | ||
| // we assume that the result is always positive | ||
| std::size_t length = std::snprintf(buffer, N + 1, "%Lf", value); | ||
| if (length > N) | ||
| { | ||
| // the + 4 is for the decimal, 'e', | ||
| // its sign, and the sign of the integral portion | ||
| const int reserved_count = | ||
| (std::max)(2, count_digits( | ||
| std::numeric_limits<long double>::max_exponent10)) + 4; | ||
| const int precision = narrow > reserved_count ? | ||
| N - reserved_count : 0; | ||
| // switch to scientific notation | ||
| length = std::snprintf(buffer, N + 1, "%.*Le", precision, value); | ||
| } | ||
| return length; | ||
| } | ||
| ); | ||
| return result; | ||
| } | ||
|
|
||
| #ifdef BOOST_STATIC_STRING_HAS_WCHAR | ||
|
|
@@ -652,73 +688,85 @@ inline | |
| static_wstring<N> | ||
| to_static_wstring_float_impl(double value) noexcept | ||
| { | ||
| using size_type = typename static_wstring<N>::size_type; | ||
| // we have to assume here that no reasonable implementation | ||
| // will require more than 2^63 chars to represent a float value. | ||
| const long long narrow = | ||
| static_cast<long long>(N); | ||
| // extra one needed for null terminator | ||
| wchar_t buffer[N + 1]; | ||
| // swprintf returns a negative number if it can't | ||
| // fit all the characters in the buffer. | ||
| // mingw has a non-standard swprintf, so | ||
| // this just covers all the bases. short | ||
| // circuit evaluation will ensure that the | ||
| // second operand is not evaluated on conforming | ||
| // implementations. | ||
| const long long num_written = | ||
| std::swprintf(buffer, N + 1, L"%f", value); | ||
| if (num_written < 0 || | ||
| num_written > narrow) | ||
| { | ||
| // the + 4 is for the decimal, 'e', | ||
| // its sign, and the sign of the integral portion | ||
| const int reserved_count = | ||
| (std::max)(2, count_digits( | ||
| std::numeric_limits<double>::max_exponent10)) + 4; | ||
| const int precision = narrow > reserved_count ? | ||
| N - reserved_count : 0; | ||
| // switch to scientific notation | ||
| std::swprintf(buffer, N + 1, L"%.*e", precision, value); | ||
| } | ||
| // this will not throw | ||
| return static_wstring<N>(buffer); | ||
| static_wstring<N> result; | ||
| result.resize_and_overwrite( | ||
| N, | ||
| [&](wchar_t* buffer, size_type) -> size_type | ||
| { | ||
| // swprintf returns a negative number if it can't | ||
| // fit all the characters in the buffer. | ||
| // mingw has a non-standard swprintf, so | ||
| // this just covers all the bases. short | ||
| // circuit evaluation will ensure that the | ||
| // second operand is not evaluated on conforming | ||
| // implementations. | ||
| long long num_written = | ||
| std::swprintf(buffer, N + 1, L"%f", value); | ||
| if (num_written < 0 || | ||
| num_written > narrow) | ||
| { | ||
| // the + 4 is for the decimal, 'e', | ||
| // its sign, and the sign of the integral portion | ||
| const int reserved_count = | ||
| (std::max)(2, count_digits( | ||
| std::numeric_limits<double>::max_exponent10)) + 4; | ||
| const int precision = narrow > reserved_count ? | ||
| N - reserved_count : 0; | ||
| // switch to scientific notation | ||
| num_written = std::swprintf(buffer, N + 1, L"%.*e", precision, value); | ||
| } | ||
| return num_written; | ||
| } | ||
| ); | ||
| return result; | ||
| } | ||
|
|
||
| template<std::size_t N> | ||
| inline | ||
| static_wstring<N> | ||
| to_static_wstring_float_impl(long double value) noexcept | ||
| { | ||
| using size_type = typename static_wstring<N>::size_type; | ||
| // we have to assume here that no reasonable implementation | ||
| // will require more than 2^63 chars to represent a float value. | ||
| const long long narrow = | ||
| static_cast<long long>(N); | ||
| // extra one needed for null terminator | ||
| wchar_t buffer[N + 1]; | ||
| // swprintf returns a negative number if it can't | ||
| // fit all the characters in the buffer. | ||
| // mingw has a non-standard swprintf, so | ||
| // this just covers all the bases. short | ||
| // circuit evaluation will ensure that the | ||
| // second operand is not evaluated on conforming | ||
| // implementations. | ||
| const long long num_written = | ||
| std::swprintf(buffer, N + 1, L"%Lf", value); | ||
| if (num_written < 0 || | ||
| num_written > narrow) | ||
| { | ||
| // the + 4 is for the decimal, 'e', | ||
| // its sign, and the sign of the integral portion | ||
| const int reserved_count = | ||
| (std::max)(2, count_digits( | ||
| std::numeric_limits<long double>::max_exponent10)) + 4; | ||
| const int precision = narrow > reserved_count ? | ||
| N - reserved_count : 0; | ||
| // switch to scientific notation | ||
| std::swprintf(buffer, N + 1, L"%.*Le", precision, value); | ||
| } | ||
| // this will not throw | ||
| return static_wstring<N>(buffer); | ||
| static_wstring<N> result; | ||
| result.resize_and_overwrite( | ||
| N, | ||
| [&](wchar_t* buffer, size_type) -> size_type | ||
| { | ||
| // swprintf returns a negative number if it can't | ||
| // fit all the characters in the buffer. | ||
| // mingw has a non-standard swprintf, so | ||
| // this just covers all the bases. short | ||
| // circuit evaluation will ensure that the | ||
| // second operand is not evaluated on conforming | ||
| // implementations. | ||
| long long num_written = | ||
| std::swprintf(buffer, N + 1, L"%Lf", value); | ||
| if (num_written < 0 || | ||
| num_written > narrow) | ||
| { | ||
| // the + 4 is for the decimal, 'e', | ||
| // its sign, and the sign of the integral portion | ||
| const int reserved_count = | ||
| (std::max)(2, count_digits( | ||
| std::numeric_limits<long double>::max_exponent10)) + 4; | ||
| const int precision = narrow > reserved_count ? | ||
| N - reserved_count : 0; | ||
| // switch to scientific notation | ||
| num_written = std::swprintf(buffer, N + 1, L"%.*Le", precision, value); | ||
| } | ||
| return num_written; | ||
| } | ||
| ); | ||
| return result; | ||
| } | ||
| #endif | ||
|
|
||
|
|
@@ -925,6 +973,7 @@ class basic_static_string | |
| private: | ||
| template<std::size_t, class, class> | ||
| friend class basic_static_string; | ||
|
|
||
| public: | ||
| //-------------------------------------------------------------------------- | ||
| // | ||
|
|
@@ -3708,6 +3757,31 @@ class basic_static_string | |
| size_type n, | ||
| value_type c); | ||
|
|
||
| /** | ||
| Resize the string and overwrite its contents. | ||
|
|
||
| Resizes the string to contain `n` characters, and uses the | ||
| provided function object `op` to overwrite the string contents. | ||
| The function object is called with two arguments: a pointer to | ||
| the string internal buffer, and the size of the string. The | ||
| function object shall return the number of characters written to | ||
| the buffer, which shall be less than or equal to `n`. The string | ||
| size is set to the value returned by the function object. | ||
|
|
||
| @par Exception Safety | ||
|
|
||
| Strong guarantee. However, if an exception is thrown by | ||
| `std::move(op)(p, count)`, the behavior is undefined. | ||
|
|
||
| @throw std::length_error `n > max_size()` | ||
| */ | ||
| template<typename Operation> | ||
| BOOST_STATIC_STRING_CPP14_CONSTEXPR | ||
| void | ||
| resize_and_overwrite( | ||
| size_type n, | ||
| Operation op); | ||
|
|
||
| /** Swap two strings. | ||
|
|
||
| Swaps the contents of the string and `s`. | ||
|
|
@@ -6658,6 +6732,26 @@ resize(size_type n, value_type c) | |
| term(); | ||
| } | ||
|
|
||
| template<std::size_t N, typename CharT, typename Traits> | ||
| template<typename Operation> | ||
| BOOST_STATIC_STRING_CPP14_CONSTEXPR | ||
| void | ||
| basic_static_string<N, CharT, Traits>:: | ||
| resize_and_overwrite( | ||
| size_type n, | ||
| Operation op) | ||
| { | ||
| if (n > max_size()) { | ||
| detail::throw_exception<std::length_error>("n > max_size() in resize_and_overwrite()"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe the message "n > max_size()" (identical to what's thrown in resize()) is sufficient. there's no need to proliferate string literals. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it sufficient? It gives no clue to the user about where the problem occurred. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BOOST_THROW_EXCEPTION, if used inside detail::throw_exception, would emit the source location of detail::throw_exception. You probably want boost::throw_with_location here, or the two argument overload of boost::throw_exception, so that the source location of the throw is captured, not that of detail::throw_exception. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gennaroprota, it might be more convenient to create an issue for this and address it in a separate PR once you’re done with the current one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this configuration is to still be supported (is it?) you'd probably need to define BOOST_STATIC_STRING_CURRENT_LOCATION and then use Something better might also be possible, I'm not sure. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'd be happy to remove the support. The less conditional compilation, the better. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably ask on the list. |
||
| } | ||
|
|
||
| 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)); | ||
| term(); | ||
| } | ||
|
|
||
| template<std::size_t N, typename CharT, typename Traits> | ||
| BOOST_STATIC_STRING_CPP14_CONSTEXPR | ||
| void | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that is useful, the size passed here doesn't really matter, does it? Maybe a
write_and_resize(op)is more useful? Or is there any realistic use case for this param being less than N?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's how the function is specified for std::string.
For static_string specifically the only thing the passed size does is throw if it's too big.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, makes sense