Reduce redundant code in stringconversion.h

This commit is contained in:
Martchus 2021-05-24 19:28:39 +02:00
parent 3afed30760
commit 2e93882882
2 changed files with 127 additions and 149 deletions

View File

@ -526,125 +526,6 @@ void raiseAndAdd(IntegralType &result, BaseType base, CharType character)
} // namespace Detail
/// \endcond
/*!
* \brief Converts the given \a string to an unsigned number assuming \a string uses the specified \a base.
* \tparam IntegralType The data type used to store the converted value.
* \tparam StringType The string type (should be an instantiation of the basic_string class template).
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString(), bufferToNumber()
*/
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) {
Detail::raiseAndAdd(result, base, c);
}
return result;
}
/*!
* \brief Converts the given \a string to a signed number assuming \a string uses the specified \a base.
* \tparam IntegralType The data type used to store the converted value.
* \tparam StringType The string type (should be an instantiation of the basic_string class template).
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString(), bufferToNumber()
*/
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, BaseType base = 10)
{
auto i = string.begin();
auto end = string.end();
for (; i != end && *i == ' '; ++i)
;
if (i == end) {
return 0;
}
const bool negative = (*i == '-');
if (negative) {
++i;
}
IntegralType result = 0;
for (; i != end; ++i) {
Detail::raiseAndAdd(result, base, *i);
}
return negative ? -result : result;
}
/*!
* \brief Converts the given \a string to a number assuming \a string uses the specified \a base.
* \tparam FloatingType The data type used to store the converted value.
* \tparam StringType The string type (should be an instantiation of the basic_string class template).
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \remarks This function is using std::basic_stringstream interanlly and hence also has its limitations (eg. regarding
* \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>
FloatingType stringToNumber(const StringType &string, int base = 10)
{
std::basic_stringstream<typename StringType::value_type> ss;
ss << std::setbase(base) << string;
FloatingType result;
if ((ss >> result) && ss.eof()) {
return result;
}
std::string errorMsg;
errorMsg.reserve(48 + string.size());
errorMsg += "The string \"";
errorMsg += string;
errorMsg += "\" is no valid floating point number.";
throw ConversionException(errorMsg);
}
/*!
* \brief Converts the given null-terminated \a string to an unsigned numeric value using the specified \a base.
* \tparam IntegralType The data type used to store the converted value.
* \tparam CharType The character type.
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString(), bufferToNumber()
*/
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) {
Detail::raiseAndAdd(result, base, *string);
}
return result;
}
/*!
* \brief Converts the given null-terminated \a string to a number assuming \a string uses the specified \a base.
* \tparam FloatingType The data type used to store the converted value.
* \tparam CharType The character type.
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \remarks This function is using std::basic_stringstream interanlly and hence also has its limitations (eg. regarding
* \a base and types).
* \sa numberToString(), bufferToNumber()
*/
template <typename FloatingType, class CharType, Traits::EnableIf<std::is_floating_point<FloatingType>> * = nullptr>
FloatingType stringToNumber(const CharType *string, int base = 10)
{
std::basic_stringstream<CharType> ss;
ss << std::setbase(base) << string;
FloatingType result;
if ((ss >> result) && ss.eof()) {
return result;
}
std::string errorMsg;
errorMsg.reserve(42 + std::char_traits<CharType>::length(string));
errorMsg += "The string \"";
errorMsg += string;
errorMsg += "\" is no valid floating number.";
throw ConversionException(errorMsg);
}
/*!
* \brief Converts the given \a string of \a size characters to an unsigned numeric value using the specified \a base.
* \tparam IntegralType The data type used to store the converted value.
@ -663,36 +544,6 @@ IntegralType bufferToNumber(const CharType *string, std::size_t size, BaseType b
return result;
}
/*!
* \brief Converts the given null-terminated \a string to a signed numeric value using the specified \a base.
* \tparam IntegralType The data type used to store the converted value.
* \tparam CharType The character type.
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString(), bufferToNumber()
*/
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) {
return 0;
}
for (; *string && *string == ' '; ++string)
;
if (!*string) {
return 0;
}
const bool negative = (*string == '-');
if (negative) {
++string;
}
IntegralType result = 0;
for (; *string; ++string) {
Detail::raiseAndAdd(result, base, *string);
}
return negative ? -result : result;
}
/*!
* \brief Converts the given \a string of \a size characters to a signed numeric value using the specified \a base.
* \tparam IntegralType The data type used to store the converted value.
@ -724,6 +575,128 @@ IntegralType bufferToNumber(const CharType *string, std::size_t size, BaseType b
return negative ? -result : result;
}
/*!
* \brief Converts the given \a string to an unsigned/signed number assuming \a string uses the specified \a base.
* \tparam IntegralType The data type used to store the converted value.
* \tparam StringType The string type (should be an instantiation of the basic_string class template).
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString(), bufferToNumber()
*/
template <typename IntegralType, class StringType, typename BaseType = IntegralType,
Traits::EnableIf<std::is_integral<IntegralType>, Traits::Not<std::is_scalar<std::decay_t<StringType>>>> * = nullptr>
IntegralType stringToNumber(const StringType &string, BaseType base = 10)
{
return bufferToNumber<IntegralType, typename StringType::value_type, BaseType>(string.data(), string.size(), base);
}
/*!
* \brief Converts the given \a stringView to a number assuming \a stringView uses the specified \a base.
* \tparam FloatingType The data type used to store the converted value.
* \tparam StringViewType The string view type (must be an instantiation of the basic_string_view class template).
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \remarks This function is using std::basic_stringstream interanlly and hence also has its limitations (eg. regarding
* \a base and types).
* \sa numberToString(), bufferToNumber()
*/
template <typename FloatingType, class StringViewType,
Traits::EnableIf<std::is_floating_point<FloatingType>, Traits::IsSpecializationOf<StringViewType, std::basic_string_view>> * = nullptr>
FloatingType stringToNumber(StringViewType stringView, int base = 10)
{
std::basic_stringstream<typename StringViewType::value_type> ss;
ss << std::setbase(base) << stringView;
FloatingType result;
if ((ss >> result) && ss.eof()) {
return result;
}
std::string errorMsg;
errorMsg.reserve(48 + stringView.size());
errorMsg += "The string \"";
errorMsg += stringView;
errorMsg += "\" is no valid floating point number.";
throw ConversionException(errorMsg);
}
/*!
* \brief Converts the given \a string to a number assuming \a string uses the specified \a base.
* \tparam FloatingType The data type used to store the converted value.
* \tparam StringType The string type (should be an instantiation of the basic_string class template).
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \remarks This function is using std::basic_stringstream interanlly and hence also has its limitations (eg. regarding
* \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>>>,
Traits::Not<Traits::IsSpecializationOf<StringType, std::basic_string_view>>> * = nullptr>
FloatingType stringToNumber(const StringType &string, int base = 10)
{
using StringViewType = std::basic_string_view<typename StringType::value_type>;
return stringToNumber<FloatingType, StringViewType>(StringViewType(string.data(), string.size()), base);
}
/*!
* \brief Converts the given null-terminated \a string to an unsigned numeric value using the specified \a base.
* \tparam IntegralType The data type used to store the converted value.
* \tparam CharType The character type.
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString(), bufferToNumber()
*/
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) {
Detail::raiseAndAdd(result, base, *string);
}
return result;
}
/*!
* \brief Converts the given null-terminated \a string to a number assuming \a string uses the specified \a base.
* \tparam FloatingType The data type used to store the converted value.
* \tparam CharType The character type.
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \remarks This function is using std::basic_stringstream interanlly and hence also has its limitations (eg. regarding
* \a base and types).
* \sa numberToString(), bufferToNumber()
*/
template <typename FloatingType, class CharType, Traits::EnableIf<std::is_floating_point<FloatingType>> * = nullptr>
FloatingType stringToNumber(const CharType *string, int base = 10)
{
return stringToNumber<FloatingType, std::basic_string_view<CharType>>(string, base);
}
/*!
* \brief Converts the given null-terminated \a string to a signed numeric value using the specified \a base.
* \tparam IntegralType The data type used to store the converted value.
* \tparam CharType The character type.
* \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString(), bufferToNumber()
*/
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) {
return 0;
}
for (; *string && *string == ' '; ++string)
;
if (!*string) {
return 0;
}
const bool negative = (*string == '-');
if (negative) {
++string;
}
IntegralType result = 0;
for (; *string; ++string) {
Detail::raiseAndAdd(result, base, *string);
}
return negative ? -result : result;
}
/*!
* \brief Interprets the given \a integer at the specified position as std::string using the specified byte order.
*

View File

@ -292,6 +292,11 @@ void ConversionTests::testStringConversions()
CPPUNIT_ASSERT_EQUAL_MESSAGE("negative limit", -2147483647, stringToNumber<std::int32_t>("-2147483647", 10));
#endif
// stringToNumber() / numberToString() with floating point numbers
CPPUNIT_ASSERT_EQUAL(1.5f, stringToNumber<float>(numberToString(1.5f)));
CPPUNIT_ASSERT_EQUAL(1.5, stringToNumber<double>(numberToString(1.5)));
CPPUNIT_ASSERT_EQUAL(-10.25, stringToNumber<double>("-10.25"));
// interpretIntegerAsString()
CPPUNIT_ASSERT_EQUAL("TEST"s, interpretIntegerAsString<std::uint32_t>(0x54455354));