246.md
September 5, 2022 ยท View on GitHub
Info
-
Did you know that C++11 added a numeric literal operator template?
Example
constexpr auto operator""_i(int) -> int; // error: invalid literal operator parameter type 'int', did you mean 'unsigned long long'?
// numeric literal operator template
template <char... Cs> constexpr auto operator""_i() -> int; // ok
Puzzle
- Can you implement a numeric literal operator template
_cwhich returns a proper type depending on the given value?
template <char... Cs>
[[nodiscard]] constexpr auto operator""_c(); // TODO
static_assert(0 == 0_c);
static_assert(42 == 42_c);
static_assert(1234 == 1234_c);
static_assert(9223372036854775807l == 9223372036854775807_c);
static_assert(18446744073709551615ull == 18446744073709551615_c);
static_assert(std::is_same_v<decltype(0_c), int>);
static_assert(std::is_same_v<decltype(1000000000_c), int>);
static_assert(std::is_same_v<decltype(01000000000_c), int>);
static_assert(std::is_same_v<decltype(00000000001000000000_c), int>);
static_assert(std::is_same_v<decltype(2147483647_c), int>);
static_assert(std::is_same_v<decltype(2147483648_c), long>);
static_assert(std::is_same_v<decltype(9999999999_c), long>);
static_assert(std::is_same_v<decltype(1000000000000000000_c), long>);
static_assert(std::is_same_v<decltype(01000000000000000000_c), long>);
static_assert(std::is_same_v<decltype(9223372036854775807_c), long>);
static_assert(std::is_same_v<decltype(9223372036854775808_c), unsigned long long>);
static_assert(std::is_same_v<decltype(9999999999999999999_c), unsigned long long>);
static_assert(std::is_same_v<decltype(10000000000000000000_c), unsigned long long>);
static_assert(std::is_same_v<decltype(18446744073709551615_c), unsigned long long>);
Solutions
template <char... Cs>
[[nodiscard]] consteval auto operator""_c() {
constexpr auto value = [] {
constexpr char chars[]{Cs...};
unsigned long long i = 0;
for (auto c : chars) {
i *= 10;
i += c - '0';
}
return i;
}();
if constexpr (value <= std::numeric_limits<int>::max()) {
return static_cast<int>(value);
} else if constexpr (value <= std::numeric_limits<long>::max()) {
return static_cast<long>(value);
} else {
return value;
}
}
constexpr auto to_ull(const auto &str) {
return std::accumulate(
std::cbegin(str), std::cend(str), 0ull, [](const auto n, const auto c) {
return n * 10 + c - '0';
});
}
template <char... Cs>
[[nodiscard]] constexpr auto operator""_c() {
constexpr auto n = to_ull(std::array{Cs...});
if constexpr (n <= std::numeric_limits<int>::max()) {
return static_cast<int>(n);
} else if constexpr (n <= std::numeric_limits<long>::max()) {
return static_cast<long>(n);
} else {
return n;
}
}
template <char... Cs>
[[nodiscard]] constexpr auto operator""_c() {
using ull = unsigned long long;
constexpr std::array<int, sizeof...(Cs)> digits = {Cs-'0'...};
constexpr ull num = std::accumulate(digits.begin(), digits.end(), 0ull,
[](ull sum, int digit) { return 10*sum + digit;});
if constexpr (num <= std::numeric_limits<int>::max())
return static_cast<int>(num);
else if constexpr (num <= std::numeric_limits<long>::max())
return static_cast<long>(num);
else
return static_cast<ull> (num);
}
constexpr int char_to_digit(char x){
return x-'0';
};
template <char... Cs>
[[nodiscard]] constexpr auto operator""_c() {
constexpr std::array chars{Cs...};
constexpr auto value = std::accumulate(std::begin(chars), std::end(chars), 0ull, [=](auto agg, auto c){
const auto base = chars[0] == '0' ? 8 : 10;
return agg * base + char_to_digit(c);
});
if constexpr( value <= std::numeric_limits<int>::max()){
return int(value);
}
else if constexpr( value <= std::numeric_limits<long>::max()){
return long(value);
}
else
{
return value;
}
}
template<char ... Cs>
struct parse_integer{
static constexpr std::array<unsigned long long, sizeof...(Cs)> chars = {Cs ...};
static constexpr unsigned long long N = std::accumulate(begin(chars), end(chars), 0ULL,
[](unsigned long long accum, char x){ return 10*accum + x-'0';});
static constexpr auto u = [](auto f, auto ... xs){ return f(f, xs ...); };
template<typename ... Ts> struct types{};
static constexpr auto value =
u([]<typename T, typename ... Ts>(auto recur, types<T, Ts ...>){
if constexpr(N <= std::numeric_limits<T>::max()){
return T(N);
} else {
return recur(recur, types<Ts...>{});
}
},
types<int, long, long long, unsigned long long>{});
};
template <char C, char... Cs>
constexpr auto operator""_c(){
return parse_integer<C, Cs ...>::value;
}
template <char... Cs>
[[nodiscard]] constexpr auto operator""_c() noexcept {
constexpr auto base = [] (auto digit, char specifier = '0', auto...) {
if (sizeof...(Cs) == 1 || digit != '0') return 10;
switch (specifier) {
default:
return 8;
case 'B':
case 'b':
return 2;
case 'X':
case 'x':
return 16;
}
}(Cs...);
constexpr auto non_digits = [] (auto digit, char specifier = '0', auto...) {
if (sizeof...(Cs) == 1 || digit != '0') return 0;
switch (specifier) {
default:
return 1;
case 'B':
case 'X':
case 'b':
case 'x':
return 2;
}
}(Cs...);
constexpr auto value = [] (auto acc, auto non_digits) {
const auto digit = [&] (auto c) {
if (--non_digits >= 0) return 0;
if (c >= 'A' && c <= 'F') return c - 'A' + 0xA;
if (c >= 'a' && c <= 'f') return c - 'a' + 0xa;
return c - '0';
};
return (..., (acc *= base, acc += digit(Cs)));
}(0ull, non_digits);
if constexpr (value <= std::numeric_limits<int>::max()) {
return static_cast<int>(value);
} else if constexpr (value <= std::numeric_limits<long>::max()) {
return static_cast<long>(value);
} else if constexpr (value <= std::numeric_limits<long long>::max()) {
return static_cast<long long>(value);
} else {
return value;
}
};
constexpr unsigned long long pow10(std::size_t to) {
return to == 0 ? 1 : pow10(to/2) * pow10(to/2) * (to % 2 == 0 ? 1 : 10);
}
template <char... Cs, std::size_t ...Ix>
constexpr auto calculate(std::index_sequence<Ix...>) {
enum Type { MAKE_SIGNED = -1, Value = (((Cs - '0') * pow10(sizeof...(Ix) - 1 - Ix)) + ...) };
using U = std::underlying_type_t<Type>;
if constexpr (sizeof(U) > 8 || Value < 0)
return (unsigned long long)Value;
else
return (U)Value;
}
template <char... Cs>
[[nodiscard]] constexpr auto operator""_c() {
return calculate<Cs...>(std::make_index_sequence<sizeof...(Cs)>{});
}