From 8ccceeef0dc78c022b79d76dc75adc1d2e42317b Mon Sep 17 00:00:00 2001 From: Andreas Tsouchlos Date: Thu, 25 Nov 2021 10:16:09 +0100 Subject: [PATCH] Changed the way detail::format_args works: A preprocessing step yields the whole array without formatted values, values are then formatted in place --- inc/format.h | 225 ++++++++++++++++++--------------- inc/parse.h | 2 +- inc/{parse_types.h => types.h} | 30 ++++- inc/utility.h | 65 +++++++++- 4 files changed, 216 insertions(+), 106 deletions(-) rename inc/{parse_types.h => types.h} (82%) diff --git a/inc/format.h b/inc/format.h index 38b09d8..513aa89 100644 --- a/inc/format.h +++ b/inc/format.h @@ -15,14 +15,11 @@ constexpr void check_fmt_params() { } -template -constexpr int get_output_len() { - constexpr auto parse_result = parse_string(); - static_assert(parse_result.is_valid, "Syntax error in format string"); - +template +consteval int get_ast_output_len() { unsigned result = 0; - for (const auto& ast_node : parse_result.value) { + for (const auto& ast_node : t_ast) { if (ast_node.is_char()) ++result; else @@ -32,112 +29,136 @@ constexpr int get_output_len() { return result; } -// TODO: See if this is possible with -// TODO: Steal some code from fmtlib -// TODO: In case of error, set chars to all 'f's -template -constexpr std::array format_arg(arg_t arg) { - check_fmt_params(); +template +constexpr int get_output_len() { + constexpr auto parse_result = parse_string(); + static_assert(parse_result.is_valid, "Syntax error in format string"); - auto result = get_init_array(); - - unsigned offset = 0; - - if (arg < 0) { - result[0] = '-'; - arg = -arg; - ++offset; - } - - for (int i = result.size() - 1; (i >= static_cast(offset)) && (arg > 0); --i) { - result[i] = arg % 10 + 48; - arg = arg / 10; - } - - return result; + return get_ast_output_len(); } -// TODO: See if this is possible with -// TODO: Steal some code from fmtlib -// TODO: In case of error, set chars to all 'f's -template -constexpr std::array format_arg(arg_t arg) { - check_fmt_params(); +//// TODO: See if this is possible with +//// TODO: Steal some code from fmtlib +//// TODO: In case of error, set chars to all 'f's +// template +// constexpr std::array format_arg(arg_t arg) { +// check_fmt_params(); +// +// auto result = get_init_array(); +// +// unsigned offset = 0; +// +// if (arg < 0) { +// result[0] = '-'; +// arg = -arg; +// ++offset; +// } +// +// for (int i = result.size() - 1; +// (i >= static_cast(offset)) && (arg > 0); --i) { +// result[i] = arg % 10 + 48; +// arg = arg / 10; +// } +// +// return result; +// } +// +//// TODO: See if this is possible with +//// TODO: Steal some code from fmtlib +//// TODO: In case of error, set chars to all 'f's +// template +// constexpr std::array format_arg(arg_t arg) { +// check_fmt_params(); +// +// 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(); +// result[point_index] = '.'; +// +// +// arg = arg * multiplier; +// +// for (int i = result.size() - 1; +// (i > static_cast(point_index)) && (arg >= 1); --i) { +// +// result[i] = static_cast(arg) % 10 + 48; +// arg = arg / 10; +// } +// +// for (int i = point_index - 1; (i >= 0) && (arg >= 1); --i) { +// result[i] = static_cast(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 +// constexpr std::array format_arg(const char* arg) { +// check_fmt_params(); +// +// std::array result; +// +// for (auto& c : result) { +// if (*arg != '\0') +// c = *(arg++); +// else +// c = ' '; +// } +// +// return result; +// } +// +// +// template +// constexpr char_array_t format_args(char_array_t result) { +// return result; +// } - constexpr unsigned point_index = fmt_node.length - fmt_node.precision - 1; - constexpr unsigned multiplier = const_pow(10, fmt_node.precision); +template +consteval std::array()> get_preproc_string() { + auto result = get_zero_array()>(); - auto result = get_init_array(); - result[point_index] = '.'; + int i = 0; - - arg = arg * multiplier; - - for (int i = result.size() - 1; - (i > static_cast(point_index)) && (arg >= 1); --i) { - - result[i] = static_cast(arg) % 10 + 48; - arg = arg / 10; - } - - for (int i = point_index - 1; (i >= 0) && (arg >= 1); --i) { - result[i] = static_cast(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 -constexpr std::array format_arg(const char* arg) { - check_fmt_params(); - - std::array result; - - for (auto& c : result) { - if (*arg != '\0') - c = *(arg++); + for (const auto& ast_node : ast) { + if (ast_node.is_char()) + result[i++] = ast_node.get_char(); else - c = ' '; + i += ast_node.get_node().length; } return result; } -template -constexpr char_array_t format_args(char_array_t result) { - return result; +template +constexpr void format_arg(char* dest, fmt_data_t fmt_data, arg_t arg) { + *(dest + fmt_data.position) = 'i'; +// dest = dest + fmt_data.position; +// format_int(dest, fmt_data.length, arg); +}; +template +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 +constexpr void format_args(char*) { } -template -constexpr char_array_t format_args(char_array_t result, first_arg_t first_arg, - other_args_t... other_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( - result, first_arg, other_args...); - } else { - constexpr auto fmt_node = ast_node.get_node(); - const auto formatted_arg = format_arg(first_arg); - - std::copy(formatted_arg.begin(), formatted_arg.end(), - result.begin() + t_result_i); - - return format_args(result, - other_args...); - } - } +template +constexpr void format_args(char* dest, first_arg_t first_arg, args_t... args) { + format_arg(dest, fmt_data_array[0], first_arg); + format_args(dest, args...); } @@ -146,12 +167,14 @@ constexpr char_array_t format_args(char_array_t result, first_arg_t first_arg, template constexpr std::array()> format(args_t... args) { - constexpr auto parse_result = detail::parse_string(); - static_assert(parse_result.is_valid, "Syntax error in format string"); + constexpr auto ast = detail::parse_string().value; + constexpr auto fmt_data = detail::get_fmt_data(); - std::array()> result = {}; + auto result = detail::get_preproc_string(); - return detail::format_args(result, args...); + detail::format_args(result.begin(), args...); + + return result; } diff --git a/inc/parse.h b/inc/parse.h index 03fcc8b..a9a1260 100644 --- a/inc/parse.h +++ b/inc/parse.h @@ -3,7 +3,7 @@ #include "ConstString.h" -#include "parse_types.h" +#include "types.h" // clang-format off diff --git a/inc/parse_types.h b/inc/types.h similarity index 82% rename from inc/parse_types.h rename to inc/types.h index 46b551f..d0fcd81 100644 --- a/inc/parse_types.h +++ b/inc/types.h @@ -1,5 +1,5 @@ -#ifndef LOGGER_PARSE_TYPES_H -#define LOGGER_PARSE_TYPES_H +#ifndef LOGGER_TYPES_H +#define LOGGER_TYPES_H #include @@ -8,6 +8,13 @@ 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 }; @@ -81,7 +88,24 @@ template using string_result_t = std::array; +/* + * + * 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_PARSE_TYPES_H +#endif // LOGGER_TYPES_H diff --git a/inc/utility.h b/inc/utility.h index 26f41a7..284a3e4 100644 --- a/inc/utility.h +++ b/inc/utility.h @@ -2,7 +2,7 @@ #define LOGGER_UTILITY_H -#include "parse_types.h" +#include "types.h" namespace detail { @@ -16,6 +16,16 @@ constexpr std::size_t const_pow(std::size_t base, std::size_t pow) { } +template +constexpr std::array get_zero_array() { + std::array result; + + for (auto& c : result) + c = '0'; + + return result; +} + template constexpr std::array get_init_array() { std::array result; @@ -32,6 +42,59 @@ constexpr std::array get_init_array() { } +template +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 +consteval std::array()> get_fmt_data() { + std::array()> 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 +consteval std::array +drop_first(std::array array) { + static_assert(t_n > 0, + "Can't drop first element of array with no elements"); + + std::array result; + + std::copy(array.begin() + 1, array.end(), result.begin()); + + return result; +} + + } // namespace detail