Changed the way detail::format_args works: A preprocessing step yields the whole array without formatted values, values are then formatted in place
This commit is contained in:
parent
5a0185e074
commit
8ccceeef0d
225
inc/format.h
225
inc/format.h
@ -15,14 +15,11 @@ constexpr void check_fmt_params() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <ConstString s>
|
template <auto t_ast>
|
||||||
constexpr int get_output_len() {
|
consteval int get_ast_output_len() {
|
||||||
constexpr auto parse_result = parse_string<s>();
|
|
||||||
static_assert(parse_result.is_valid, "Syntax error in format string");
|
|
||||||
|
|
||||||
unsigned result = 0;
|
unsigned result = 0;
|
||||||
|
|
||||||
for (const auto& ast_node : parse_result.value) {
|
for (const auto& ast_node : t_ast) {
|
||||||
if (ast_node.is_char())
|
if (ast_node.is_char())
|
||||||
++result;
|
++result;
|
||||||
else
|
else
|
||||||
@ -32,112 +29,136 @@ constexpr int get_output_len() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: See if this is possible with <charconv>
|
template <ConstString s>
|
||||||
// TODO: Steal some code from fmtlib
|
constexpr int get_output_len() {
|
||||||
// TODO: In case of error, set chars to all 'f's
|
constexpr auto parse_result = parse_string<s>();
|
||||||
template <fmt_node_t fmt_node, std::integral arg_t>
|
static_assert(parse_result.is_valid, "Syntax error in format string");
|
||||||
constexpr std::array<char, fmt_node.length> format_arg(arg_t arg) {
|
|
||||||
check_fmt_params<fmt_node, arg_t>();
|
|
||||||
|
|
||||||
auto result = get_init_array<fmt_node>();
|
return get_ast_output_len<parse_result.value>();
|
||||||
|
|
||||||
unsigned offset = 0;
|
|
||||||
|
|
||||||
if (arg < 0) {
|
|
||||||
result[0] = '-';
|
|
||||||
arg = -arg;
|
|
||||||
++offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = result.size() - 1; (i >= static_cast<int>(offset)) && (arg > 0); --i) {
|
|
||||||
result[i] = arg % 10 + 48;
|
|
||||||
arg = arg / 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: See if this is possible with <charconv>
|
//// TODO: See if this is possible with <charconv>
|
||||||
// TODO: Steal some code from fmtlib
|
//// TODO: Steal some code from fmtlib
|
||||||
// TODO: In case of error, set chars to all 'f's
|
//// TODO: In case of error, set chars to all 'f's
|
||||||
template <fmt_node_t fmt_node, std::floating_point arg_t>
|
// template <fmt_node_t fmt_node, std::integral arg_t>
|
||||||
constexpr std::array<char, fmt_node.length> format_arg(arg_t arg) {
|
// constexpr std::array<char, fmt_node.length> format_arg(arg_t arg) {
|
||||||
check_fmt_params<fmt_node, arg_t>();
|
// check_fmt_params<fmt_node, arg_t>();
|
||||||
|
//
|
||||||
|
// auto result = get_init_array<fmt_node>();
|
||||||
|
//
|
||||||
|
// unsigned offset = 0;
|
||||||
|
//
|
||||||
|
// if (arg < 0) {
|
||||||
|
// result[0] = '-';
|
||||||
|
// arg = -arg;
|
||||||
|
// ++offset;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for (int i = result.size() - 1;
|
||||||
|
// (i >= static_cast<int>(offset)) && (arg > 0); --i) {
|
||||||
|
// result[i] = arg % 10 + 48;
|
||||||
|
// arg = arg / 10;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//// TODO: See if this is possible with <charconv>
|
||||||
|
//// TODO: Steal some code from fmtlib
|
||||||
|
//// TODO: In case of error, set chars to all 'f's
|
||||||
|
// template <fmt_node_t fmt_node, std::floating_point arg_t>
|
||||||
|
// constexpr std::array<char, fmt_node.length> format_arg(arg_t arg) {
|
||||||
|
// check_fmt_params<fmt_node, arg_t>();
|
||||||
|
//
|
||||||
|
// constexpr unsigned point_index = fmt_node.length - fmt_node.precision -
|
||||||
|
// 1; constexpr unsigned multiplier = const_pow(10, fmt_node.precision);
|
||||||
|
//
|
||||||
|
// auto result = get_init_array<fmt_node>();
|
||||||
|
// result[point_index] = '.';
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// arg = arg * multiplier;
|
||||||
|
//
|
||||||
|
// for (int i = result.size() - 1;
|
||||||
|
// (i > static_cast<int>(point_index)) && (arg >= 1); --i) {
|
||||||
|
//
|
||||||
|
// result[i] = static_cast<int>(arg) % 10 + 48;
|
||||||
|
// arg = arg / 10;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for (int i = point_index - 1; (i >= 0) && (arg >= 1); --i) {
|
||||||
|
// result[i] = static_cast<int>(arg) % 10 + 48;
|
||||||
|
// arg = arg / 10;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//// TODO: Steal some code from fmtlib
|
||||||
|
//// TODO: In case of error, set chars to all 'f's
|
||||||
|
// template <fmt_node_t fmt_node>
|
||||||
|
// constexpr std::array<char, fmt_node.length> format_arg(const char* arg) {
|
||||||
|
// check_fmt_params<fmt_node, const char*>();
|
||||||
|
//
|
||||||
|
// std::array<char, fmt_node.length> result;
|
||||||
|
//
|
||||||
|
// for (auto& c : result) {
|
||||||
|
// if (*arg != '\0')
|
||||||
|
// c = *(arg++);
|
||||||
|
// else
|
||||||
|
// c = ' ';
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// template <auto t_ast, unsigned t_ast_i = 0, unsigned t_result_i = 0,
|
||||||
|
// typename char_array_t>
|
||||||
|
// constexpr char_array_t format_args(char_array_t result) {
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
constexpr unsigned point_index = fmt_node.length - fmt_node.precision - 1;
|
template <auto ast>
|
||||||
constexpr unsigned multiplier = const_pow(10, fmt_node.precision);
|
consteval std::array<char, get_ast_output_len<ast>()> get_preproc_string() {
|
||||||
|
auto result = get_zero_array<get_ast_output_len<ast>()>();
|
||||||
|
|
||||||
auto result = get_init_array<fmt_node>();
|
int i = 0;
|
||||||
result[point_index] = '.';
|
|
||||||
|
|
||||||
|
for (const auto& ast_node : ast) {
|
||||||
arg = arg * multiplier;
|
if (ast_node.is_char())
|
||||||
|
result[i++] = ast_node.get_char();
|
||||||
for (int i = result.size() - 1;
|
|
||||||
(i > static_cast<int>(point_index)) && (arg >= 1); --i) {
|
|
||||||
|
|
||||||
result[i] = static_cast<int>(arg) % 10 + 48;
|
|
||||||
arg = arg / 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = point_index - 1; (i >= 0) && (arg >= 1); --i) {
|
|
||||||
result[i] = static_cast<int>(arg) % 10 + 48;
|
|
||||||
arg = arg / 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Steal some code from fmtlib
|
|
||||||
// TODO: In case of error, set chars to all 'f's
|
|
||||||
template <fmt_node_t fmt_node>
|
|
||||||
constexpr std::array<char, fmt_node.length> format_arg(const char* arg) {
|
|
||||||
check_fmt_params<fmt_node, const char*>();
|
|
||||||
|
|
||||||
std::array<char, fmt_node.length> result;
|
|
||||||
|
|
||||||
for (auto& c : result) {
|
|
||||||
if (*arg != '\0')
|
|
||||||
c = *(arg++);
|
|
||||||
else
|
else
|
||||||
c = ' ';
|
i += ast_node.get_node().length;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <auto t_ast, unsigned t_ast_i = 0, unsigned t_result_i = 0,
|
template <std::integral arg_t>
|
||||||
typename char_array_t>
|
constexpr void format_arg(char* dest, fmt_data_t fmt_data, arg_t arg) {
|
||||||
constexpr char_array_t format_args(char_array_t result) {
|
*(dest + fmt_data.position) = 'i';
|
||||||
return result;
|
// dest = dest + fmt_data.position;
|
||||||
|
// format_int(dest, fmt_data.length, arg);
|
||||||
|
};
|
||||||
|
template <std::floating_point arg_t>
|
||||||
|
constexpr void format_arg(char* dest, fmt_data_t fmt_data, arg_t) {
|
||||||
|
*(dest + fmt_data.position) = 'f';
|
||||||
|
};
|
||||||
|
constexpr void format_arg(char* dest, fmt_data_t fmt_data, const char*) {
|
||||||
|
*(dest + fmt_data.position) = 'c';
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <auto ast>
|
||||||
|
constexpr void format_args(char*) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <auto t_ast, unsigned t_ast_i = 0, unsigned t_result_i = 0,
|
template <auto fmt_data_array, typename first_arg_t, typename... args_t>
|
||||||
typename char_array_t, typename first_arg_t, typename... other_args_t>
|
constexpr void format_args(char* dest, first_arg_t first_arg, args_t... args) {
|
||||||
constexpr char_array_t format_args(char_array_t result, first_arg_t first_arg,
|
format_arg(dest, fmt_data_array[0], first_arg);
|
||||||
other_args_t... other_args) {
|
format_args<drop_first(fmt_data_array)>(dest, args...);
|
||||||
if constexpr (t_ast_i >= t_ast.size()) {
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
constexpr auto ast_node = t_ast[t_ast_i];
|
|
||||||
|
|
||||||
if (ast_node.is_char()) {
|
|
||||||
result[t_result_i] = ast_node.get_char();
|
|
||||||
return format_args<t_ast, t_ast_i + 1, t_result_i + 1>(
|
|
||||||
result, first_arg, other_args...);
|
|
||||||
} else {
|
|
||||||
constexpr auto fmt_node = ast_node.get_node();
|
|
||||||
const auto formatted_arg = format_arg<fmt_node>(first_arg);
|
|
||||||
|
|
||||||
std::copy(formatted_arg.begin(), formatted_arg.end(),
|
|
||||||
result.begin() + t_result_i);
|
|
||||||
|
|
||||||
return format_args<t_ast, t_ast_i + 1,
|
|
||||||
t_result_i + fmt_node.length>(result,
|
|
||||||
other_args...);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -146,12 +167,14 @@ constexpr char_array_t format_args(char_array_t result, first_arg_t first_arg,
|
|||||||
|
|
||||||
template <detail::ConstString s, typename... args_t>
|
template <detail::ConstString s, typename... args_t>
|
||||||
constexpr std::array<char, detail::get_output_len<s>()> format(args_t... args) {
|
constexpr std::array<char, detail::get_output_len<s>()> format(args_t... args) {
|
||||||
constexpr auto parse_result = detail::parse_string<s>();
|
constexpr auto ast = detail::parse_string<s>().value;
|
||||||
static_assert(parse_result.is_valid, "Syntax error in format string");
|
constexpr auto fmt_data = detail::get_fmt_data<ast>();
|
||||||
|
|
||||||
std::array<char, detail::get_output_len<s>()> result = {};
|
auto result = detail::get_preproc_string<ast>();
|
||||||
|
|
||||||
return detail::format_args<parse_result.value>(result, args...);
|
detail::format_args<fmt_data>(result.begin(), args...);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "ConstString.h"
|
#include "ConstString.h"
|
||||||
#include "parse_types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#ifndef LOGGER_PARSE_TYPES_H
|
#ifndef LOGGER_TYPES_H
|
||||||
#define LOGGER_PARSE_TYPES_H
|
#define LOGGER_TYPES_H
|
||||||
|
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
@ -8,6 +8,13 @@
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 };
|
enum class FormatType { s, c, b, B, d, o, x, X, a, A, e, E, f, F, g, G, p };
|
||||||
|
|
||||||
|
|
||||||
@ -81,7 +88,24 @@ template <std::size_t N>
|
|||||||
using string_result_t = std::array<ast_node_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
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
#endif // LOGGER_PARSE_TYPES_H
|
#endif // LOGGER_TYPES_H
|
||||||
@ -2,7 +2,7 @@
|
|||||||
#define LOGGER_UTILITY_H
|
#define LOGGER_UTILITY_H
|
||||||
|
|
||||||
|
|
||||||
#include "parse_types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
@ -16,6 +16,16 @@ constexpr std::size_t const_pow(std::size_t base, std::size_t pow) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <std::size_t t_n>
|
||||||
|
constexpr std::array<char, t_n> get_zero_array() {
|
||||||
|
std::array<char, t_n> result;
|
||||||
|
|
||||||
|
for (auto& c : result)
|
||||||
|
c = '0';
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
template <fmt_node_t fmt_node>
|
template <fmt_node_t fmt_node>
|
||||||
constexpr std::array<char, fmt_node.length> get_init_array() {
|
constexpr std::array<char, fmt_node.length> get_init_array() {
|
||||||
std::array<char, fmt_node.length> result;
|
std::array<char, fmt_node.length> result;
|
||||||
@ -32,6 +42,59 @@ constexpr std::array<char, fmt_node.length> get_init_array() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <auto ast>
|
||||||
|
consteval 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 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 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user