summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/cxxformat/formatters.hpp92
1 files changed, 84 insertions, 8 deletions
diff --git a/include/cxxformat/formatters.hpp b/include/cxxformat/formatters.hpp
index 515d0ec..119f7ea 100644
--- a/include/cxxformat/formatters.hpp
+++ b/include/cxxformat/formatters.hpp
@@ -22,18 +22,31 @@ namespace format {
}
template<typename Outputter>
- constexpr void formatPadded(const format_output auto& out, Outputter&& outputter, std::size_t outputWidth, char padding, bool leftJustified, optional_int<std::size_t> minWidth)
+ constexpr void formatPadded(const format_output auto& out, Outputter&& outputter, std::size_t outputWidth, char padding, bool leftJustified, std::size_t minWidth)
{
- if(outputWidth < minWidth.value_or(0) && !leftJustified)
+ if (!leftJustified && outputWidth < minWidth)
{
- out(padding, *minWidth - outputWidth);
+ out(padding, minWidth - outputWidth);
}
outputter(out);
- if(outputWidth < minWidth.value_or(0) && leftJustified)
+ if (leftJustified && outputWidth < minWidth)
{
- out(padding, *minWidth - outputWidth);
+ out(padding, minWidth - outputWidth);
+ }
+ }
+
+ template<typename Outputter>
+ constexpr void formatPadded(const format_output auto& out, Outputter&& outputter, std::size_t outputWidth, char padding, bool leftJustified, optional_int<std::size_t> minWidth)
+ {
+ if (minWidth)
+ {
+ formatPadded(out, outputter, outputWidth, padding, leftJustified, *minWidth);
+ }
+ else
+ {
+ outputter(out);
}
}
@@ -159,6 +172,67 @@ namespace format {
}
}
+ template<typename Output>
+ class autopad_output : public FormatOutputBase<autopad_output<Output>> {
+ const Output& out;
+ std::size_t minWidth;
+ bool leftJustified;
+ char padding;
+#ifndef _NDEBUG
+ mutable bool used{false};
+#endif
+
+ public:
+ constexpr autopad_output(const Output& out, std::size_t minWidth, bool leftJustified, char padding) noexcept : out{out}, minWidth{minWidth}, leftJustified{leftJustified}, padding{padding} {}
+
+ void output(std::string_view what) const
+ {
+#ifndef _NDEBUG
+ assert(!used && "autopad_formatters must not call out() more than once for them to work correctly!");
+ used = true;
+#endif
+
+ formatPadded(out, what, leftJustified, padding, minWidth);
+ }
+ };
+
+ // padding of '\0' means take from spec
+ template<typename T, typename Derived, char padding = '\0'>
+ struct autopad_formatter {
+ template<typename U>
+ static constexpr void format(const format_output auto& out, U&& u, format_specifier spec, optional_int<std::size_t> minWidth, optional_int<std::size_t> precision)
+ {
+#ifdef _NDEBUG
+ if (minWidth)
+ {
+ Derived::formatAutopad(autopad_output{out, *minWidth, spec.flags.leftJustified, padding_(spec)}, std::forward<U>(u), spec, precision);
+ }
+ else
+ {
+ Derived::formatAutopad(out, std::forward<U>(u), spec, precision);
+ }
+#else
+ Derived::formatAutopad(autopad_output{out, minWidth.value_or(0), spec.flags.leftJustified, padding_(spec)}, std::forward<U>(u), spec, precision);
+#endif
+ }
+
+ private:
+ static constexpr char padding_([[maybe_unused]] format_specifier spec) noexcept
+ {
+ static_assert(std::derived_from<Derived, autopad_formatter<T, Derived, padding>>, "autopad_formatter must be used with CRTP");
+ static_assert(requires(T t, autopad_output<NullOutput> o, optional_int<std::size_t> p, format_specifier s) { Derived::formatAutopad(o, t, s, p); }, "The derived class must implement the static method formatAutopad(const format_output auto&, T-compatible, format_specifier, optional_int<std::size_t>)");
+
+ if constexpr (padding == '\0')
+ {
+ return spec.padding;
+ }
+ else
+ {
+ return padding;
+ }
+ }
+ };
+
using invalid_argument = std::invalid_argument;
template<std::integral T>
@@ -629,10 +703,10 @@ namespace format {
};
template<>
- struct formatter<std::nullptr_t> {
- static constexpr void format(const format_output auto& out, std::nullptr_t, format_specifier spec, optional_int<std::size_t> minWidth, optional_int<std::size_t>)
+ struct formatter<std::nullptr_t> : autopad_formatter<std::nullptr_t, formatter<std::nullptr_t>, ' '> {
+ static constexpr void formatAutopad(const format_output auto& out, std::nullptr_t, format_specifier, optional_int<std::size_t>)
{
- formatPadded(out, "(nil)", ' ', spec.flags.leftJustified, minWidth);
+ out("(nil)");
}
static constexpr void conversionSupported(format_specifier spec)
@@ -641,6 +715,8 @@ namespace format {
}
};
+ static_assert(has_formatter<std::nullptr_t>);
+
template<>
struct formatter<const char*> {
static constexpr void format(const format_output auto& out, const char* s, format_specifier spec, optional_int<std::size_t> minWidth, optional_int<std::size_t> precision)