summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/cxxformat/runtime.hpp120
1 files changed, 66 insertions, 54 deletions
diff --git a/include/cxxformat/runtime.hpp b/include/cxxformat/runtime.hpp
index 23f2b96..3bfe7fe 100644
--- a/include/cxxformat/runtime.hpp
+++ b/include/cxxformat/runtime.hpp
@@ -15,15 +15,43 @@ namespace format {
std::vector<std::variant<std::string, format_specifier>> parts;
std::size_t argCount;
+ template<typename Out>
+ struct argument_formatter {
+ virtual void format(const Out& out, format_specifier spec, optional_int<std::size_t> minWidth, optional_int<std::size_t> precision) = 0;
+ virtual void conversionSupported(format_specifier spec) = 0;
+ virtual ~argument_formatter() = default;
+ };
+
+ template<typename Out, typename Arg>
+ class argument_formatter_for : public argument_formatter<Out> {
+ Arg&& arg;
+
+ using formatter = formatter_with_fallback<std::decay_t<Arg>>;
+
+ public:
+ argument_formatter_for(Arg&& arg) : arg{std::forward<Arg>(arg)} {}
+
+ void format(const Out& out, format_specifier spec, optional_int<std::size_t> minWidth, optional_int<std::size_t> precision) override
+ {
+ formatter::format(out, arg, spec, minWidth, precision);
+ }
+
+ void conversionSupported(format_specifier spec) override
+ {
+ formatter::conversionSupported(spec);
+ }
+ };
+
public:
format_template(std::vector<std::variant<std::string, format_specifier>>&& parts, std::size_t argCount) noexcept : parts{std::move(parts)}, argCount{argCount} {}
template<format_output_compatible Output, typename... Args> requires (has_some_formatter<std::decay_t<Args>> && ...)
void operator()(Output&& output, Args&&... args) const
{
- checkArgCount(sizeof...(Args));
+ constexpr auto passedArgCount = sizeof...(Args);
+ checkArgCount(passedArgCount);
- std::array<optional_int<std::size_t>, sizeof...(Args)> indexArgs{[]<typename Arg>(Arg&& arg) constexpr -> optional_int<std::size_t> {
+ std::array<optional_int<std::size_t>, passedArgCount> indexArgs{[]<typename Arg>(Arg&& arg) constexpr -> optional_int<std::size_t> {
if constexpr (std::unsigned_integral<Arg> && sizeof(Arg) <= sizeof(std::size_t))
{
return static_cast<std::size_t>(arg);
@@ -34,69 +62,53 @@ namespace format {
}
}(std::forward<Args>(args))...};
- const auto checkPart = [&args..., &indexArgs]<std::size_t... indices>(std::index_sequence<indices...>)
- {
- return [&args..., &indexArgs](format_specifier spec)
- {
- ([spec, &indexArgs]<typename Arg>(Arg&&, std::size_t index)
- {
- if (index == spec.argIndex)
- {
- if (spec.flags.widthAsArg)
- {
- if (!indexArgs[*spec.minWidth])
- {
- throw std::invalid_argument{"Width argument must be an unsigned integral at most as big as std::size_t"};
- }
- }
- if (spec.flags.precisionAsArg)
- {
- if (!indexArgs[*spec.precision])
- {
- throw std::invalid_argument{"Precision argument must be an unsigned integral at most as big as std::size_t"};
- }
- }
- formatter_with_fallback<std::decay_t<Arg>>::conversionSupported(spec);
- }
- }(std::forward<Args>(args), indices), ...);
- };
- }(std::make_index_sequence<sizeof...(Args)>());
+ const auto out = make_output(std::forward<Output>(output));
+ using Out = decltype(out);
+
+ std::array<std::unique_ptr<argument_formatter<Out>>, passedArgCount> argFormatters{[]<typename Arg>(Arg&& arg) {
+ return std::unique_ptr<argument_formatter<Out>>{new argument_formatter_for<Out, Arg>{std::forward<Arg>(arg)}};
+ }(std::forward<Args>(args))...};
for (const auto& part : parts)
{
if (std::holds_alternative<format_specifier>(part))
{
- checkPart(std::get<format_specifier>(part));
+ const auto spec = std::get<format_specifier>(part);
+ if (spec.flags.widthAsArg)
+ {
+ if (!indexArgs[*spec.minWidth])
+ {
+ throw std::invalid_argument{"Width argument must be an unsigned integral at most as big as std::size_t"};
+ }
+ }
+ if (spec.flags.precisionAsArg)
+ {
+ if (!indexArgs[*spec.precision])
+ {
+ throw std::invalid_argument{"Precision argument must be an unsigned integral at most as big as std::size_t"};
+ }
+ }
+ argFormatters[spec.argIndex]->conversionSupported(spec);
}
}
- const auto out = make_output(std::forward<Output>(output));
const overloaded_callable visitor{
- [&args..., &out, &indexArgs]<std::size_t... indices>(std::index_sequence<indices...>)
+ [&out, &indexArgs, &argFormatters](format_specifier spec)
{
- return [&args..., &out, &indexArgs](format_specifier spec)
+ optional_int<std::size_t> minWidth = spec.minWidth;
+ if (spec.flags.widthAsArg)
{
- ([&out, spec, &indexArgs]<typename Arg>(Arg&& arg, std::size_t index)
- {
- if (index == spec.argIndex)
- {
- optional_int<std::size_t> minWidth = spec.minWidth;
- if (spec.flags.widthAsArg)
- {
- minWidth = indexArgs[*minWidth];
- assert(minWidth);
- }
- optional_int<std::size_t> precision = spec.precision;
- if (spec.flags.precisionAsArg)
- {
- precision = indexArgs[*precision];
- assert(precision);
- }
- return formatter_with_fallback<std::decay_t<Arg>>::format(out, std::forward<Arg>(arg), spec, minWidth, precision);
- }
- }(std::forward<Args>(args), indices), ...);
- };
- }(std::make_index_sequence<sizeof...(Args)>()),
+ minWidth = indexArgs[*minWidth];
+ assert(minWidth);
+ }
+ optional_int<std::size_t> precision = spec.precision;
+ if (spec.flags.precisionAsArg)
+ {
+ precision = indexArgs[*precision];
+ assert(precision);
+ }
+ return argFormatters[spec.argIndex]->format(out, spec, minWidth, precision);
+ },
[&out](std::string text) { out(text); }
};
for (const auto& part : parts)