Skip to content

Commit 9cfd842

Browse files
committed
Add a resize_and_overwrite() member and implement the arithmetic conversions in terms of it
Reason: Implementing the arithmetic conversions without accessing any private members. And, thus, allowing users to do the same, implementing their own conversions with the same efficiency as ours.
1 parent 15a1e18 commit 9cfd842

2 files changed

Lines changed: 244 additions & 122 deletions

File tree

include/boost/static_string/static_string.hpp

Lines changed: 181 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <limits>
4040
#include <iosfwd>
4141
#include <type_traits>
42+
#include <utility>
4243

4344
namespace boost {
4445
namespace static_strings {
@@ -549,13 +550,21 @@ inline
549550
static_string<N>
550551
to_static_string_int_impl(Integer value) noexcept
551552
{
553+
using size_type = typename static_string<N>::size_type;
552554
static_string<N> result;
553-
char * const digits_end = result.data() + N;
554-
char * const digits_begin = integer_to_string<std::char_traits<char>, Integer>(
555-
digits_end, value, std::is_signed<Integer>{});
556-
result.set_size(digits_end - digits_begin);
557-
std::char_traits<char>::move(result.data(), digits_begin, result.size());
558-
result.term();
555+
result.resize_and_overwrite(
556+
N,
557+
[&](char* buffer, size_type) -> size_type
558+
{
559+
char* const digits_end = buffer + N;
560+
char* const digits_begin = integer_to_string<std::char_traits<char>, Integer>(
561+
digits_end, value, std::is_signed<Integer>{});
562+
const size_type len = digits_end - digits_begin;
563+
std::char_traits<char>::move(buffer, digits_begin, len);
564+
return len;
565+
}
566+
);
567+
559568
return result;
560569
}
561570

@@ -565,13 +574,20 @@ inline
565574
static_wstring<N>
566575
to_static_wstring_int_impl(Integer value) noexcept
567576
{
577+
using size_type = typename static_wstring<N>::size_type;
568578
static_wstring<N> result;
569-
wchar_t * const digits_end = result.data() + N;
570-
wchar_t * const digits_begin = integer_to_wstring<std::char_traits<wchar_t>, Integer>(
571-
digits_end, value, std::is_signed<Integer>{});
572-
result.set_size(digits_end - digits_begin);
573-
std::char_traits<wchar_t>::move(result.data(), digits_begin, result.size());
574-
result.term();
579+
result.resize_and_overwrite(
580+
N,
581+
[&](wchar_t* buffer, size_type) -> size_type
582+
{
583+
wchar_t* const digits_end = buffer + N;
584+
wchar_t* const digits_begin = integer_to_wstring<std::char_traits<wchar_t>, Integer>(
585+
digits_end, value, std::is_signed<Integer>{});
586+
const size_type len = digits_end - digits_begin;
587+
std::char_traits<wchar_t>::move(buffer, digits_begin, len);
588+
return len;
589+
}
590+
);
575591
return result;
576592
}
577593
#endif
@@ -597,27 +613,34 @@ inline
597613
static_string<N>
598614
to_static_string_float_impl(double value) noexcept
599615
{
616+
using size_type = typename static_string<N>::size_type;
600617
// we have to assume here that no reasonable implementation
601618
// will require more than 2^63 chars to represent a float value.
602619
const long long narrow =
603620
static_cast<long long>(N);
604621
static_string<N> result;
605-
// we know that a formatting error will not occur, so
606-
// we assume that the result is always positive
607-
std::size_t length = std::snprintf(result.data(), N + 1, "%f", value);
608-
if (length > N)
609-
{
610-
// the + 4 is for the decimal, 'e',
611-
// its sign, and the sign of the integral portion
612-
const int reserved_count =
613-
(std::max)(2, count_digits(
614-
std::numeric_limits<double>::max_exponent10)) + 4;
615-
const int precision = narrow > reserved_count ?
616-
N - reserved_count : 0;
617-
// switch to scientific notation
618-
length = std::snprintf(result.data(), N + 1, "%.*e", precision, value);
619-
}
620-
result.set_size(length);
622+
result.resize_and_overwrite(
623+
N,
624+
[&](char* buffer, size_type) -> size_type
625+
{
626+
// we know that a formatting error will not occur, so
627+
// we assume that the result is always positive
628+
std::size_t length = std::snprintf(buffer, N + 1, "%f", value);
629+
if (length > N)
630+
{
631+
// the + 4 is for the decimal, 'e',
632+
// its sign, and the sign of the integral portion
633+
const int reserved_count =
634+
(std::max)(2, count_digits(
635+
std::numeric_limits<double>::max_exponent10)) + 4;
636+
const int precision = narrow > reserved_count ?
637+
N - reserved_count : 0;
638+
// switch to scientific notation
639+
length = std::snprintf(buffer, N + 1, "%.*e", precision, value);
640+
}
641+
return length;
642+
}
643+
);
621644
return result;
622645
}
623646

@@ -626,29 +649,36 @@ inline
626649
static_string<N>
627650
to_static_string_float_impl(long double value) noexcept
628651
{
652+
using size_type = typename static_string<N>::size_type;
629653
// we have to assume here that no reasonable implementation
630654
// will require more than 2^63 chars to represent a float value.
631655
const long long narrow =
632656
static_cast<long long>(N);
633657
static_string<N> result;
634-
// snprintf returns the number of characters
635-
// that would have been written
636-
// we know that a formatting error will not occur, so
637-
// we assume that the result is always positive
638-
std::size_t length = std::snprintf(result.data(), N + 1, "%Lf", value);
639-
if (length > N)
640-
{
641-
// the + 4 is for the decimal, 'e',
642-
// its sign, and the sign of the integral portion
643-
const int reserved_count =
644-
(std::max)(2, count_digits(
645-
std::numeric_limits<long double>::max_exponent10)) + 4;
646-
const int precision = narrow > reserved_count ?
647-
N - reserved_count : 0;
648-
// switch to scientific notation
649-
length = std::snprintf(result.data(), N + 1, "%.*Le", precision, value);
650-
}
651-
result.set_size(length);
658+
result.resize_and_overwrite(
659+
N,
660+
[&](char* buffer, size_type)->size_type
661+
{
662+
// snprintf returns the number of characters
663+
// that would have been written
664+
// we know that a formatting error will not occur, so
665+
// we assume that the result is always positive
666+
std::size_t length = std::snprintf(buffer, N + 1, "%Lf", value);
667+
if (length > N)
668+
{
669+
// the + 4 is for the decimal, 'e',
670+
// its sign, and the sign of the integral portion
671+
const int reserved_count =
672+
(std::max)(2, count_digits(
673+
std::numeric_limits<long double>::max_exponent10)) + 4;
674+
const int precision = narrow > reserved_count ?
675+
N - reserved_count : 0;
676+
// switch to scientific notation
677+
length = std::snprintf(buffer, N + 1, "%.*Le", precision, value);
678+
}
679+
return length;
680+
}
681+
);
652682
return result;
653683
}
654684

@@ -658,34 +688,41 @@ inline
658688
static_wstring<N>
659689
to_static_wstring_float_impl(double value) noexcept
660690
{
691+
using size_type = typename static_wstring<N>::size_type;
661692
// we have to assume here that no reasonable implementation
662693
// will require more than 2^63 chars to represent a float value.
663694
const long long narrow =
664695
static_cast<long long>(N);
665696
static_wstring<N> result;
666-
// swprintf returns a negative number if it can't
667-
// fit all the characters in the buffer.
668-
// mingw has a non-standard swprintf, so
669-
// this just covers all the bases. short
670-
// circuit evaluation will ensure that the
671-
// second operand is not evaluated on conforming
672-
// implementations.
673-
long long num_written =
674-
std::swprintf(result.data(), N + 1, L"%f", value);
675-
if (num_written < 0 ||
676-
num_written > narrow)
677-
{
678-
// the + 4 is for the decimal, 'e',
679-
// its sign, and the sign of the integral portion
680-
const int reserved_count =
681-
(std::max)(2, count_digits(
682-
std::numeric_limits<double>::max_exponent10)) + 4;
683-
const int precision = narrow > reserved_count ?
684-
N - reserved_count : 0;
685-
// switch to scientific notation
686-
num_written = std::swprintf(result.data(), N + 1, L"%.*e", precision, value);
687-
}
688-
result.set_size(static_cast<std::size_t>(num_written));
697+
result.resize_and_overwrite(
698+
N,
699+
[&](wchar_t* buffer, size_type) -> size_type
700+
{
701+
// swprintf returns a negative number if it can't
702+
// fit all the characters in the buffer.
703+
// mingw has a non-standard swprintf, so
704+
// this just covers all the bases. short
705+
// circuit evaluation will ensure that the
706+
// second operand is not evaluated on conforming
707+
// implementations.
708+
long long num_written =
709+
std::swprintf(buffer, N + 1, L"%f", value);
710+
if (num_written < 0 ||
711+
num_written > narrow)
712+
{
713+
// the + 4 is for the decimal, 'e',
714+
// its sign, and the sign of the integral portion
715+
const int reserved_count =
716+
(std::max)(2, count_digits(
717+
std::numeric_limits<double>::max_exponent10)) + 4;
718+
const int precision = narrow > reserved_count ?
719+
N - reserved_count : 0;
720+
// switch to scientific notation
721+
num_written = std::swprintf(buffer, N + 1, L"%.*e", precision, value);
722+
}
723+
return num_written;
724+
}
725+
);
689726
return result;
690727
}
691728

@@ -694,34 +731,41 @@ inline
694731
static_wstring<N>
695732
to_static_wstring_float_impl(long double value) noexcept
696733
{
734+
using size_type = typename static_wstring<N>::size_type;
697735
// we have to assume here that no reasonable implementation
698736
// will require more than 2^63 chars to represent a float value.
699737
const long long narrow =
700738
static_cast<long long>(N);
701739
static_wstring<N> result;
702-
// swprintf returns a negative number if it can't
703-
// fit all the characters in the buffer.
704-
// mingw has a non-standard swprintf, so
705-
// this just covers all the bases. short
706-
// circuit evaluation will ensure that the
707-
// second operand is not evaluated on conforming
708-
// implementations.
709-
long long num_written =
710-
std::swprintf(result.data(), N + 1, L"%Lf", value);
711-
if (num_written < 0 ||
712-
num_written > narrow)
713-
{
714-
// the + 4 is for the decimal, 'e',
715-
// its sign, and the sign of the integral portion
716-
const int reserved_count =
717-
(std::max)(2, count_digits(
718-
std::numeric_limits<long double>::max_exponent10)) + 4;
719-
const int precision = narrow > reserved_count ?
720-
N - reserved_count : 0;
721-
// switch to scientific notation
722-
num_written = std::swprintf(result.data(), N + 1, L"%.*Le", precision, value);
723-
}
724-
result.set_size(static_cast<std::size_t>(num_written));
740+
result.resize_and_overwrite(
741+
N,
742+
[&](wchar_t* buffer, size_type) -> size_type
743+
{
744+
// swprintf returns a negative number if it can't
745+
// fit all the characters in the buffer.
746+
// mingw has a non-standard swprintf, so
747+
// this just covers all the bases. short
748+
// circuit evaluation will ensure that the
749+
// second operand is not evaluated on conforming
750+
// implementations.
751+
long long num_written =
752+
std::swprintf(buffer, N + 1, L"%Lf", value);
753+
if (num_written < 0 ||
754+
num_written > narrow)
755+
{
756+
// the + 4 is for the decimal, 'e',
757+
// its sign, and the sign of the integral portion
758+
const int reserved_count =
759+
(std::max)(2, count_digits(
760+
std::numeric_limits<long double>::max_exponent10)) + 4;
761+
const int precision = narrow > reserved_count ?
762+
N - reserved_count : 0;
763+
// switch to scientific notation
764+
num_written = std::swprintf(buffer, N + 1, L"%.*Le", precision, value);
765+
}
766+
return num_written;
767+
}
768+
);
725769
return result;
726770
}
727771
#endif
@@ -930,36 +974,6 @@ class basic_static_string
930974
template<std::size_t, class, class>
931975
friend class basic_static_string;
932976

933-
template<std::size_t P, typename Integer>
934-
friend
935-
static_string<P>
936-
detail::to_static_string_int_impl(Integer value) noexcept;
937-
938-
template<std::size_t P>
939-
friend
940-
static_string<P>
941-
detail::to_static_string_float_impl(double value) noexcept;
942-
943-
template<std::size_t P>
944-
friend
945-
static_string<P>
946-
detail::to_static_string_float_impl(long double value) noexcept;
947-
948-
#ifdef BOOST_STATIC_STRING_HAS_WCHAR
949-
template<std::size_t P, typename Integer>
950-
friend static_wstring<P>
951-
detail::to_static_wstring_int_impl(Integer value) noexcept;
952-
953-
template<std::size_t P>
954-
friend
955-
static_wstring<P>
956-
detail::to_static_wstring_float_impl(double value) noexcept;
957-
958-
template<std::size_t P>
959-
friend
960-
static_wstring<P>
961-
detail::to_static_wstring_float_impl(long double value) noexcept;
962-
#endif
963977
public:
964978
//--------------------------------------------------------------------------
965979
//
@@ -3751,6 +3765,31 @@ class basic_static_string
37513765
size_type n,
37523766
value_type c);
37533767

3768+
/**
3769+
Resize the string and overwrite its contents.
3770+
3771+
Resizes the string to contain `n` characters, and uses the
3772+
provided function object `op` to overwrite the string contents.
3773+
The function object is called with two arguments: a pointer to
3774+
the string internal buffer, and the size of the string. The
3775+
function object shall return the number of characters written to
3776+
the buffer, which shall be less than or equal to `n`. The string
3777+
size is set to the value returned by the function object.
3778+
3779+
@par Exception Safety
3780+
3781+
Strong guarantee. However, if an exception is thrown by
3782+
`std::move(op)(p, count)`, the behavior is undefined.
3783+
3784+
@throw std::length_error `n > max_size()`
3785+
*/
3786+
template<typename Operation>
3787+
BOOST_STATIC_STRING_CPP14_CONSTEXPR
3788+
void
3789+
resize_and_overwrite(
3790+
size_type n,
3791+
Operation op);
3792+
37543793
/** Swap two strings.
37553794
37563795
Swaps the contents of the string and `s`.
@@ -6701,6 +6740,26 @@ resize(size_type n, value_type c)
67016740
term();
67026741
}
67036742

6743+
template<std::size_t N, typename CharT, typename Traits>
6744+
template<typename Operation>
6745+
BOOST_STATIC_STRING_CPP14_CONSTEXPR
6746+
void
6747+
basic_static_string<N, CharT, Traits>::
6748+
resize_and_overwrite(
6749+
size_type n,
6750+
Operation op)
6751+
{
6752+
if (n > max_size()) {
6753+
detail::throw_exception<std::length_error>("n > max_size() in resize_and_overwrite()");
6754+
}
6755+
6756+
CharT* p = data();
6757+
const auto new_size = std::move(op)(p, n);
6758+
BOOST_STATIC_STRING_ASSERT(new_size >= 0 && size_type(new_size) <= n);
6759+
this->set_size(size_type(new_size));
6760+
term();
6761+
}
6762+
67046763
template<std::size_t N, typename CharT, typename Traits>
67056764
BOOST_STATIC_STRING_CPP14_CONSTEXPR
67066765
void

0 commit comments

Comments
 (0)