| | S>S>// compile_time_calc.hpp — reference implementation of the constexpr calculator
S>// C++20/23, single-header. Drop it into a project and `#include`.
S>#ifndef COMPILE_TIME_CALC_HPP
S>#define COMPILE_TIME_CALC_HPP
S>#include <string_view>
S>#include <optional>
S>#include <limits>
S>#include <cstddef>
S>#include <type_traits>
S>//---------------------------------------------------------------------
S>// 1. NTTP-строка как тип
S>//---------------------------------------------------------------------
S>template<char... Cs>
S>struct lit {
S> static constexpr char str[sizeof...(Cs) + 1]{ Cs..., '\0' };
S>};
S>// пользовательский литерал "42*(3+1)"_expr → объект типа lit<'4','2','*', ...>
S>template<typename CharT, CharT... Cs>
S>consteval auto operator"" _expr() {
S> static_assert(std::is_same_v<CharT, char>, "_expr supports only narrow char literals");
S> return lit<Cs...>{};
S>}
S>//---------------------------------------------------------------------
S>// 2. Проверка переполнения для пяти базовых операций
S>//---------------------------------------------------------------------
S>constexpr unsigned long long MAX = std::numeric_limits<unsigned long long>::max();
S>constexpr std::optional<unsigned long long> add(unsigned long long a, unsigned long long b) {
S> return (a > MAX - b) ? std::nullopt : std::optional{ a + b };
S>}
S>constexpr std::optional<unsigned long long> sub(unsigned long long a, unsigned long long b) {
S> return (a < b) ? std::nullopt : std::optional{ a - b };
S>}
S>constexpr std::optional<unsigned long long> mul(unsigned long long a, unsigned long long b) {
S> return (b != 0 && a > MAX / b) ? std::nullopt : std::optional{ a * b };
S>}
S>constexpr std::optional<unsigned long long> div(unsigned long long a, unsigned long long b) {
S> return (b == 0) ? std::nullopt : std::optional{ a / b };
S>}
S>constexpr std::optional<unsigned long long> mod(unsigned long long a, unsigned long long b) {
S> return (b == 0) ? std::nullopt : std::optional{ a % b };
S>}
S>//---------------------------------------------------------------------
S>// 3. Рекурсивный парсер-оценщик
S>//---------------------------------------------------------------------
S>struct parser {
S> std::string_view s;
S> std::size_t pos = 0;
S> constexpr bool eof() const { return pos >= s.size(); }
S> constexpr char peek() const { return eof() ? '\0' : s[pos]; }
S> constexpr void skip_ws() {
S> while (!eof() && (peek() == ' ' || peek() == '\t' || peek() == '\n')) ++pos;
S> }
S> constexpr bool consume(char c) {
S> skip_ws();
S> if (peek() == c) { ++pos; return true; }
S> return false;
S> }
S> // number := [0-9]+
S> constexpr std::optional<unsigned long long> number() {
S> skip_ws();
S> if (peek() < '0' || peek() > '9') return std::nullopt;
S> unsigned long long v = 0;
S> while (!eof() && peek() >= '0' && peek() <= '9') {
S> int digit = peek() - '0';
S> if (auto nv = mul(v, 10)) {
S> if (auto nv2 = add(*nv, digit)) {
S> v = *nv2;
S> } else return std::nullopt;
S> } else return std::nullopt;
S> ++pos;
S> }
S> return v;
S> }
S> // factor := number | '(' expr ')'
S> constexpr std::optional<unsigned long long> factor() {
S> skip_ws();
S> if (consume('(')) {
S> auto val = expr();
S> if (!val || !consume(')')) return std::nullopt;
S> return val;
S> }
S> return number();
S> }
S> // term := factor { ('*' | '/' | '%') factor }
S> constexpr std::optional<unsigned long long> term() {
S> auto lhs = factor();
S> if (!lhs) return std::nullopt;
S> while (true) {
S> skip_ws();
S> char op = peek();
S> if (op != '*' && op != '/' && op != '%') break;
S> ++pos;
S> auto rhs = factor();
S> if (!rhs) return std::nullopt;
S> switch (op) {
S> case '*': lhs = mul(*lhs, *rhs); break;
S> case '/': lhs = div(*lhs, *rhs); break;
S> case '%': lhs = mod(*lhs, *rhs); break;
S> }
S> if (!lhs) return std::nullopt;
S> }
S> return lhs;
S> }
S> // expr := term { ('+' | '-') term }
S> constexpr std::optional<unsigned long long> expr() {
S> auto lhs = term();
S> if (!lhs) return std::nullopt;
S> while (true) {
S> skip_ws();
S> char op = peek();
S> if (op != '+' && op != '-') break;
S> ++pos;
S> auto rhs = term();
S> if (!rhs) return std::nullopt;
S> switch (op) {
S> case '+': lhs = add(*lhs, *rhs); break;
S> case '-': lhs = sub(*lhs, *rhs); break;
S> }
S> if (!lhs) return std::nullopt;
S> }
S> return lhs;
S> }
S>};
S>//---------------------------------------------------------------------
S>// 4. Вспомогательный meta-проход: lit<Cs...> → optional<ULL>
S>//---------------------------------------------------------------------
S>template<char... Cs>
S>consteval std::optional<unsigned long long> eval_impl() {
S> constexpr std::string_view sv{ lit<Cs...>::str, sizeof...(Cs) }; // без NUL
S> parser p{ sv };
S> auto val = p.expr();
S> p.skip_ws();
S> return (!val || !p.eof()) ? std::nullopt : val;
S>}
S>// превращаем объект lit<...> в мета-информацию
S>template<typename T>
S>struct parsed; // общая шаблонная декларация
S>template<char... Cs>
S>struct parsed<lit<Cs...>> {
S> static constexpr auto opt = eval_impl<Cs...>();
S> static constexpr bool ok = opt.has_value();
S> static constexpr unsigned long long value = *opt; // ОК использовать, только если ok == true
S>};
S>//---------------------------------------------------------------------
S>// 5. Концепты и основной шаблон calc
S>//---------------------------------------------------------------------
S>template<auto Lit>
S>concept ValidExpr = parsed<decltype(Lit)>::ok;
S>template<auto Lit>
S>requires ValidExpr<Lit>
S>struct calc {
S> static constexpr unsigned long long value = parsed<decltype(Lit)>::value;
S> constexpr operator unsigned long long() const { return value; }
S>};
S>//---------------------------------------------------------------------
S>// 6. Утилита-alias для SFINAE-френдли value-типа
S>//---------------------------------------------------------------------
S>template<auto Lit>
S>using calc_v = std::integral_constant<unsigned long long, calc<Lit>::value>;
S>#endif // COMPILE_TIME_CALC_HPP
S>int main(int argc, const char * argv[]) {
S> static_assert(calc<"42 * (10 - 8)"_expr>::value == 84);
S> static_assert(ValidExpr<"((7+5)*3)"_expr>);
S>}
S>
|