diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-03-20 14:03:45 +0100 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-03-20 14:03:45 +0100 |
| commit | 07cb15c42c16497b0b09b75886ce3baddeaaafb3 (patch) | |
| tree | d2c4d91e6f55b6c1694f0ddeb89a457e8061c3e1 /libs | |
| parent | 8365a09e18ac043d2e0aa6a9d3e394a02fe7c6cb (diff) | |
| download | teachos-07cb15c42c16497b0b09b75886ce3baddeaaafb3.tar.xz teachos-07cb15c42c16497b0b09b75886ce3baddeaaafb3.zip | |
kstd/format: split implementation
Diffstat (limited to 'libs')
21 files changed, 772 insertions, 621 deletions
diff --git a/libs/kstd/include/kstd/bits/format/arg.hpp b/libs/kstd/include/kstd/bits/format/arg.hpp new file mode 100644 index 0000000..92e6431 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/arg.hpp @@ -0,0 +1,26 @@ +#ifndef KSTD_BITS_FORMAT_ARG_HPP +#define KSTD_BITS_FORMAT_ARG_HPP + +// IWYU pragma: private, include <kstd/format> + +#include "fwd.hpp" + +#include <cstddef> + +namespace kstd +{ + + struct format_arg + { + 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; + + void const * value_pointer; + format_function_type * format_function; + get_size_function_type * get_size_function; + }; + +} // 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 new file mode 100644 index 0000000..5cca3ff --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/args.hpp @@ -0,0 +1,75 @@ +#ifndef KSTD_BITS_FORMAT_ARGS_HPP +#define KSTD_BITS_FORMAT_ARGS_HPP + +// IWYU pragma: private, include <kstd/format> + +#include "context.hpp" +#include "error.hpp" +#include "fwd.hpp" +#include "parse_context.hpp" + +#include <array> +#include <cstddef> +#include <span> +#include <type_traits> + +namespace kstd +{ + + namespace bits::format + { + + template<typename ValueType> + auto format_trampoline(void const * value_pointer, format_parse_context & parse_context, format_context & context) + -> void + { + auto typed_value_pointer = static_cast<ValueType const *>(value_pointer); + auto fmt = formatter<std::remove_cvref_t<ValueType>>{}; + auto const it = fmt.parse(parse_context); + parse_context.advance_to(it); + fmt.format(*typed_value_pointer, context); + } + + template<typename ValueType> + auto get_size_trampoline(void const * value_pointer) -> std::size_t + { + if constexpr (is_width_v<ValueType>) + { + return static_cast<std::size_t>(*static_cast<ValueType const *>(value_pointer)); + } + else + { + error("Dynamic width argument is not an integral value."); + return 0; + } + } + + } // namespace bits::format + + using format_args = std::span<format_arg const>; + + template<std::size_t Count> + struct format_arg_store + { + std::array<format_arg, Count> args{}; + }; + + template<typename... Arguments> + [[nodiscard]] auto make_format_args(Arguments const &... args) noexcept -> format_arg_store<sizeof...(Arguments)> + { + using namespace bits::format; + + if constexpr (sizeof...(Arguments) == 0) + { + return format_arg_store<0>{}; + } + else + { + return format_arg_store<sizeof...(Arguments)>{std::array<format_arg, sizeof...(Arguments)>{ + format_arg{static_cast<void const *>(&args), format_trampoline<Arguments>, get_size_trampoline<Arguments>}...}}; + } + } + +} // namespace kstd + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/context.hpp b/libs/kstd/include/kstd/bits/format/context.hpp new file mode 100644 index 0000000..478a48f --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/context.hpp @@ -0,0 +1,61 @@ +#ifndef KSTD_BITS_FORMAT_CONTEXT_HPP +#define KSTD_BITS_FORMAT_CONTEXT_HPP + +// IWYU pragma: private, include <kstd/format> + +#include "arg.hpp" + +#include <kstd/os/error.hpp> + +#include <concepts> +#include <cstddef> +#include <span> +#include <string_view> + +namespace kstd +{ + + namespace bits::format + { + template<typename T> + constexpr auto inline is_width_v = std::integral<T> && // + !std::same_as<bool, T> && // + !std::same_as<char, T> && // + !std::same_as<wchar_t, T> && // + !std::same_as<char8_t, T> && // + !std::same_as<char16_t, T> && // + !std::same_as<char32_t, T>; + } + + struct format_context + { + using writer_function = void(void *, std::string_view); + using format_args = std::span<format_arg const>; + + writer_function * writer{}; + void * user_data{}; + format_args args{}; + + [[nodiscard]] auto arg(std::size_t const id) const -> format_arg const & + { + if (id >= args.size()) + { + kstd::os::panic("[kstd:format] argument index out of range!"); + } + return args[id]; + } + + constexpr auto push(std::string_view string) -> void + { + writer(user_data, string); + } + + constexpr auto push(char character) -> void + { + writer(user_data, std::string_view(&character, 1)); + } + }; + +} // namespace kstd + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/error.hpp b/libs/kstd/include/kstd/bits/format/error.hpp new file mode 100644 index 0000000..f0863eb --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/error.hpp @@ -0,0 +1,24 @@ +#ifndef KSTD_BITS_FORMAT_ERROR_HPP +#define KSTD_BITS_FORMAT_ERROR_HPP + +#include "kstd/os/error.hpp" + +namespace kstd::bits::format +{ + + constexpr auto error(char const * message) -> void + { + if consteval + { + extern void compile_time_format_error_triggered(char const *); + compile_time_format_error_triggered(message); + } + else + { + kstd::os::panic("Error while formatting a string."); + } + } + +} // namespace kstd::bits::format + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter.hpp b/libs/kstd/include/kstd/bits/format/formatter.hpp new file mode 100644 index 0000000..bff5f55 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter.hpp @@ -0,0 +1,14 @@ +#ifndef KSTD_BITS_FORMATTER_HPP +#define KSTD_BITS_FORMATTER_HPP + +// IWYU pragma: private, include <kstd/format> + +namespace kstd +{ + + template<typename> + struct formatter; + +} // namespace kstd + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp new file mode 100644 index 0000000..bb6cacf --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp @@ -0,0 +1,83 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_BOOL_HPP +#define KSTD_BITS_FORMAT_FORMATTER_BOOL_HPP + +#include "../context.hpp" +#include "../error.hpp" +#include "../formatter.hpp" +#include "../parse_context.hpp" +#include "../specifiers.hpp" + +#include <iterator> +#include <string_view> + +namespace kstd +{ + + template<> + struct formatter<bool> + { + bits::format::format_specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it != '}') + { + if (*it == 's') + { + specifiers.type = *it; + std::advance(it, 1); + } + else + { + bits::format::error("Invalid type specifier for bool."); + } + } + + if (it != end && *it != '}') + { + bits::format::error("Missing terminating '}' in format string."); + } + + return it; + } + + auto format(bool value, format_context & context) const -> void + { + auto const text = value ? std::string_view{"true"} : std::string_view{"false"}; + auto final_width = 0uz; + + if (specifiers.width_mode == bits::format::width_mode::static_value) + { + final_width = specifiers.width_value; + } + 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); + } + + auto padding = bits::format::calculate_format_padding(final_width, text.size(), specifiers.align, + bits::format::alignment::left); + + for (auto i = 0uz; i < padding.left; ++i) + { + context.push(specifiers.fill); + } + + context.push(text); + + for (auto i = 0uz; i < padding.right; ++i) + { + context.push(specifiers.fill); + } + } + }; + +} // namespace kstd + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp new file mode 100644 index 0000000..9afb974 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp @@ -0,0 +1,29 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_CSTRING_HPP +#define KSTD_BITS_FORMAT_FORMATTER_CSTRING_HPP + +#include "../context.hpp" +#include "../formatter.hpp" +#include "string_view.hpp" + +#include <string_view> + +namespace kstd +{ + + template<> + struct formatter<char const *> : formatter<std::string_view> + { + auto format(char const * string, format_context & context) const -> void + { + formatter<std::string_view>::format(string ? std::string_view{string} : "(null)", context); + } + }; + + template<> + struct formatter<char *> : formatter<char const *> + { + }; + +} // 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 new file mode 100644 index 0000000..b0caed1 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp @@ -0,0 +1,204 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_INTEGRAL_HPP +#define KSTD_BITS_FORMAT_FORMATTER_INTEGRAL_HPP + +#include "../context.hpp" +#include "../error.hpp" +#include "../formatter.hpp" +#include "../parse_context.hpp" +#include "../specifiers.hpp" + +#include <array> +#include <concepts> +#include <cstddef> +#include <iterator> +#include <string_view> +#include <type_traits> +#include <utility> + +namespace kstd +{ + + template<std::integral T> + struct formatter<T> + { + bits::format::format_specifiers specifiers{}; + + constexpr auto static maximum_digits = 80; + + enum struct base + { + bin = 2, + oct = 8, + dec = 10, + hex = 16, + }; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it != '}') + { + if (*it == 'b' || *it == 'B' || *it == 'd' || *it == 'o' || *it == 'x' || *it == 'X' || *it == 'p') + { + specifiers.type = *it; + std::advance(it, 1); + } + else + { + bits::format::error("Invalid type specifier for integral type."); + } + } + + if (it != end && *it != '}') + { + bits::format::error("Missing terminating '}' in format string."); + } + + return it; + } + + auto format(T value, format_context & context) const -> void + { + auto final_width = 0uz; + if (specifiers.width_mode == bits::format::width_mode::static_value) + { + final_width = specifiers.width_value; + } + 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); + } + + using unsigned_T = std::make_unsigned_t<T>; + auto absolute_value = static_cast<unsigned_T>(value); + auto is_negative = false; + + if constexpr (std::is_signed_v<T>) + { + if (value < 0) + { + is_negative = true; + absolute_value = 0 - static_cast<unsigned_T>(value); + } + } + + auto const base = [type = specifiers.type] -> auto { + switch (type) + { + case 'x': + case 'X': + case 'p': + return base::hex; + case 'b': + case 'B': + return base::bin; + case 'o': + return base::oct; + default: + return base::dec; + } + }(); + + auto buffer = std::array<char, maximum_digits>{}; + auto digits = (specifiers.type == 'X') ? "0123456789ABCDEF" : "0123456789abcdef"; + auto current = buffer.rbegin(); + + if (absolute_value == 0) + { + *current = '0'; + std::advance(current, 1); + } + else + { + while (absolute_value != 0) + { + *current = digits[absolute_value % std::to_underlying(base)]; + std::advance(current, 1); + absolute_value /= std::to_underlying(base); + } + } + + auto content_length = static_cast<std::size_t>(std::distance(buffer.rbegin(), current)); + auto prefix = std::array<char, 2>{'0', '\0'}; + auto prefix_length = 0uz; + if (specifiers.alternative_form) + { + switch (base) + { + case base::bin: + prefix[1] = specifiers.type == 'B' ? 'B' : 'b'; + prefix_length = 2; + break; + case base::oct: + prefix_length = 1; + break; + case base::hex: + prefix[1] = specifiers.type == 'X' ? 'X' : 'x'; + prefix_length = 2; + break; + default: + break; + } + } + + auto sign_character = '\0'; + if (is_negative) + { + sign_character = '-'; + } + else if (specifiers.sign == bits::format::sign_mode::plus) + { + sign_character = '+'; + } + else if (specifiers.sign == bits::format::sign_mode::space) + { + sign_character = ' '; + } + + auto const total_length = content_length + prefix_length + (sign_character != '\0'); + auto const padding = bits::format::calculate_format_padding(final_width, total_length, specifiers.align, + bits::format::alignment::right); + auto const effective_zero_pad = specifiers.zero_pad && (specifiers.align == bits::format::alignment::none); + + if (!effective_zero_pad) + { + for (auto i = 0uz; i < padding.left; ++i) + { + context.push(specifiers.fill); + } + } + + if (sign_character != '\0') + { + context.push(sign_character); + } + if (prefix_length > 0) + { + context.push(std::string_view{prefix.data(), prefix_length}); + } + + if (effective_zero_pad) + { + for (auto i = 0uz; i < padding.left; ++i) + { + context.push('0'); + } + } + + context.push(std::string_view{current.base(), content_length}); + + for (auto i = 0uz; i < padding.right; ++i) + { + context.push(specifiers.fill); + } + } + }; + +} // namespace kstd + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp b/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp new file mode 100644 index 0000000..78e7f7b --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp @@ -0,0 +1,111 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_ORDERING_HPP +#define KSTD_BITS_FORMAT_FORMATTER_ORDERING_HPP + +#include "../context.hpp" +#include "../formatter.hpp" +#include "../parse_context.hpp" +#include "../specifiers.hpp" + +#include <compare> + +namespace kstd +{ + + template<> + struct formatter<std::strong_ordering> + { + bits::format::format_specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + return context.begin(); + } + + auto format(std::strong_ordering value, format_context & context) const -> void + { + if (value == std::strong_ordering::equal) + { + return context.push(specifiers.alternative_form ? "==" : "equal"); + } + else if (value == std::strong_ordering::equivalent) + { + return context.push(specifiers.alternative_form ? "==" : "equivalent"); + } + else if (value == std::strong_ordering::greater) + { + return context.push(specifiers.alternative_form ? ">" : "greater"); + } + else if (value == std::strong_ordering::less) + { + return context.push(specifiers.alternative_form ? "<" : "less"); + } + kstd::os::panic("[kstd:format] Invalid strong ordering value!"); + } + }; + + template<> + struct formatter<std::weak_ordering> + { + bits::format::format_specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + return context.begin(); + } + + auto format(std::weak_ordering value, format_context & context) const -> void + { + if (value == std::weak_ordering::equivalent) + { + return context.push(specifiers.alternative_form ? "==" : "equivalent"); + } + else if (value == std::weak_ordering::greater) + { + return context.push(specifiers.alternative_form ? ">" : "greater"); + } + else if (value == std::weak_ordering::less) + { + return context.push(specifiers.alternative_form ? "<" : "less"); + } + kstd::os::panic("[kstd:format] Invalid weak ordering value!"); + } + }; + + template<> + struct formatter<std::partial_ordering> + { + bits::format::format_specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + return context.begin(); + } + + auto format(std::partial_ordering value, format_context & context) const -> void + { + if (value == std::partial_ordering::equivalent) + { + return context.push(specifiers.alternative_form ? "==" : "equivalent"); + } + else if (value == std::partial_ordering::greater) + { + return context.push(specifiers.alternative_form ? ">" : "greater"); + } + else if (value == std::partial_ordering::less) + { + return context.push(specifiers.alternative_form ? "<" : "less"); + } + else if (value == std::partial_ordering::unordered) + { + return context.push(specifiers.alternative_form ? "<=>" : "unordered"); + } + kstd::os::panic("[kstd:format] Invalid partial ordering value!"); + } + }; + +} // namespace kstd + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp b/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp new file mode 100644 index 0000000..fe75a2f --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp @@ -0,0 +1,42 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_POINTER_HPP +#define KSTD_BITS_FORMAT_FORMATTER_POINTER_HPP + +#include "../context.hpp" +#include "../formatter.hpp" +#include "../parse_context.hpp" +#include "../specifiers.hpp" +#include "integral.hpp" + +#include <bit> +#include <cstdint> + +namespace kstd +{ + + template<typename T> + struct formatter<T const *> : formatter<std::uintptr_t> + { + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + auto result = formatter<std::uintptr_t>::parse(context); + if (!this->specifiers.type) + { + this->specifiers.type = 'p'; + this->specifiers.alternative_form = true; + } + return result; + } + + auto format(T const * pointer, format_context & context) const -> void + { + formatter<std::uintptr_t>::format(std::bit_cast<std::uintptr_t>(pointer), context); + } + }; + + template<typename T> + struct formatter<T *> : formatter<T const *> + { + }; + +} // namespace kstd +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp b/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp new file mode 100644 index 0000000..f5b698e --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp @@ -0,0 +1,41 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_STRING_VIEW_HPP +#define KSTD_BITS_FORMAT_FORMATTER_STRING_VIEW_HPP + +#include "../context.hpp" +#include "../error.hpp" +#include "../formatter.hpp" +#include "../parse_context.hpp" + +#include <string_view> + +namespace kstd +{ + + template<> + struct formatter<std::string_view> + { + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + auto it = context.begin(); + + if (it != context.end() && *it == 's') + { + ++it; + } + + if (it != context.end() && *it != '}') + { + bits::format::error("Invalid specifier for string_view."); + } + return it; + } + + auto format(std::string_view const & string, format_context & context) const -> void + { + context.push(string); + } + }; + +} // namespace kstd + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/fwd.hpp b/libs/kstd/include/kstd/bits/format/fwd.hpp new file mode 100644 index 0000000..6caedae --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/fwd.hpp @@ -0,0 +1,23 @@ +#ifndef KSTD_BITS_FORMAT_FWD_HPP +#define KSTD_BITS_FORMAT_FWD_HPP + +// IWYU pragma: private + +#include <cstddef> + +namespace kstd +{ + + struct format_parse_context; + struct format_context; + struct format_arg; + + template<typename> + struct formatter; + + template<std::size_t> + struct format_arg_store; + +} // namespace kstd + +#endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_parse_context.hpp b/libs/kstd/include/kstd/bits/format/parse_context.hpp index 76c3f03..063263b 100644 --- a/libs/kstd/include/kstd/bits/format_parse_context.hpp +++ b/libs/kstd/include/kstd/bits/format/parse_context.hpp @@ -3,7 +3,7 @@ // IWYU pragma: private, include <kstd/format> -#include <kstd/os/error.hpp> +#include "error.hpp" #include <cstddef> #include <string_view> @@ -11,19 +11,6 @@ namespace kstd { - constexpr auto report_format_error(char const * message) -> void - { - if consteval - { - extern void compile_time_format_error_triggered(char const *); - compile_time_format_error_triggered(message); - } - else - { - kstd::os::panic("Error while formatting a string."); - } - } - struct format_parse_context { using iterator = std::string_view::const_iterator; @@ -55,14 +42,14 @@ namespace kstd { if (m_mode == index_mode::manual) { - report_format_error("Cannot mix automatic and manual indexing."); + bits::format::error("Cannot mix automatic and manual indexing."); } m_mode = index_mode::automatic; if (m_next_argument_id >= m_argument_count) { - report_format_error("Argument index out of bounds."); + bits::format::error("Argument index out of bounds."); } return m_next_argument_id++; } @@ -71,14 +58,14 @@ namespace kstd { if (m_mode == index_mode::automatic) { - report_format_error("Cannot mix automatic and manual indexing."); + bits::format::error("Cannot mix automatic and manual indexing."); } m_mode = index_mode::manual; if (index >= m_argument_count) { - report_format_error("Argument index out of bounds."); + bits::format::error("Argument index out of bounds."); } } @@ -87,7 +74,7 @@ namespace kstd check_arg_id(id); if (m_is_integral && !m_is_integral[id]) { - report_format_error("Dynamic width argument must be an integral object."); + bits::format::error("Dynamic width argument must be an integral object."); } } @@ -96,7 +83,7 @@ namespace kstd auto const id = next_arg_id(); if (m_is_integral && !m_is_integral[id]) { - report_format_error("Dynamic width argument must be an integral object."); + bits::format::error("Dynamic width argument must be an integral object."); } return id; } diff --git a/libs/kstd/include/kstd/bits/format_specifiers.hpp b/libs/kstd/include/kstd/bits/format/specifiers.hpp index 00cca40..85581e6 100644 --- a/libs/kstd/include/kstd/bits/format_specifiers.hpp +++ b/libs/kstd/include/kstd/bits/format/specifiers.hpp @@ -3,14 +3,14 @@ // IWYU pragma: private -#include "kstd/bits/format_context.hpp" -#include "kstd/bits/format_parse_context.hpp" +#include "error.hpp" +#include "parse_context.hpp" #include <cstddef> #include <cstdint> #include <iterator> -namespace kstd::bits +namespace kstd::bits::format { enum struct alignment : std::uint8_t { @@ -153,7 +153,7 @@ namespace kstd::bits if (it == end || *it != '}') { - report_format_error("Expected '}' for dynamic width."); + error("Expected '}' for dynamic width."); } std::advance(it, 1); specifiers.width_value = argument_id; @@ -200,6 +200,6 @@ namespace kstd::bits } } -} // namespace kstd::bits +} // namespace kstd::bits::format #endif
\ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_string.hpp b/libs/kstd/include/kstd/bits/format/string.hpp index f16f1ee..2e7d60a 100644 --- a/libs/kstd/include/kstd/bits/format_string.hpp +++ b/libs/kstd/include/kstd/bits/format/string.hpp @@ |
