Информация об изменениях

Сообщение Кому компил-тайма отсыпать? от 27.04.2025 3:13

Изменено 27.04.2025 4:05 Shmj

Кому компил-тайма отсыпать?
Вот вам парсер-калькулятор строк в компил-тайме, все как вы любите:

// compile_time_calc.hpp — reference implementation of the constexpr calculator
// C++20/23, single-header.  Drop it into a project and `#include`.

#ifndef COMPILE_TIME_CALC_HPP
#define COMPILE_TIME_CALC_HPP

#include <string_view>
#include <optional>
#include <limits>
#include <cstddef>
#include <type_traits>

//---------------------------------------------------------------------
// 1.  NTTP-строка как тип
//---------------------------------------------------------------------

template<char... Cs>
struct lit {
    static constexpr char str[sizeof...(Cs) + 1]{ Cs..., '\0' };
};

// пользовательский литерал       "42*(3+1)"_expr  →  объект типа lit<'4','2','*', ...>

template<typename CharT, CharT... Cs>
consteval auto operator"" _expr() {
    static_assert(std::is_same_v<CharT, char>, "_expr supports only narrow char literals");
    return lit<Cs...>{};
}

//---------------------------------------------------------------------
// 2.  Проверка переполнения для пяти базовых операций
//---------------------------------------------------------------------

constexpr unsigned long long MAX = std::numeric_limits<unsigned long long>::max();

constexpr std::optional<unsigned long long> add(unsigned long long a, unsigned long long b) {
    return (a > MAX - b) ? std::nullopt : std::optional{ a + b };
}
constexpr std::optional<unsigned long long> sub(unsigned long long a, unsigned long long b) {
    return (a < b) ? std::nullopt : std::optional{ a - b };
}
constexpr std::optional<unsigned long long> mul(unsigned long long a, unsigned long long b) {
    return (b != 0 && a > MAX / b) ? std::nullopt : std::optional{ a * b };
}
constexpr std::optional<unsigned long long> div(unsigned long long a, unsigned long long b) {
    return (b == 0) ? std::nullopt : std::optional{ a / b };
}
constexpr std::optional<unsigned long long> mod(unsigned long long a, unsigned long long b) {
    return (b == 0) ? std::nullopt : std::optional{ a % b };
}

//---------------------------------------------------------------------
// 3.  Рекурсивный парсер-оценщик
//---------------------------------------------------------------------

struct parser {
    std::string_view s;
    std::size_t pos = 0;

    constexpr bool eof() const { return pos >= s.size(); }
    constexpr char peek() const { return eof() ? '\0' : s[pos]; }
    constexpr void skip_ws() {
        while (!eof() && (peek() == ' ' || peek() == '\t' || peek() == '\n')) ++pos;
    }
    constexpr bool consume(char c) {
        skip_ws();
        if (peek() == c) { ++pos; return true; }
        return false;
    }

    // number := [0-9]+
    constexpr std::optional<unsigned long long> number() {
        skip_ws();
        if (peek() < '0' || peek() > '9') return std::nullopt;
        unsigned long long v = 0;
        while (!eof() && peek() >= '0' && peek() <= '9') {
            int digit = peek() - '0';
            if (auto nv = mul(v, 10)) {
                if (auto nv2 = add(*nv, digit)) {
                    v = *nv2;
                } else return std::nullopt;
            } else return std::nullopt;
            ++pos;
        }
        return v;
    }

    // factor := number | '(' expr ')'
    constexpr std::optional<unsigned long long> factor() {
        skip_ws();
        if (consume('(')) {
            auto val = expr();
            if (!val || !consume(')')) return std::nullopt;
            return val;
        }
        return number();
    }

    // term := factor { ('*' | '/' | '%') factor }
    constexpr std::optional<unsigned long long> term() {
        auto lhs = factor();
        if (!lhs) return std::nullopt;
        while (true) {
            skip_ws();
            char op = peek();
            if (op != '*' && op != '/' && op != '%') break;
            ++pos;
            auto rhs = factor();
            if (!rhs) return std::nullopt;
            switch (op) {
            case '*': lhs = mul(*lhs, *rhs); break;
            case '/': lhs = div(*lhs, *rhs); break;
            case '%': lhs = mod(*lhs, *rhs); break;
            }
            if (!lhs) return std::nullopt;
        }
        return lhs;
    }

    // expr := term { ('+' | '-') term }
    constexpr std::optional<unsigned long long> expr() {
        auto lhs = term();
        if (!lhs) return std::nullopt;
        while (true) {
            skip_ws();
            char op = peek();
            if (op != '+' && op != '-') break;
            ++pos;
            auto rhs = term();
            if (!rhs) return std::nullopt;
            switch (op) {
            case '+': lhs = add(*lhs, *rhs); break;
            case '-': lhs = sub(*lhs, *rhs); break;
            }
            if (!lhs) return std::nullopt;
        }
        return lhs;
    }
};

//---------------------------------------------------------------------
// 4.  Вспомогательный meta-проход: lit<Cs...> → optional<ULL>
//---------------------------------------------------------------------

template<char... Cs>
consteval std::optional<unsigned long long> eval_impl() {
    constexpr std::string_view sv{ lit<Cs...>::str, sizeof...(Cs) }; // без NUL
    parser p{ sv };
    auto val = p.expr();
    p.skip_ws();
    return (!val || !p.eof()) ? std::nullopt : val;
}

// превращаем объект lit<...> в мета-информацию

template<typename T>
struct parsed;                       // общая шаблонная декларация

template<char... Cs>
struct parsed<lit<Cs...>> {
    static constexpr auto opt = eval_impl<Cs...>();
    static constexpr bool ok  = opt.has_value();
    static constexpr unsigned long long value = *opt; // ОК использовать, только если ok == true
};

//---------------------------------------------------------------------
// 5.  Концепты и основной шаблон calc
//---------------------------------------------------------------------

template<auto Lit>
concept ValidExpr = parsed<decltype(Lit)>::ok;

template<auto Lit>
requires ValidExpr<Lit>
struct calc {
    static constexpr unsigned long long value = parsed<decltype(Lit)>::value;
    constexpr operator unsigned long long() const { return value; }
};

//---------------------------------------------------------------------
// 6.  Утилита-alias для SFINAE-френдли value-типа
//---------------------------------------------------------------------

template<auto Lit>
using calc_v = std::integral_constant<unsigned long long, calc<Lit>::value>;


#endif // COMPILE_TIME_CALC_HPP


int main(int argc, const char * argv[]) {
    static_assert(calc<"42 * (10 - 8)"_expr>::value == 84);
    static_assert(ValidExpr<"((7+5)*3)"_expr>);
}


Кому компил-тайма отсыпать?
Вот вам парсер-калькулятор строк в компил-тайме, все как вы любите:

// compile_time_calc.hpp — reference implementation of the constexpr calculator
// C++20/23, single-header.  Drop it into a project and `#include`.

#ifndef COMPILE_TIME_CALC_HPP
#define COMPILE_TIME_CALC_HPP

#include <string_view>
#include <optional>
#include <limits>
#include <cstddef>
#include <type_traits>

//---------------------------------------------------------------------
// 1.  NTTP-строка как тип
//---------------------------------------------------------------------

template<char... Cs>
struct lit {
    static constexpr char str[sizeof...(Cs) + 1]{ Cs..., '\0' };
};

// пользовательский литерал       "42*(3+1)"_expr  →  объект типа lit<'4','2','*', ...>

template<typename CharT, CharT... Cs>
consteval auto operator"" _expr() {
    static_assert(std::is_same_v<CharT, char>, "_expr supports only narrow char literals");
    return lit<Cs...>{};
}

//---------------------------------------------------------------------
// 2.  Проверка переполнения для пяти базовых операций
//---------------------------------------------------------------------

constexpr unsigned long long MAX = std::numeric_limits<unsigned long long>::max();

constexpr std::optional<unsigned long long> add(unsigned long long a, unsigned long long b) {
    return (a > MAX - b) ? std::nullopt : std::optional{ a + b };
}
constexpr std::optional<unsigned long long> sub(unsigned long long a, unsigned long long b) {
    return (a < b) ? std::nullopt : std::optional{ a - b };
}
constexpr std::optional<unsigned long long> mul(unsigned long long a, unsigned long long b) {
    return (b != 0 && a > MAX / b) ? std::nullopt : std::optional{ a * b };
}
constexpr std::optional<unsigned long long> div(unsigned long long a, unsigned long long b) {
    return (b == 0) ? std::nullopt : std::optional{ a / b };
}
constexpr std::optional<unsigned long long> mod(unsigned long long a, unsigned long long b) {
    return (b == 0) ? std::nullopt : std::optional{ a % b };
}

//---------------------------------------------------------------------
// 3.  Рекурсивный парсер-оценщик
//---------------------------------------------------------------------

struct parser {
    std::string_view s;
    std::size_t pos = 0;

    constexpr bool eof() const { return pos >= s.size(); }
    constexpr char peek() const { return eof() ? '\0' : s[pos]; }
    constexpr void skip_ws() {
        while (!eof() && (peek() == ' ' || peek() == '\t' || peek() == '\n')) ++pos;
    }
    constexpr bool consume(char c) {
        skip_ws();
        if (peek() == c) { ++pos; return true; }
        return false;
    }

    // number := [0-9]+
    constexpr std::optional<unsigned long long> number() {
        skip_ws();
        if (peek() < '0' || peek() > '9') return std::nullopt;
        unsigned long long v = 0;
        while (!eof() && peek() >= '0' && peek() <= '9') {
            int digit = peek() - '0';
            if (auto nv = mul(v, 10)) {
                if (auto nv2 = add(*nv, digit)) {
                    v = *nv2;
                } else return std::nullopt;
            } else return std::nullopt;
            ++pos;
        }
        return v;
    }

    // factor := number | '(' expr ')'
    constexpr std::optional<unsigned long long> factor() {
        skip_ws();
        if (consume('(')) {
            auto val = expr();
            if (!val || !consume(')')) return std::nullopt;
            return val;
        }
        return number();
    }

    // term := factor { ('*' | '/' | '%') factor }
    constexpr std::optional<unsigned long long> term() {
        auto lhs = factor();
        if (!lhs) return std::nullopt;
        while (true) {
            skip_ws();
            char op = peek();
            if (op != '*' && op != '/' && op != '%') break;
            ++pos;
            auto rhs = factor();
            if (!rhs) return std::nullopt;
            switch (op) {
            case '*': lhs = mul(*lhs, *rhs); break;
            case '/': lhs = div(*lhs, *rhs); break;
            case '%': lhs = mod(*lhs, *rhs); break;
            }
            if (!lhs) return std::nullopt;
        }
        return lhs;
    }

    // expr := term { ('+' | '-') term }
    constexpr std::optional<unsigned long long> expr() {
        auto lhs = term();
        if (!lhs) return std::nullopt;
        while (true) {
            skip_ws();
            char op = peek();
            if (op != '+' && op != '-') break;
            ++pos;
            auto rhs = term();
            if (!rhs) return std::nullopt;
            switch (op) {
            case '+': lhs = add(*lhs, *rhs); break;
            case '-': lhs = sub(*lhs, *rhs); break;
            }
            if (!lhs) return std::nullopt;
        }
        return lhs;
    }
};

//---------------------------------------------------------------------
// 4.  Вспомогательный meta-проход: lit<Cs...> → optional<ULL>
//---------------------------------------------------------------------

template<char... Cs>
consteval std::optional<unsigned long long> eval_impl() {
    constexpr std::string_view sv{ lit<Cs...>::str, sizeof...(Cs) }; // без NUL
    parser p{ sv };
    auto val = p.expr();
    p.skip_ws();
    return (!val || !p.eof()) ? std::nullopt : val;
}

// превращаем объект lit<...> в мета-информацию

template<typename T>
struct parsed;                       // общая шаблонная декларация

template<char... Cs>
struct parsed<lit<Cs...>> {
    static constexpr auto opt = eval_impl<Cs...>();
    static constexpr bool ok  = opt.has_value();
    static constexpr unsigned long long value = *opt; // ОК использовать, только если ok == true
};

//---------------------------------------------------------------------
// 5.  Концепты и основной шаблон calc
//---------------------------------------------------------------------

template<auto Lit>
concept ValidExpr = parsed<decltype(Lit)>::ok;

template<auto Lit>
requires ValidExpr<Lit>
struct calc {
    static constexpr unsigned long long value = parsed<decltype(Lit)>::value;
    constexpr operator unsigned long long() const { return value; }
};

//---------------------------------------------------------------------
// 6.  Утилита-alias для SFINAE-френдли value-типа
//---------------------------------------------------------------------

template<auto Lit>
using calc_v = std::integral_constant<unsigned long long, calc<Lit>::value>;


#endif // COMPILE_TIME_CALC_HPP


int main(int argc, const char * argv[]) {
    static_assert(calc<"42 * (10 - 8)"_expr>::value == 84);
    static_assert(ValidExpr<"((7+5)*3)"_expr>);
}




И вопрос — как такое отлаживать по шагам?