Retain source compatibility of numberToString()/stringToNumber()

IntegralType and BaseType need to be kept different types, otherwise one
runs into deduction errors if both arguments have different types (which is
quite likely in practise). Since the base is always a small positive
integer the cast should always be safe.
This commit is contained in:
Martchus 2021-03-22 12:27:40 +01:00
parent a30ba0c147
commit b43de126b9
1 changed files with 35 additions and 26 deletions

View File

@ -397,18 +397,18 @@ template <typename CharType> constexpr CharType digitToChar(CharType digit)
* \tparam StringType The string type (should be an instantiation of the basic_string class template).
* \sa stringToNumber()
*/
template <typename IntegralType, class StringType = std::string,
template <typename IntegralType, class StringType = std::string, typename BaseType = IntegralType,
CppUtilities::Traits::EnableIf<std::is_integral<IntegralType>, std::is_unsigned<IntegralType>> * = nullptr>
StringType numberToString(IntegralType number, IntegralType base = 10)
StringType numberToString(IntegralType number, BaseType base = 10)
{
std::size_t resSize = 0;
for (auto n = number; n; n /= base, ++resSize)
for (auto n = number; n; n /= static_cast<IntegralType>(base), ++resSize)
;
StringType res;
res.reserve(resSize);
do {
res.insert(res.begin(), digitToChar<typename StringType::value_type>(static_cast<typename StringType::value_type>(number % base)));
number /= base;
number /= static_cast<IntegralType>(base);
} while (number);
return res;
}
@ -419,9 +419,9 @@ StringType numberToString(IntegralType number, IntegralType base = 10)
* \tparam StringType The string type (should be an instantiation of the basic_string class template).
* \sa stringToNumber()
*/
template <typename IntegralType, class StringType = std::string,
template <typename IntegralType, class StringType = std::string, typename BaseType = IntegralType,
Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>> * = nullptr>
StringType numberToString(IntegralType number, IntegralType base = 10)
StringType numberToString(IntegralType number, BaseType base = 10)
{
const bool negative = number < 0;
std::size_t resSize;
@ -430,13 +430,14 @@ StringType numberToString(IntegralType number, IntegralType base = 10)
} else {
resSize = 0;
}
for (auto n = number; n; n /= base, ++resSize)
for (auto n = number; n; n /= static_cast<IntegralType>(base), ++resSize)
;
StringType res;
res.reserve(resSize);
do {
res.insert(res.begin(), digitToChar<typename StringType::value_type>(static_cast<typename StringType::value_type>(number % base)));
number /= base;
res.insert(res.begin(),
digitToChar<typename StringType::value_type>(static_cast<typename StringType::value_type>(number % static_cast<IntegralType>(base))));
number /= static_cast<IntegralType>(base);
} while (number);
if (negative) {
res.insert(res.begin(), '-');
@ -492,15 +493,17 @@ template <typename CharType> CharType charToDigit(CharType character, CharType b
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString(), bufferToNumber()
*/
template <typename IntegralType, typename StringType, Traits::EnableIf<std::is_integral<IntegralType>, std::is_unsigned<IntegralType>, Traits::Not<std::is_scalar<std::decay_t<StringType>>>> * = nullptr>
IntegralType stringToNumber(const StringType &string, IntegralType base = 10)
template <typename IntegralType, class StringType, typename BaseType = IntegralType,
Traits::EnableIf<std::is_integral<IntegralType>, std::is_unsigned<IntegralType>, Traits::Not<std::is_scalar<std::decay_t<StringType>>>>
* = nullptr>
IntegralType stringToNumber(const StringType &string, BaseType base = 10)
{
IntegralType result = 0;
for (const auto &c : string) {
if (c == ' ') {
continue;
}
result *= base;
result *= static_cast<IntegralType>(base);
result += static_cast<IntegralType>(charToDigit<typename StringType::value_type>(c, static_cast<typename StringType::value_type>(base)));
}
return result;
@ -513,7 +516,8 @@ IntegralType stringToNumber(const StringType &string, IntegralType base = 10)
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString(), bufferToNumber()
*/
template <typename IntegralType, class StringType, Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>, Traits::Not<std::is_scalar<std::decay_t<StringType>>>> * = nullptr>
template <typename IntegralType, class StringType, typename BaseType = IntegralType,
Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>, Traits::Not<std::is_scalar<std::decay_t<StringType>>>> * = nullptr>
IntegralType stringToNumber(const StringType &string, IntegralType base = 10)
{
auto i = string.begin();
@ -532,7 +536,7 @@ IntegralType stringToNumber(const StringType &string, IntegralType base = 10)
if (*i == ' ') {
continue;
}
result *= base;
result *= static_cast<IntegralType>(base);
result += static_cast<IntegralType>(charToDigit<typename StringType::value_type>(*i, static_cast<typename StringType::value_type>(base)));
}
return negative ? -result : result;
@ -547,7 +551,8 @@ IntegralType stringToNumber(const StringType &string, IntegralType base = 10)
* \a base and types).
* \sa numberToString(), bufferToNumber()
*/
template <typename FloatingType, class StringType, Traits::EnableIf<std::is_floating_point<FloatingType>, Traits::Not<std::is_scalar<std::decay_t<StringType>>>> * = nullptr>
template <typename FloatingType, class StringType,
Traits::EnableIf<std::is_floating_point<FloatingType>, Traits::Not<std::is_scalar<std::decay_t<StringType>>>> * = nullptr>
FloatingType stringToNumber(const StringType &string, int base = 10)
{
std::basic_stringstream<typename StringType::value_type> ss;
@ -571,15 +576,16 @@ FloatingType stringToNumber(const StringType &string, int base = 10)
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString(), bufferToNumber()
*/
template <typename IntegralType, class CharType, Traits::EnableIf<std::is_integral<IntegralType>, std::is_unsigned<IntegralType>> * = nullptr>
IntegralType stringToNumber(const CharType *string, IntegralType base = 10)
template <typename IntegralType, typename CharType, typename BaseType = IntegralType,
Traits::EnableIf<std::is_integral<IntegralType>, std::is_unsigned<IntegralType>> * = nullptr>
IntegralType stringToNumber(const CharType *string, BaseType base = 10)
{
IntegralType result = 0;
for (; *string; ++string) {
if (*string == ' ') {
continue;
}
result *= base;
result *= static_cast<IntegralType>(base);
result += static_cast<IntegralType>(charToDigit<CharType>(*string, static_cast<CharType>(base)));
}
return result;
@ -618,15 +624,16 @@ FloatingType stringToNumber(const CharType *string, int base = 10)
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString(), stringToNumber()
*/
template <typename IntegralType, class CharType, Traits::EnableIf<std::is_integral<IntegralType>, std::is_unsigned<IntegralType>> * = nullptr>
IntegralType bufferToNumber(const CharType *string, std::size_t size, IntegralType base = 10)
template <typename IntegralType, class CharType, typename BaseType = IntegralType,
Traits::EnableIf<std::is_integral<IntegralType>, std::is_unsigned<IntegralType>> * = nullptr>
IntegralType bufferToNumber(const CharType *string, std::size_t size, BaseType base = 10)
{
IntegralType result = 0;
for (const CharType *end = string + size; string != end; ++string) {
if (*string == ' ') {
continue;
}
result *= base;
result *= static_cast<IntegralType>(base);
result += static_cast<IntegralType>(charToDigit<CharType>(*string, static_cast<CharType>(base)));
}
return result;
@ -639,7 +646,8 @@ IntegralType bufferToNumber(const CharType *string, std::size_t size, IntegralTy
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString(), bufferToNumber()
*/
template <typename IntegralType, class CharType, Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>> * = nullptr>
template <typename IntegralType, typename CharType, typename BaseType = IntegralType,
Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>> * = nullptr>
IntegralType stringToNumber(const CharType *string, IntegralType base = 10)
{
if (!*string) {
@ -659,7 +667,7 @@ IntegralType stringToNumber(const CharType *string, IntegralType base = 10)
if (*string == ' ') {
continue;
}
result *= base;
result *= static_cast<IntegralType>(base);
result += static_cast<IntegralType>(charToDigit<CharType>(*string, static_cast<CharType>(base)));
}
return negative ? -result : result;
@ -672,8 +680,9 @@ IntegralType stringToNumber(const CharType *string, IntegralType base = 10)
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString(), stringToNumber()
*/
template <typename IntegralType, class CharType, Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>> * = nullptr>
IntegralType bufferToNumber(const CharType *string, std::size_t size, IntegralType base = 10)
template <typename IntegralType, typename CharType, typename BaseType = IntegralType,
Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>> * = nullptr>
IntegralType bufferToNumber(const CharType *string, std::size_t size, BaseType base = 10)
{
if (!size) {
return 0;
@ -693,7 +702,7 @@ IntegralType bufferToNumber(const CharType *string, std::size_t size, IntegralTy
if (*string == ' ') {
continue;
}
result *= base;
result *= static_cast<IntegralType>(base);
result += static_cast<IntegralType>(charToDigit<CharType>(*string, static_cast<CharType>(base)));
}
return negative ? -result : result;