From 8ae0f5a9a83aa58f2bd9eacfb51369b0bf966809 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 15:22:37 +0100 Subject: kstd/format: use tagged union to reduce template bloat --- kernel/kstd/print.cpp | 80 +++++++++++++++-- libs/kstd/include/kstd/bits/format/arg.hpp | 63 +++++++++++-- libs/kstd/include/kstd/bits/format/args.hpp | 100 +++++++++++++++++++-- .../include/kstd/bits/format/formatter/bool.hpp | 2 +- .../include/kstd/bits/format/formatter/cstring.hpp | 6 ++ .../kstd/bits/format/formatter/integral.hpp | 2 +- 6 files changed, 230 insertions(+), 23 deletions(-) diff --git a/kernel/kstd/print.cpp b/kernel/kstd/print.cpp index db03816..beee2e5 100644 --- a/kernel/kstd/print.cpp +++ b/kernel/kstd/print.cpp @@ -127,13 +127,81 @@ namespace kstd::os if (index < args.size()) { auto const & arg = args[index]; - if (arg.format_function) + switch (arg.type) { - arg.format_function(arg.value_pointer, parse_context, context); - } - else - { - context.push("{?}"); + case kstd::bits::format::arg_type::boolean: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.boolean, context); + break; + } + case kstd::bits::format::arg_type::character: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.character, context); + break; + } + case kstd::bits::format::arg_type::integer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.integer, context); + break; + } + case kstd::bits::format::arg_type::unsigned_integer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.unsigned_integer, context); + break; + } + case kstd::bits::format::arg_type::string_view: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.string_view, context); + break; + } + case kstd::bits::format::arg_type::c_string: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.c_string, context); + break; + } + case kstd::bits::format::arg_type::pointer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.pointer, context); + break; + } + case kstd::bits::format::arg_type::user_defined: + { + if (arg.value.user_defined.format) + { + arg.value.user_defined.format(arg.value.user_defined.pointer, parse_context, context); + } + else + { + context.push("{?}"); + } + break; + } + default: + { + context.push("{fmt-err: unknown-type}"); + break; + } } } else diff --git a/libs/kstd/include/kstd/bits/format/arg.hpp b/libs/kstd/include/kstd/bits/format/arg.hpp index 92e6431..a9a6ab5 100644 --- a/libs/kstd/include/kstd/bits/format/arg.hpp +++ b/libs/kstd/include/kstd/bits/format/arg.hpp @@ -3,24 +3,73 @@ // IWYU pragma: private, include +#include "error.hpp" #include "fwd.hpp" #include +#include +#include namespace kstd { - struct format_arg + namespace bits::format { - using format_function_type = auto(void const * value, format_parse_context & parse_context, - format_context & context) -> void; - using get_size_function_type = auto(void const * value) -> std::size_t; + enum struct arg_type : std::uint8_t + { + none, + boolean, + character, + integer, + unsigned_integer, + string_view, + c_string, + pointer, + user_defined, + }; + } // namespace bits::format - void const * value_pointer; - format_function_type * format_function; - get_size_function_type * get_size_function; + struct format_arg + { + bits::format::arg_type type{}; + union + { + bool boolean; + char character; + std::int64_t integer; + std::uint64_t unsigned_integer; + std::string_view string_view; + char const * c_string; + void const * pointer; + struct + { + void const * pointer; + auto (*format)(void const * value, format_parse_context & parse_context, format_context & context) -> void; + } user_defined; + } value{}; }; + namespace bits::format + { + constexpr auto extrat_dynamic_width(format_arg const & arg) -> std::size_t + { + if (arg.type == arg_type::unsigned_integer) + { + return static_cast(arg.value.unsigned_integer); + } + else if (arg.type == arg_type::integer) + { + if (arg.value.integer < 0) + { + error("Dynamic width cannont be negative."); + } + return static_cast(arg.value.integer); + } + error("Dynamic width argument is not an integral value."); + return 0; + } + } // namespace bits::format + } // namespace kstd #endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/args.hpp b/libs/kstd/include/kstd/bits/format/args.hpp index 5cca3ff..008cc03 100644 --- a/libs/kstd/include/kstd/bits/format/args.hpp +++ b/libs/kstd/include/kstd/bits/format/args.hpp @@ -3,14 +3,17 @@ // IWYU pragma: private, include +#include "arg.hpp" #include "context.hpp" -#include "error.hpp" #include "fwd.hpp" #include "parse_context.hpp" #include +#include #include +#include #include +#include #include namespace kstd @@ -31,17 +34,98 @@ namespace kstd } template - auto get_size_trampoline(void const * value_pointer) -> std::size_t + constexpr auto determine_arg_type() -> arg_type { - if constexpr (is_width_v) + using decay_type = std::remove_cvref_t>; + if constexpr (std::same_as) { - return static_cast(*static_cast(value_pointer)); + return arg_type::boolean; + } + else if constexpr (std::same_as) + { + return arg_type::character; + } + else if constexpr (std::integral && std::is_signed_v) + { + return arg_type::integer; + } + else if constexpr (std::integral && std::is_unsigned_v) + { + return arg_type::unsigned_integer; + } + else if constexpr (std::same_as) + { + return arg_type::string_view; + } + else if constexpr (std::same_as || std::same_as) + { + return arg_type::c_string; + } + else if constexpr (std::is_pointer_v || std::same_as) + { + if constexpr (std::same_as || std::same_as) + { + return arg_type::user_defined; + } + else + { + return arg_type::pointer; + } + } + else + { + return arg_type::user_defined; + } + } + + template + constexpr auto make_single_arg(ValueType const & value) -> format_arg + { + auto result = format_arg{}; + constexpr auto type = determine_arg_type(); + result.type = type; + + if constexpr (type == arg_type::boolean) + { + result.value.boolean = value; + } + else if constexpr (type == arg_type::character) + { + result.value.character = value; + } + else if constexpr (type == arg_type::integer) + { + result.value.integer = static_cast(value); + } + else if constexpr (type == arg_type::unsigned_integer) + { + result.value.unsigned_integer = static_cast(value); + } + else if constexpr (type == arg_type::string_view) + { + result.value.string_view = value; + } + else if constexpr (type == arg_type::c_string) + { + result.value.c_string = value; + } + else if constexpr (type == arg_type::pointer) + { + if constexpr (std::same_as, std::nullptr_t>) + { + result.value.pointer = nullptr; + } + else + { + result.value.pointer = static_cast(value); + } } else { - error("Dynamic width argument is not an integral value."); - return 0; + result.value.user_defined.pointer = &value; + result.value.user_defined.format = format_trampoline; } + return result; } } // namespace bits::format @@ -65,8 +149,8 @@ namespace kstd } else { - return format_arg_store{std::array{ - format_arg{static_cast(&args), format_trampoline, get_size_trampoline}...}}; + return format_arg_store{ + std::array{make_single_arg(args)...}}; } } diff --git a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp index b409e06..336e1b0 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp @@ -58,7 +58,7 @@ namespace kstd else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id) { auto const & arg = context.arg(specifiers.width_value); - final_width = arg.get_size_function(arg.value_pointer); + final_width = bits::format::extrat_dynamic_width(arg); } auto padding = bits::format::calculate_format_padding(final_width, text.size(), specifiers.align, diff --git a/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp index 9afb974..bf52f2e 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp @@ -5,6 +5,7 @@ #include "../formatter.hpp" #include "string_view.hpp" +#include #include namespace kstd @@ -24,6 +25,11 @@ namespace kstd { }; + template + struct formatter : formatter // NOLINT + { + }; + } // namespace kstd #endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp index d5cd6c5..4912a44 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp @@ -71,7 +71,7 @@ namespace kstd else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id) { auto const & arg = context.arg(specifiers.width_value); - final_width = arg.get_size_function(arg.value_pointer); + final_width = bits::format::extrat_dynamic_width(arg); } using unsigned_T = std::make_unsigned_t; -- cgit v1.2.3