This article discusses StrToNum which is a Header-only library to convert char and wchar_t Text strings to numbers.
StrToNum is a header-only library for converting char
and wchar_t
strings to numbers. The library is fully constexpr
, which means that conversions can be done at compile time if a converting string is a constexpr
string.
StrToNum is completely based on the Microsoft's std::from_chars
implementation from the <charconv>
standard header. The one significant difference though is the ability to work with wchar_t
strings, because the C++ standard doesn't provide such functionality at the moment.
StrToNum features std::(w)string_view
as an input, so you are no longer obliged to have only null-terminated strings, which is mandatory in all std::strto*
functions family.
The library also recognizes 0x
and 0X
prefixes as hex strings, when iBase
is 0
or 16
, which std::from_chars
doesn't.
As a return type StrToNum uses either std::optional
(by default) or very convenient std::expected
from c++23, which holds either a converted number or a from_chars_result
struct in case of converting error. Both these types are very similar. To use the std::expected
version use the /DSTN_USE_EXPECTED
compiler flag.
template<typename IntegralT> requires std::is_integral_v<IntegralT>
[[nodiscard]] constexpr auto StrToNum(std::string_view sv, int iBase = 0)noexcept
->std::optional<IntegralT>
template<typename IntegralT> requires std::is_integral_v<IntegralT>
[[nodiscard]] constexpr auto StrToNum(std::wstring_view wsv, int iBase = 0)noexcept
->std::optional<IntegralT>
template<typename FloatingT> requires std::is_floating_point_v<FloatingT>
[[nodiscard]] constexpr auto StrToNum(std::string_view sv, chars_format fmt = chars_format::general)noexcept
->std::optional<FloatingT>
template<typename FloatingT> requires std::is_floating_point_v<FloatingT>
[[nodiscard]] constexpr auto StrToNum(std::wstring_view wsv, chars_format fmt = chars_format::general)noexcept
->std::optional<FloatingT>
Basically StrToNum is a thin wrapper over the std::from_chars
machinery with the convenient interface and the ability to work with wchar_t
strings. Non-allocating, non-throwing, locale-independent.
StrToNum
is the main templated method which is very easy to use. But there are also predefined wrappers for convenience for all integral and floating types:
[[nodiscard]] inline constexpr auto StrToInt8(std::string_view sv, int iBase = 0)noexcept
->std::optional<std::int8_t>;
[[nodiscard]] inline constexpr auto StrToUInt8(std::string_view sv, int iBase = 0)noexcept
->std::optional<std::uint8_t>;
[[nodiscard]] inline constexpr auto StrToInt16(std::string_view sv, int iBase = 0)noexcept
->std::optional<std::int16_t>;
[[nodiscard]] inline constexpr auto StrToUInt16(std::string_view sv, int iBase = 0)noexcept
->std::optional<std::uint16_t>;
[[nodiscard]] inline constexpr auto StrToInt32(std::string_view sv, int iBase = 0)noexcept
->std::optional<std::int32_t>;
[[nodiscard]] inline constexpr auto StrToUInt32(std::string_view sv, int iBase = 0)noexcept
->std::optional<std::uint32_t>;
[[nodiscard]] inline constexpr auto StrToInt64(std::string_view sv, int iBase = 0)noexcept
->std::optional<std::int64_t>;
[[nodiscard]] inline constexpr auto StrToUInt64(std::string_view sv, int iBase = 0)noexcept
->std::optional<std::uint64_t>;
[[nodiscard]] inline constexpr auto StrToFloat(std::string_view sv, chars_format fmt = chars_format::general)noexcept
->std::optional<float>;
[[nodiscard]] inline constexpr auto StrToDouble(std::string_view sv, chars_format fmt = chars_format::general)noexcept
->std::optional<double>;
#include <iomanip>
#include <iostream>
#include "StrToNum.h"
int main()
{
constexpr auto str = "1234567890";
constexpr auto wstr = L"1234567890";
constexpr auto Int1 = stn::StrToInt32(std::string_view(str).substr(0, 5));
static_assert(Int1 == 12345);
std::cout << "Int1 = " << Int1.value_or(-1) << "\n";
constexpr auto Int2 = stn::StrToInt32(std::wstring_view(wstr).substr(5));
static_assert(Int2 == 67890);
std::cout << "Int2 = " << Int2.value_or(-1) << "\n";
constexpr auto LL1 = stn::StrToInt64("0xABCDEF");
static_assert(LL1 == 0xABCDEF);
std::cout << std::hex << "LL1 = " << LL1.value_or(-1) << "\n";
constexpr auto LL2 = stn::StrToInt64(L"0xFEDCBA");
static_assert(LL2 == 0xFEDCBA);
std::cout << std::hex << "LL2 = " << LL2.value_or(-1) << "\n";
constexpr auto LL3 = stn::StrToInt64(L"-0xACE987");
static_assert(LL3 == -0xACE987);
std::cout << std::dec << "LL3 = " << LL3.value_or(-1) << "\n";
constexpr auto Dbl1 = stn::StrToDouble("3.14159265358979");
static_assert(Dbl1 == 3.14159265358979);
std::cout << std::fixed << std::setprecision(14) << "Dbl1 = " << Dbl1.value_or(-1.) << "\n";
constexpr auto Dbl2 = stn::StrToDouble(L"-987.654321");
static_assert(Dbl2 == -987.654321);
std::cout << std::fixed << std::setprecision(6) << "Dbl2 = " << Dbl2.value_or(-1.) << "\n";
constexpr auto flHex = stn::StrToFloat(L"0x1.Fp-2", stn::chars_format::hex);
static_assert(flHex == 0x1.Fp-2);
std::cout << std::fixed << std::setprecision(6) << "flHex = " << flHex.value_or(-1.) << "\n";
}