Renamed inc directory to const_fmt
This commit is contained in:
52
const_fmt/Logger.h
Normal file
52
const_fmt/Logger.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef LOGGER_LOGGER_H
|
||||
#define LOGGER_LOGGER_H
|
||||
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <tuple>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Logger class
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
concept output_policy_c = requires(T t) {
|
||||
t.write('c');
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
template <detail::output_policy_c output_policy_t>
|
||||
class Logger {
|
||||
|
||||
public:
|
||||
Logger(output_policy_t output_policy) : m_output_policy(output_policy) {
|
||||
}
|
||||
|
||||
template <detail::ConstString msg, typename... args_t>
|
||||
void log(args_t... args) {
|
||||
const auto formatted_msg = format<msg>(args...);
|
||||
|
||||
for (const auto& c : formatted_msg) {
|
||||
m_output_policy.write(c);
|
||||
}
|
||||
|
||||
m_output_policy.write('\n');
|
||||
}
|
||||
|
||||
private:
|
||||
output_policy_t& m_output_policy;
|
||||
};
|
||||
|
||||
|
||||
#endif // LOGGER_LOGGER_H
|
||||
138
const_fmt/format.h
Normal file
138
const_fmt/format.h
Normal file
@@ -0,0 +1,138 @@
|
||||
#ifndef LOGGER_FORMAT_H
|
||||
#define LOGGER_FORMAT_H
|
||||
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "parse.h"
|
||||
#include "utility.h"
|
||||
#include "format_impl.h"
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Utility functions
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
template <ConstString s>
|
||||
constexpr inline int get_output_len() {
|
||||
constexpr auto parse_result = parse_string<s>();
|
||||
static_assert(parse_result.is_valid, "Syntax error in format string");
|
||||
|
||||
return get_ast_output_len<parse_result.value>();
|
||||
}
|
||||
|
||||
template <fmt_node_t fmt_node, typename T>
|
||||
constexpr inline void check_fmt_params() {
|
||||
static_assert(fmt_node.length > fmt_node.precision + 1,
|
||||
"Insufficient length for desired precision");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Formatting wrapper functions
|
||||
*
|
||||
*/
|
||||
|
||||
template <fmt_data_t fmt_data, std::integral arg_t>
|
||||
constexpr inline void format_arg(char* dest, arg_t arg) {
|
||||
detail::format_int(dest, arg, fmt_data);
|
||||
};
|
||||
|
||||
template <fmt_data_t fmt_data, std::floating_point arg_t>
|
||||
constexpr inline void format_arg(char* dest, arg_t arg) {
|
||||
//detail::format_float(dest, arg, fmt_data);
|
||||
};
|
||||
|
||||
// TODO: Error handling
|
||||
template<fmt_data_t fmt_data>
|
||||
constexpr inline void format_arg(char* dest, const char* arg) {
|
||||
const std::size_t len = const_strlen(arg);
|
||||
if (len > fmt_data.length) return;
|
||||
|
||||
dest = dest + fmt_data.length - len;
|
||||
|
||||
if (!std::is_constant_evaluated()) {
|
||||
std::memcpy(dest, arg, len);
|
||||
return;
|
||||
}
|
||||
for (std::size_t i = 0; i < len; ++i) {
|
||||
*(dest++) = *(arg++);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// End of recursion
|
||||
template <auto ast>
|
||||
constexpr inline void format_args(char*) {
|
||||
}
|
||||
|
||||
template <auto fmt_data_array, typename first_arg_t, typename... args_t>
|
||||
constexpr inline void format_args(char* dest, first_arg_t first_arg, args_t... args) {
|
||||
format_arg<fmt_data_array[0]>(dest + fmt_data_array[0].position, first_arg);
|
||||
format_args<drop_first(fmt_data_array)>(dest, args...);
|
||||
}
|
||||
|
||||
|
||||
template <auto ast>
|
||||
consteval inline std::array<char, get_ast_output_len<ast>()> get_preproc_string() {
|
||||
auto result = get_init_array<get_ast_output_len<ast>()>('f');
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (const auto& ast_node : ast) {
|
||||
if (ast_node.is_char())
|
||||
result[i++] = ast_node.get_char();
|
||||
else
|
||||
i += ast_node.get_node().length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Public Interface
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
template <detail::ConstString s, typename... args_t>
|
||||
constexpr inline auto format(args_t... args) {
|
||||
constexpr auto ast = detail::parse_string<s>().value;
|
||||
constexpr auto fmt_data = detail::get_fmt_data<ast>();
|
||||
|
||||
auto result = detail::get_preproc_string<ast>();
|
||||
|
||||
detail::format_args<fmt_data>(result.begin(), args...);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template<detail::ConstString t_s>
|
||||
class fmt_literal_obj_t {
|
||||
public:
|
||||
template<typename... args_t>
|
||||
constexpr auto operator()(args_t... args) {
|
||||
return format<t_s>(args...);
|
||||
}
|
||||
};
|
||||
|
||||
template <detail::ConstString t_s>
|
||||
constexpr auto operator""_fmt() {
|
||||
return fmt_literal_obj_t<t_s>{};
|
||||
}
|
||||
|
||||
|
||||
#endif // LOGGER_FORMAT_H
|
||||
154
const_fmt/format_impl.h
Normal file
154
const_fmt/format_impl.h
Normal file
@@ -0,0 +1,154 @@
|
||||
#ifndef LOGGER_FORMAT_IMPL_H
|
||||
#define LOGGER_FORMAT_IMPL_H
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* ****************************************************************
|
||||
* Disclaimer: Most of this code is shamelessly stolen from fmtlib
|
||||
* ****************************************************************
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Utility functions
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#define FMT_POWERS_OF_10(factor) \
|
||||
factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
|
||||
(factor)*1000000, (factor)*10000000, (factor)*100000000, \
|
||||
(factor)*1000000000
|
||||
|
||||
template <typename T>
|
||||
constexpr int count_digits_fallback(T n) {
|
||||
int count = 1;
|
||||
for (;;) {
|
||||
if (n < 10) return count;
|
||||
if (n < 100) return count + 1;
|
||||
if (n < 1000) return count + 2;
|
||||
if (n < 10000) return count + 3;
|
||||
n /= 10000u;
|
||||
count += 4;
|
||||
}
|
||||
}
|
||||
|
||||
inline int do_count_digits(uint64_t n) {
|
||||
// Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
|
||||
static constexpr uint8_t bsr2log10[] = {
|
||||
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5,
|
||||
6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
|
||||
10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
|
||||
15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
|
||||
auto t = bsr2log10[__builtin_clzll(n | 1) ^ 63];
|
||||
static constexpr const uint64_t zero_or_powers_of_10[] = {
|
||||
0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL),
|
||||
10000000000000000000ULL};
|
||||
return t - (n < zero_or_powers_of_10[t]);
|
||||
}
|
||||
|
||||
constexpr inline auto count_digits(uint64_t n) -> int {
|
||||
if (!std::is_constant_evaluated()) {
|
||||
return do_count_digits(n);
|
||||
}
|
||||
return count_digits_fallback(n);
|
||||
}
|
||||
|
||||
|
||||
// Converts value in the range [0, 100) to a string.
|
||||
constexpr inline const char* digits2(size_t value) {
|
||||
// GCC generates slightly better code when value is pointer-size.
|
||||
return &"0001020304050607080910111213141516171819"
|
||||
"2021222324252627282930313233343536373839"
|
||||
"4041424344454647484950515253545556575859"
|
||||
"6061626364656667686970717273747576777879"
|
||||
"8081828384858687888990919293949596979899"[value * 2];
|
||||
}
|
||||
|
||||
|
||||
constexpr inline void copy2(char* dst, const char* src) {
|
||||
if (!std::is_constant_evaluated()) {
|
||||
std::memcpy(dst, src, 2);
|
||||
return;
|
||||
}
|
||||
*dst++ = static_cast<char>(*src++);
|
||||
*dst = static_cast<char>(*src);
|
||||
}
|
||||
|
||||
template <typename uint_t>
|
||||
constexpr inline void format_decimal(char* out, uint_t value, int size) {
|
||||
if (count_digits(value) > size) return;
|
||||
|
||||
out += size;
|
||||
while (value >= 100) {
|
||||
out -= 2;
|
||||
copy2(out, digits2(static_cast<size_t>(value % 100)));
|
||||
value /= 100;
|
||||
}
|
||||
|
||||
if (value < 10) {
|
||||
*--out = static_cast<char>('0' + value);
|
||||
return;
|
||||
}
|
||||
|
||||
out -= 2;
|
||||
copy2(out, digits2(static_cast<size_t>(value)));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Integral types
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
template <std::unsigned_integral uint_t>
|
||||
constexpr inline void format_int(char* out, uint_t value, fmt_data_t fmt_node) {
|
||||
format_decimal(out, value, fmt_node.length);
|
||||
}
|
||||
|
||||
template <std::signed_integral uint_t>
|
||||
constexpr inline void format_int(char* out, uint_t value, fmt_data_t fmt_node) {
|
||||
auto abs_value = static_cast<uint64_t>(value);
|
||||
const bool negative = value < 0;
|
||||
|
||||
if (negative) abs_value = 0 - abs_value;
|
||||
format_decimal(out + 1 * (negative), abs_value,
|
||||
fmt_node.length - 1 * (negative));
|
||||
|
||||
if (negative) *out = '-';
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Floating point types
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
template <std::floating_point float_t>
|
||||
constexpr inline void format_float(char* out, float_t value,
|
||||
fmt_data_t fmt_data) {
|
||||
|
||||
*(out) = 'f';
|
||||
*(out + fmt_data.length - fmt_data.precision - 1) = '.';
|
||||
}
|
||||
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
#endif // LOGGER_FORMAT_IMPL_H
|
||||
249
const_fmt/parse.h
Normal file
249
const_fmt/parse.h
Normal file
@@ -0,0 +1,249 @@
|
||||
#ifndef LOGGER_PARSE_H
|
||||
#define LOGGER_PARSE_H
|
||||
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
// clang-format off
|
||||
|
||||
/*
|
||||
*
|
||||
* fmtlib grammar:
|
||||
* replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
|
||||
* arg_id ::= integer | identifier
|
||||
* integer ::= digit+
|
||||
* digit ::= "0"..."9"
|
||||
* identifier ::= id_start id_continue*
|
||||
* id_start ::= "a"..."z" | "A"..."Z" | "_"
|
||||
* id_continue ::= id_start | digit
|
||||
*
|
||||
* format_spec ::= [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type]
|
||||
* fill ::= <a character other than '{' or '}'>
|
||||
* align ::= "<" | ">" | "^"
|
||||
* sign ::= "+" | "-" | " "
|
||||
* width ::= integer | "{" [arg_id] "}"
|
||||
* precision ::= integer | "{" [arg_id] "}"
|
||||
* type ::= "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" |
|
||||
* "o" | "p" | "s" | "x" | "X"
|
||||
*
|
||||
* grammar:
|
||||
* string ::= { braces | <a character other than '{' or '}'> }
|
||||
* braces ::= "{" [":" fmt_string] "}"
|
||||
* fmt_string ::= ["0"][width]["." precision][type]
|
||||
*
|
||||
*/
|
||||
|
||||
// clang-format on
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Functions to determine the size of data structures
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
template <ConstString s>
|
||||
constexpr inline unsigned count_braces() {
|
||||
unsigned result = 0;
|
||||
|
||||
for (unsigned i = 0; i < s.size(); ++i) {
|
||||
if (s[i] == '{') ++result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <ConstString s>
|
||||
constexpr inline unsigned strlen_braces() {
|
||||
unsigned result = 0;
|
||||
|
||||
bool brace_open = false;
|
||||
for (unsigned i = 0; i < s.size(); ++i) {
|
||||
if (!brace_open) {
|
||||
if (s[i] == '{') {
|
||||
brace_open = true;
|
||||
++result;
|
||||
}
|
||||
} else {
|
||||
++result;
|
||||
if (s[i] == '}') {
|
||||
brace_open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <ConstString s>
|
||||
constexpr inline int get_ast_len() {
|
||||
return (s.size() - strlen_braces<s>() + count_braces<s>());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Parsing functions
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
template <ConstString s>
|
||||
constexpr inline bool is_digit(unsigned i) {
|
||||
return (s[i] > 47) && (s[i] < 58);
|
||||
}
|
||||
|
||||
|
||||
template <ConstString s>
|
||||
constexpr inline parse_result_t<unsigned> parse_number(unsigned i) {
|
||||
unsigned number = 0;
|
||||
|
||||
if (!is_digit<s>(i)) {
|
||||
return {false, i, number};
|
||||
}
|
||||
|
||||
while ((i < s.size()) && is_digit<s>(i)) {
|
||||
number = number * 10;
|
||||
number += (s[i] - 48);
|
||||
++i;
|
||||
}
|
||||
|
||||
return {true, i, number};
|
||||
}
|
||||
|
||||
template <ConstString s>
|
||||
constexpr inline parse_result_t<FormatType> parse_type(unsigned i) {
|
||||
switch (s[i]) {
|
||||
case 's':
|
||||
return {true, ++i, FormatType::s};
|
||||
case 'c':
|
||||
return {true, ++i, FormatType::c};
|
||||
case 'b':
|
||||
return {true, ++i, FormatType::b};
|
||||
case 'B':
|
||||
return {true, ++i, FormatType::B};
|
||||
case 'd':
|
||||
return {true, ++i, FormatType::d};
|
||||
case 'o':
|
||||
return {true, ++i, FormatType::o};
|
||||
case 'x':
|
||||
return {true, ++i, FormatType::x};
|
||||
case 'X':
|
||||
return {true, ++i, FormatType::X};
|
||||
case 'a':
|
||||
return {true, ++i, FormatType::a};
|
||||
case 'A':
|
||||
return {true, ++i, FormatType::A};
|
||||
case 'e':
|
||||
return {true, ++i, FormatType::e};
|
||||
case 'E':
|
||||
return {true, ++i, FormatType::E};
|
||||
case 'f':
|
||||
return {true, ++i, FormatType::f};
|
||||
case 'F':
|
||||
return {true, ++i, FormatType::F};
|
||||
case 'g':
|
||||
return {true, ++i, FormatType::g};
|
||||
case 'G':
|
||||
return {true, ++i, FormatType::G};
|
||||
case 'p':
|
||||
return {true, ++i, FormatType::p};
|
||||
default:
|
||||
return {false, i, FormatType::s};
|
||||
}
|
||||
}
|
||||
|
||||
template <ConstString s>
|
||||
constexpr inline parse_result_t<fmt_node_t> parse_fmt_string(unsigned i) {
|
||||
fmt_node_t result;
|
||||
|
||||
if (s[i] == '0') {
|
||||
++i;
|
||||
result.has_zero_padding = true;
|
||||
}
|
||||
|
||||
if (is_digit<s>(i)) {
|
||||
auto [is_valid, new_i, number] = parse_number<s>(i);
|
||||
if (!is_valid) return {false, i, result};
|
||||
i = new_i;
|
||||
result.length = number;
|
||||
}
|
||||
|
||||
if (s[i] == '.') {
|
||||
++i;
|
||||
auto [is_valid, new_i, number] = parse_number<s>(i);
|
||||
if (!is_valid) return {false, i, result};
|
||||
i = new_i;
|
||||
result.precision = number;
|
||||
}
|
||||
|
||||
if (s[i] != '}') {
|
||||
auto [is_valid, new_i, type] = parse_type<s>(i);
|
||||
if (!is_valid) return {false, i, result};
|
||||
i = new_i;
|
||||
result.type = type;
|
||||
}
|
||||
|
||||
return {true, i, result};
|
||||
}
|
||||
|
||||
template <ConstString s>
|
||||
constexpr inline parse_result_t<fmt_node_t> parse_braces(unsigned i) {
|
||||
if (s[i] == '}') {
|
||||
++i;
|
||||
return {true, i, {}};
|
||||
} else if (s[i] == ':') {
|
||||
++i;
|
||||
|
||||
auto [is_valid, new_i, format_node] = parse_fmt_string<s>(i);
|
||||
if (!is_valid) return {false, i, {}};
|
||||
i = new_i;
|
||||
|
||||
if (s[i] == '}') {
|
||||
++i;
|
||||
return {true, i, format_node};
|
||||
}
|
||||
}
|
||||
|
||||
return {false, i, {}};
|
||||
}
|
||||
|
||||
template <ConstString s>
|
||||
constexpr inline parse_result_t<string_result_t<get_ast_len<s>()>> parse_string() {
|
||||
parse_result_t<string_result_t<get_ast_len<s>()>> result;
|
||||
result.is_valid = true;
|
||||
|
||||
unsigned ast_position = 0;
|
||||
|
||||
unsigned i = 0;
|
||||
while (i < s.size()) {
|
||||
if (s[i] == '{') {
|
||||
++i;
|
||||
|
||||
auto [is_valid, new_i, format_node] = parse_braces<s>(i);
|
||||
if (!is_valid) return {false, i, {}};
|
||||
i = new_i;
|
||||
result.value[ast_position++].set_node(format_node);
|
||||
|
||||
} else if (s[i] == '}') {
|
||||
return {false, i, {}};
|
||||
} else {
|
||||
result.value[ast_position++].set_char(s[i]);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
|
||||
#endif // LOGGER_PARSE_H
|
||||
142
const_fmt/types.h
Normal file
142
const_fmt/types.h
Normal file
@@ -0,0 +1,142 @@
|
||||
#ifndef LOGGER_TYPES_H
|
||||
#define LOGGER_TYPES_H
|
||||
|
||||
|
||||
#include <array>
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Types used everywhere
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
template <std::size_t N>
|
||||
class ConstString {
|
||||
public:
|
||||
constexpr ConstString(const char (&content)[N]) noexcept {
|
||||
std::copy(std::begin(content), std::end(content),
|
||||
std::begin(m_content));
|
||||
}
|
||||
|
||||
constexpr char operator[](std::size_t index) const noexcept {
|
||||
return m_content[index];
|
||||
}
|
||||
|
||||
constexpr std::size_t size() const noexcept {
|
||||
return N - 1;
|
||||
}
|
||||
|
||||
std::array<char, N> m_content;
|
||||
};
|
||||
|
||||
|
||||
template <std::size_t N>
|
||||
ConstString(const char (&)[N]) -> ConstString<N>;
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Types used mainly in parsing
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
enum class FormatType { s, c, b, B, d, o, x, X, a, A, e, E, f, F, g, G, p };
|
||||
|
||||
|
||||
template <typename value_t>
|
||||
struct parse_result_t {
|
||||
bool is_valid = false;
|
||||
unsigned new_index = 0;
|
||||
value_t value;
|
||||
};
|
||||
|
||||
|
||||
struct fmt_node_t {
|
||||
bool has_zero_padding = false;
|
||||
int length = 6;
|
||||
int precision = 2;
|
||||
FormatType type = FormatType::s;
|
||||
|
||||
constexpr bool operator==(const fmt_node_t& rhs) const {
|
||||
return (rhs.has_zero_padding == has_zero_padding) &&
|
||||
(rhs.length == length) && (rhs.precision == precision) &&
|
||||
(rhs.type == type);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ast_node_t {
|
||||
public:
|
||||
constexpr ast_node_t() {
|
||||
}
|
||||
|
||||
constexpr ast_node_t(char c) : m_is_char(true), m_c(c) {
|
||||
}
|
||||
|
||||
constexpr ast_node_t(fmt_node_t node) : m_is_char(false), m_node(node) {
|
||||
}
|
||||
|
||||
constexpr bool operator==(const ast_node_t& rhs) const {
|
||||
return ((rhs.m_is_char == m_is_char) && (rhs.m_c == m_c)) ||
|
||||
((rhs.m_is_char != m_is_char) && (rhs.m_node == m_node));
|
||||
}
|
||||
|
||||
constexpr void set_char(char c) {
|
||||
m_c = c;
|
||||
m_is_char = true;
|
||||
}
|
||||
|
||||
constexpr void set_node(fmt_node_t node) {
|
||||
m_node = node;
|
||||
m_is_char = false;
|
||||
}
|
||||
|
||||
constexpr char get_char() const {
|
||||
return m_c;
|
||||
}
|
||||
|
||||
constexpr fmt_node_t get_node() const {
|
||||
return m_node;
|
||||
}
|
||||
|
||||
constexpr bool is_char() const {
|
||||
return m_is_char;
|
||||
}
|
||||
|
||||
bool m_is_char = false;
|
||||
char m_c = 'c';
|
||||
fmt_node_t m_node;
|
||||
};
|
||||
|
||||
|
||||
template <std::size_t N>
|
||||
using string_result_t = std::array<ast_node_t, N>;
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Types used mainly in formatting
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
struct fmt_data_t {
|
||||
bool has_zero_padding = 0;
|
||||
std::size_t length = 0;
|
||||
std::size_t precision = 0;
|
||||
FormatType type;
|
||||
|
||||
std::size_t position = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
#endif // LOGGER_TYPES_H
|
||||
111
const_fmt/utility.h
Normal file
111
const_fmt/utility.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#ifndef LOGGER_UTILITY_H
|
||||
#define LOGGER_UTILITY_H
|
||||
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
|
||||
constexpr inline std::size_t const_pow(std::size_t base, std::size_t pow) {
|
||||
if (pow == 0)
|
||||
return 1;
|
||||
else
|
||||
return base * const_pow(base, pow - 1);
|
||||
}
|
||||
|
||||
|
||||
template <std::size_t t_n>
|
||||
consteval inline std::array<char, t_n> get_init_array(char val) {
|
||||
std::array<char, t_n> result;
|
||||
|
||||
for (auto& c : result)
|
||||
c = val;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template <auto ast>
|
||||
consteval inline std::size_t count_ast_format_nodes() {
|
||||
std::size_t result = 0;
|
||||
|
||||
for (const auto& node : ast)
|
||||
if (!node.is_char()) ++result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template <auto ast>
|
||||
consteval inline std::array<fmt_data_t, count_ast_format_nodes<ast>()> get_fmt_data() {
|
||||
std::array<fmt_data_t, count_ast_format_nodes<ast>()> result = {};
|
||||
|
||||
std::size_t position = 0;
|
||||
std::size_t i = 0;
|
||||
|
||||
for (const auto& node : ast) {
|
||||
if (!node.is_char()) {
|
||||
const auto fmt_node = node.get_node();
|
||||
|
||||
result[i].has_zero_padding = fmt_node.has_zero_padding;
|
||||
result[i].length = fmt_node.length;
|
||||
result[i].precision = fmt_node.precision;
|
||||
result[i].type = fmt_node.type;
|
||||
result[i].position = position;
|
||||
|
||||
++i;
|
||||
position += fmt_node.length;
|
||||
} else {
|
||||
++position;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template <typename elem_t, std::size_t t_n>
|
||||
consteval inline std::array<elem_t, t_n - 1>
|
||||
drop_first(std::array<elem_t, t_n> array) {
|
||||
static_assert(t_n > 0,
|
||||
"Can't drop first element of array with no elements");
|
||||
|
||||
std::array<elem_t, t_n - 1> result;
|
||||
|
||||
std::copy(array.begin() + 1, array.end(), result.begin());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template <auto t_ast>
|
||||
consteval inline int get_ast_output_len() {
|
||||
unsigned result = 0;
|
||||
|
||||
for (const auto& ast_node : t_ast) {
|
||||
if (ast_node.is_char())
|
||||
++result;
|
||||
else
|
||||
result += ast_node.get_node().length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr inline std::size_t const_strlen(const char* arg) {
|
||||
if (std::is_constant_evaluated()) {
|
||||
return *arg ? 1 + const_strlen(arg + 1) : 0;
|
||||
} else {
|
||||
return strlen(arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
#endif // LOGGER_UTILITY_H
|
||||
Reference in New Issue
Block a user