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 --- 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 +- 5 files changed, 156 insertions(+), 17 deletions(-) (limited to 'libs') 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