Как сделать constexpr strtol?

В C++, функция strtol является стандартной функцией для преобразования строки в числовое значение в соответствии с заданной системой счисления. Однако, по умолчанию strtol не является constexpr, что означает, что она не может быть использована для вычисления значений во время компиляции.

Однако, начиная с C++20, мы можем использовать новую фичу языка - "constinit", которая позволяет нам использовать constexpr для глобальных переменных и некоторых выражений с побочными эффектами. Обязательным условием является их инициализация константными выражениями, т.е. значениями, которые могут быть вычислены во время компиляции.

Ниже приведен пример реализации constexpr версии функции strtol:

#include <cstdlib>

constexpr long long int constexpr_strtol(const char* str, char** str_end, int base = 10)
{
    if(base != 10 && base != 16 && base != 8 && base != 2)
    {
        // Обработка некорректного значения base
        return 0;
    }

    long long int result = 0;
    bool is_negative = false;

    // Пропуск пробелов
    while (*str == ' ')
    {
        ++str;
    }

    // Определение знака числа
    if (*str == '-')
    {
        is_negative = true;
        ++str;
    }
    else if (*str == '+')
    {
        ++str;
    }

    // Определение системы счисления base
    if(base == 0)
    {
        if(*str == '0')
        {
            if (*(str + 1) == 'x' || *(str + 1) == 'X')
            {
                base = 16;
                str += 2;
            }
            else
            {
                base = 8;
                ++str;
            }
        }
        else
        {
            base = 10;
        }
    }

    // Преобразование строки в число
    while (*str != '')
    {
        int digit = 0;
        if (*str >= '0' && *str <= '9')
        {
            digit = *str - '0';
        }
        else if (*str >= 'A' && *str <= 'F')
        {
            digit = 10 + *str - 'A';
        }
        else if (*str >= 'a' && *str <= 'f')
        {
            digit = 10 + *str - 'a';
        }
        else
        {
            break;
        }

        if (digit >= base)
        {
            break;
        }

        result = result * base + digit;
        ++str;
    }

    // Устанавливаем указатель на конец преобразованного числа
    if (str_end != nullptr)
    {
        *str_end = const_cast<char*>(str);
    }

    // Возвращаем результат с учетом знака
    return is_negative ? -result : result;
}

int main()
{
    constexpr char str[] = "12345";
    constexpr char* str_end = nullptr;
    constexpr long long int value = constexpr_strtol(str, &str_end);
    
    return 0;
}

В этом примере функция constexpr_strtol определена как constexpr и выполняет аналогичные операции по преобразованию строки в число, как и стандартная функция strtol. Она принимает три аргумента: str, str_end и base. str - это указатель на строку, которую мы хотим преобразовать, str_end - указатель на указатель, который будет указывать на конец преобразованной части строки, base - это система счисления основания, которую мы хотим использовать для преобразования.

В main функции, мы определяем строку str, а затем вызываем constexpr_strtol, чтобы преобразовать ее в число. Результатом будет value, содержащая числовое значение строки. Мы также передаем адрес str_end, чтобы сохранить указатель на конец преобразованной части строки.

Таким образом, используя constexpr и новую фичу C++20 - "constinit", мы можем создать constexpr версию функции strtol, которая может быть использована для вычисления значений во время компиляции.