Last active
February 28, 2019 05:06
-
-
Save sylveon/b0d23fbb5189ec2790b124a51081adcb to your computer and use it in GitHub Desktop.
Roast me
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace impl { | |
constexpr bool IsDecimalDigit(wchar_t character) | |
{ | |
return character >= L'0' && character <= L'9'; | |
} | |
constexpr bool IsCapitalHexDigit(wchar_t character) | |
{ | |
return character >= L'A' && character <= L'F'; | |
} | |
constexpr bool IsLowerHexDigit(wchar_t character) | |
{ | |
return character >= L'a' && character <= L'f'; | |
} | |
template<typename T> | |
constexpr T pow(uint8_t base, std::size_t exponent) | |
{ | |
if (exponent == 0) | |
{ | |
return 1; | |
} | |
else | |
{ | |
T result = base; | |
for (std::size_t i = exponent - 1; i > 0; i--) | |
{ | |
result *= base; | |
} | |
return result; | |
} | |
} | |
template<typename T, uint8_t base> | |
struct NumberParser; | |
template<typename T> | |
struct NumberParser<T, 10> { | |
static constexpr T impl(std::wstring_view number) | |
{ | |
bool isNegative = false; | |
if (number.length() > 1 && number[0] == L'-') | |
{ | |
if constexpr (std::is_signed_v<T>) | |
{ | |
number.remove_prefix(1); | |
isNegative = true; | |
} | |
else | |
{ | |
throw std::invalid_argument("Cannot convert a negative number to a unsigned integer"); | |
} | |
} | |
T result {}; | |
for (std::size_t i = 0; i < number.length(); i++) | |
{ | |
if (impl::IsDecimalDigit(number[i])) | |
{ | |
const T power = impl::pow<T>(10, number.length() - i - 1); | |
result += (number[i] - L'0') * power; | |
} | |
else | |
{ | |
throw std::invalid_argument("Not a number"); | |
} | |
} | |
if constexpr (std::is_signed_v<T>) | |
{ | |
return isNegative ? -result : result; | |
} | |
else | |
{ | |
return result; | |
} | |
} | |
}; | |
template<typename T> | |
struct NumberParser<T, 16> { | |
static_assert (std::is_unsigned_v<T>, "T must be unsigned to parse in base 16"); | |
static constexpr T impl(std::wstring_view number) | |
{ | |
if (number.length() > 2 && number[0] == L'0' && (number[1] == L'x' || number[1] == L'X')) | |
{ | |
number.remove_prefix(2); | |
} | |
T result {}; | |
for (std::size_t i = 0; i < number.length(); i++) | |
{ | |
const T power = 1 << ((number.length() - i - 1) * 4); | |
if (impl::IsDecimalDigit(number[i])) | |
{ | |
result += (number[i] - L'0') * power; | |
} | |
else if (impl::IsCapitalHexDigit(number[i])) | |
{ | |
result += (number[i] - L'A' + 10) * power; | |
} | |
else if (impl::IsLowerHexDigit(number[i])) | |
{ | |
result += (number[i] - L'a' + 10) * power; | |
} | |
else | |
{ | |
throw std::invalid_argument("Not a number"); | |
} | |
} | |
return result; | |
} | |
}; | |
} | |
// Apparently no wide string to number parser accepted an explicit ending to the string | |
// so here I am. Also C locales sucks. | |
template<typename T = int32_t, uint8_t base = 10> | |
constexpr T ParseNumber(std::wstring_view number) | |
{ | |
static_assert(std::is_integral_v<T>, "T must be an integral type"); | |
return impl::NumberParser<T, base>::impl(number); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment