246.md

September 5, 2022 ยท View on GitHub

Info

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

https://godbolt.org/z/Tj1MEdd17

Puzzle

  • Can you implement a numeric literal operator template _c which 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>);

https://godbolt.org/z/d5P81rjjE

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;
  }
}

https://godbolt.org/z/jfY741or5

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;
    }
}

https://godbolt.org/z/x57rPa9dE

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);
}

https://godbolt.org/z/sKWP7arMr

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;
    }
}

https://godbolt.org/z/b3vbYM9nz

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;
}

https://godbolt.org/z/WvPs6z3cx

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;
    }
};

https://godbolt.org/z/dMGrsjEeq

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)>{});
}

https://godbolt.org/z/xG9rEhEbf