Сообщение Кому компил-тайма отсыпать? от 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>);
}И вопрос — как такое отлаживать по шагам?