string to int: Allow specifying string size

This commit is contained in:
Martchus 2017-06-08 00:40:59 +02:00
parent 045c713cf5
commit 28f37bbbcd
3 changed files with 82 additions and 22 deletions

View File

@ -121,8 +121,8 @@ set(META_APP_AUTHOR "Martchus")
set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}") set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
set(META_APP_DESCRIPTION "Common C++ classes and routines used by my applications such as argument parser, IO and conversion utilities") set(META_APP_DESCRIPTION "Common C++ classes and routines used by my applications such as argument parser, IO and conversion utilities")
set(META_VERSION_MAJOR 4) set(META_VERSION_MAJOR 4)
set(META_VERSION_MINOR 8) set(META_VERSION_MINOR 9)
set(META_VERSION_PATCH 1) set(META_VERSION_PATCH 0)
# find required 3rd party libraries # find required 3rd party libraries
include(3rdParty) include(3rdParty)

View File

@ -301,7 +301,7 @@ StringType numberToString(IntegralType number, typename StringType::value_type b
* \tparam StringType The string type (should be an instantiation of the basic_string class template). * \tparam StringType The string type (should be an instantiation of the basic_string class template).
* \remarks This function is using std::basic_stringstream interanlly and hence also has its limitations (eg. regarding * \remarks This function is using std::basic_stringstream interanlly and hence also has its limitations (eg. regarding
* \a base and types). * \a base and types).
* \sa stringToNumber() * \sa stringToNumber(), bufferToNumber()
*/ */
template <typename FloatingType, class StringType = std::string, Traits::EnableIf<std::is_floating_point<FloatingType>>...> template <typename FloatingType, class StringType = std::string, Traits::EnableIf<std::is_floating_point<FloatingType>>...>
StringType numberToString(FloatingType number, typename StringType::value_type base = 10) StringType numberToString(FloatingType number, typename StringType::value_type base = 10)
@ -334,11 +334,11 @@ template <typename CharType> CharType charToDigit(CharType character, CharType b
} }
/*! /*!
* \brief Converts the given \a string to a number assuming \a string uses the specified \a base. * \brief Converts the given \a string to an unsigned number assuming \a string uses the specified \a base.
* \tparam NumberType The data type used to store the converted value. * \tparam NumberType The data type used to store the converted value.
* \tparam StringType The string type (should be an instantiation of the basic_string class template). * \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. * \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString() * \sa numberToString(), bufferToNumber()
*/ */
template <typename IntegralType, class StringType, Traits::EnableIf<std::is_integral<IntegralType>, Traits::Not<std::is_signed<IntegralType>>>...> template <typename IntegralType, class StringType, Traits::EnableIf<std::is_integral<IntegralType>, Traits::Not<std::is_signed<IntegralType>>>...>
IntegralType stringToNumber(const StringType &string, typename StringType::value_type base = 10) IntegralType stringToNumber(const StringType &string, typename StringType::value_type base = 10)
@ -355,11 +355,11 @@ IntegralType stringToNumber(const StringType &string, typename StringType::value
} }
/*! /*!
* \brief Converts the given \a string to a number assuming \a string uses the specified \a base. * \brief Converts the given \a string to a signed number assuming \a string uses the specified \a base.
* \tparam NumberType The data type used to store the converted value. * \tparam NumberType The data type used to store the converted value.
* \tparam StringType The string type (should be an instantiation of the basic_string class template). * \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. * \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString() * \sa numberToString(), bufferToNumber()
*/ */
template <typename IntegralType, class StringType, Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>>...> template <typename IntegralType, class StringType, Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>>...>
IntegralType stringToNumber(const StringType &string, typename StringType::value_type base = 10) IntegralType stringToNumber(const StringType &string, typename StringType::value_type base = 10)
@ -393,7 +393,7 @@ IntegralType stringToNumber(const StringType &string, typename StringType::value
* \throws A ConversionException will be thrown if the provided \a string is not a valid number. * \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 * \remarks This function is using std::basic_stringstream interanlly and hence also has its limitations (eg. regarding
* \a base and types). * \a base and types).
* \sa numberToString() * \sa numberToString(), bufferToNumber()
*/ */
template <typename FloatingType, class StringType, Traits::EnableIf<std::is_floating_point<FloatingType>>...> template <typename FloatingType, class StringType, Traits::EnableIf<std::is_floating_point<FloatingType>>...>
FloatingType stringToNumber(const StringType &string, typename StringType::value_type base = 10) FloatingType stringToNumber(const StringType &string, typename StringType::value_type base = 10)
@ -409,11 +409,11 @@ FloatingType stringToNumber(const StringType &string, typename StringType::value
} }
/*! /*!
* \brief Converts the given null-terminated \a string to a numeric value using the specified \a base. * \brief Converts the given null-terminated \a string to an unsigned numeric value using the specified \a base.
* \tparam NumberType The data type used to store the converted value. * \tparam NumberType The data type used to store the converted value.
* \tparam StringType The string type (should be an instantiation of the basic_string class template). * \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. * \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString() * \sa numberToString(), bufferToNumber()
*/ */
template <typename IntegralType, class CharType, Traits::EnableIf<std::is_integral<IntegralType>, Traits::Not<std::is_signed<IntegralType>>>...> template <typename IntegralType, class CharType, Traits::EnableIf<std::is_integral<IntegralType>, Traits::Not<std::is_signed<IntegralType>>>...>
IntegralType stringToNumber(const CharType *string, unsigned char base = 10) IntegralType stringToNumber(const CharType *string, unsigned char base = 10)
@ -430,11 +430,32 @@ IntegralType stringToNumber(const CharType *string, unsigned char base = 10)
} }
/*! /*!
* \brief Converts the given null-terminated \a string to a numeric value using the specified \a base. * \brief Converts the given \a string of \a size characters to an unsigned numeric value using the specified \a base.
* \tparam NumberType The data type used to store the converted value. * \tparam NumberType The data type used to store the converted value.
* \tparam StringType The string type (should be an instantiation of the basic_string class template). * \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. * \throws A ConversionException will be thrown if the provided \a string is not a valid number.
* \sa numberToString() * \sa numberToString(), stringToNumber()
*/
template <typename IntegralType, class CharType, Traits::EnableIf<std::is_integral<IntegralType>, Traits::Not<std::is_signed<IntegralType>>>...>
IntegralType bufferToNumber(const CharType *string, std::size_t size, unsigned char base = 10)
{
IntegralType result = 0;
for (const CharType *end = string + size; string != end; ++string) {
if (*string == ' ') {
continue;
}
result *= base;
result += charToDigit<CharType>(*string, base);
}
return result;
}
/*!
* \brief Converts the given null-terminated \a string to a signed numeric value using the specified \a base.
* \tparam NumberType 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 CharType, Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>>...> template <typename IntegralType, class CharType, Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>>...>
IntegralType stringToNumber(const CharType *string, unsigned char base = 10) IntegralType stringToNumber(const CharType *string, unsigned char base = 10)
@ -462,6 +483,40 @@ IntegralType stringToNumber(const CharType *string, unsigned char base = 10)
return negative ? -result : result; 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 NumberType 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(), stringToNumber()
*/
template <typename IntegralType, class CharType, Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>>...>
IntegralType bufferToNumber(const CharType *string, std::size_t size, unsigned char base = 10)
{
if (!size) {
return 0;
}
const CharType *end = string + size;
for (; string != end && *string == ' '; ++string)
;
if (string == end) {
return 0;
}
const bool negative = (*string == '-');
if (negative) {
++string;
}
IntegralType result = 0;
for (; string != end; ++string) {
if (*string == ' ') {
continue;
}
result *= base;
result += charToDigit<CharType>(*string, base);
}
return negative ? -result : result;
}
/*! /*!
* \brief Interprets the given \a integer at the specified position as std::string using the specified byte order. * \brief Interprets the given \a integer at the specified position as std::string using the specified byte order.
* *

View File

@ -228,24 +228,27 @@ void ConversionTests::testStringEncodingConversions()
void ConversionTests::testStringConversions() void ConversionTests::testStringConversions()
{ {
// stringToNumber() / numberToString() with zero and random numbers // stringToNumber() / numberToString() with zero and random numbers
CPPUNIT_ASSERT_EQUAL(string("0"), numberToString<unsigned int>(0)); CPPUNIT_ASSERT_EQUAL("0"s, numberToString<unsigned int>(0));
CPPUNIT_ASSERT_EQUAL(string("0"), numberToString<signed int>(0)); CPPUNIT_ASSERT_EQUAL("0"s, numberToString<signed int>(0));
uniform_int_distribution<int64> randomDistSigned(numeric_limits<int64>::min()); uniform_int_distribution<int64> randomDistSigned(numeric_limits<int64>::min());
uniform_int_distribution<uint64> randomDistUnsigned(0); uniform_int_distribution<uint64> randomDistUnsigned(0);
const string stringMsg("string"), wideStringMsg("wide string"), bufferMsg("buffer");
for (byte b = 1; b < 100; ++b) { for (byte b = 1; b < 100; ++b) {
auto signedRandom = randomDistSigned(m_randomEngine); auto signedRandom = randomDistSigned(m_randomEngine);
auto unsignedRandom = randomDistUnsigned(m_randomEngine); auto unsignedRandom = randomDistUnsigned(m_randomEngine);
for (const auto base : initializer_list<byte>{ 2, 8, 10, 16 }) { for (const auto base : initializer_list<byte>{ 2, 8, 10, 16 }) {
auto resultString = stringToNumber<uint64, string>(numberToString<uint64, string>(unsignedRandom, base), base); const auto asString = numberToString<uint64, string>(unsignedRandom, static_cast<string::value_type>(base));
auto resultWideString = stringToNumber<uint64, wstring>(numberToString<uint64, wstring>(unsignedRandom, base), base); const auto asWideString = numberToString<uint64, wstring>(unsignedRandom, base);
CPPUNIT_ASSERT_EQUAL(unsignedRandom, resultString); CPPUNIT_ASSERT_EQUAL_MESSAGE(stringMsg, unsignedRandom, stringToNumber<uint64>(asString, static_cast<string::value_type>(base)));
CPPUNIT_ASSERT_EQUAL(unsignedRandom, resultWideString); CPPUNIT_ASSERT_EQUAL_MESSAGE(wideStringMsg, unsignedRandom, stringToNumber<uint64>(asWideString, base));
CPPUNIT_ASSERT_EQUAL_MESSAGE(bufferMsg, unsignedRandom, bufferToNumber<uint64>(asString.data(), asString.size(), base));
} }
for (const auto base : initializer_list<byte>{ 10 }) { for (const auto base : initializer_list<byte>{ 10 }) {
auto resultString = stringToNumber<int64, string>(numberToString<int64, string>(signedRandom, base), base); const auto asString = numberToString<int64, string>(signedRandom, static_cast<string::value_type>(base));
auto resultWideString = stringToNumber<int64, wstring>(numberToString<int64, wstring>(signedRandom, base), base); const auto asWideString = numberToString<int64, wstring>(signedRandom, base);
CPPUNIT_ASSERT_EQUAL(signedRandom, resultString); CPPUNIT_ASSERT_EQUAL_MESSAGE(stringMsg, signedRandom, stringToNumber<int64>(asString, static_cast<string::value_type>(base)));
CPPUNIT_ASSERT_EQUAL(signedRandom, resultWideString); CPPUNIT_ASSERT_EQUAL_MESSAGE(wideStringMsg, signedRandom, stringToNumber<int64>(asWideString, base));
CPPUNIT_ASSERT_EQUAL_MESSAGE(bufferMsg, signedRandom, bufferToNumber<int64>(asString.data(), asString.size(), base));
} }
} }
@ -254,10 +257,12 @@ void ConversionTests::testStringConversions()
CPPUNIT_ASSERT_EQUAL(1, stringToNumber<int32>(L"01"s)); CPPUNIT_ASSERT_EQUAL(1, stringToNumber<int32>(L"01"s));
CPPUNIT_ASSERT_EQUAL(1, stringToNumber<int32>(u"01"s)); CPPUNIT_ASSERT_EQUAL(1, stringToNumber<int32>(u"01"s));
CPPUNIT_ASSERT_EQUAL(-23, stringToNumber<int32>(" - 023"s)); CPPUNIT_ASSERT_EQUAL(-23, stringToNumber<int32>(" - 023"s));
CPPUNIT_ASSERT_EQUAL(-23, bufferToNumber<int32>(" - 023", 6));
CPPUNIT_ASSERT_EQUAL(1u, stringToNumber<uint32>("01")); CPPUNIT_ASSERT_EQUAL(1u, stringToNumber<uint32>("01"));
CPPUNIT_ASSERT_EQUAL(1u, stringToNumber<uint32>(L"01"s)); CPPUNIT_ASSERT_EQUAL(1u, stringToNumber<uint32>(L"01"s));
CPPUNIT_ASSERT_EQUAL(1u, stringToNumber<uint32>(u"01"s)); CPPUNIT_ASSERT_EQUAL(1u, stringToNumber<uint32>(u"01"s));
CPPUNIT_ASSERT_EQUAL(23u, stringToNumber<uint32>(" 023"s)); CPPUNIT_ASSERT_EQUAL(23u, stringToNumber<uint32>(" 023"s));
CPPUNIT_ASSERT_EQUAL(23u, bufferToNumber<uint32>(" 023", 5));
// interpretIntegerAsString() // interpretIntegerAsString()
CPPUNIT_ASSERT_EQUAL("TEST"s, interpretIntegerAsString<uint32>(0x54455354)); CPPUNIT_ASSERT_EQUAL("TEST"s, interpretIntegerAsString<uint32>(0x54455354));