Renamed inc directory to const_fmt

This commit is contained in:
2022-02-09 17:51:37 +01:00
parent d26cf43b32
commit 55ff861f67
11 changed files with 9 additions and 11 deletions

52
const_fmt/Logger.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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