Skip to content

Latest commit

 

History

History
241 lines (195 loc) · 5.86 KB

184.md

File metadata and controls

241 lines (195 loc) · 5.86 KB
Info

Example

struct foo {
  int value{};
};

template<>
struct fmt::formatter<foo> : fmt::formatter<int> {
  template<class FormatContext>
  auto format(foo f, FormatContext& ctx) {
    return fmt::formatter<int>::format(f.value, ctx);
  }
};

int main() {
  std::cout << fmt::format("{}", foo{.value = 42}); // prints 42
}

https://godbolt.org/z/K3hv69

Puzzle

  • Can you implement a custom formatter for foo?
template<class...>
struct foo;

int main() {
  using namespace boost::ut;
  using namespace std::literals;

  "custom formatter"_test = [] {
    should("format empty with no spaces") = [] {
      expect("foo()"sv == fmt::format("{}", foo{}));
    };

    should("format fields seperated by space") = [] {
      expect("foo( 1 )"sv == fmt::format("{}", foo{1}));
      expect("foo( 1 2 )"sv == fmt::format("{}", foo{1, 2}));
      expect("foo( 1 2 3 )"sv == fmt::format("{}", foo{1, 2, 3}));
    };

    should("format fields seperated by space with different types") = [] {
      expect("foo( Quant Lab )"sv == fmt::format("{}", foo{"Quant", "Lab"}));
      expect("foo( Q 42 B )"sv == fmt::format("{}", foo{'Q', 42, "B"}));
    };
  };
}

https://godbolt.org/z/nob8c9

Solutions

template <class... Ts>
struct foo {
  std::tuple<Ts...> values;
  foo(const Ts... args) : values(std::make_tuple(args...)){};
};

template <class... Ts>
struct fmt::formatter<foo<Ts...>> {
  constexpr auto parse(format_parse_context& ctx) {
    return ctx.end();
  }
  template <class FormatContext>
  auto format(foo<Ts...> f, FormatContext& ctx) {
    return fmt::format_to(
        ctx.out(),
        std::tuple_size<decltype(f.values)>::value ? "foo( {} )" : "foo({})",
        fmt::join(f.values, " "));
  }
};

https://godbolt.org/z/G596ov

template<typename... Ts>
struct foo {
    foo(Ts...t) : data{t...} {}

    std::tuple<Ts...> data{};
};

template<typename ...Ts>
struct fmt::formatter<foo<Ts...>> {
    constexpr auto parse(auto& ctx) {
        return ctx.end();
    }

    auto format(foo<Ts...> f, auto& ctx) {
        if constexpr (0 == sizeof...(Ts)) {
            return fmt::format_to(ctx.out(), "foo()");
        } else {
            return fmt::format_to(ctx.out(), "foo( {} )", fmt::join(f.data, " "));
        }
    }
};

https://godbolt.org/z/6WGo4P

template <class... TArgs>
struct foo {
  constexpr foo(TArgs... args) : val{args...} {}
  std::tuple<TArgs...> val;
};

template <class... TArgs>
struct fmt::formatter<foo<TArgs...>> {
  constexpr auto parse(format_parse_context& ctx) const { return ctx.end(); }

  constexpr auto format(foo<TArgs...> f, auto& ctx) const {
    if constexpr (sizeof...(TArgs)) {
      return fmt::format_to(ctx.out(), "foo( {} )", fmt::join(f.val, " "));
    }
    return fmt::format_to(ctx.out(), "foo()");
  }
};

https://godbolt.org/z/YfKsf3

template <class... TArgs>
struct foo : std::tuple<TArgs...> {
  constexpr foo(TArgs... args) : std::tuple<TArgs...>{args...} {}
};

template <typename... Ts>
struct fmt::formatter<foo<Ts...>> : fmt::formatter<std::string_view> {
  template <class FormatContext>
  auto format(foo<Ts...> f, FormatContext& ctx) {
    if (sizeof...(Ts) == 0) {
      return fmt::format_to(ctx.out(), "foo()");
    }
    return fmt::format_to(ctx.out(), "foo( {} )", fmt::join(f, " "));
  }
};

https://godbolt.org/z/sn131T

template <class... TArgs>
struct foo : std::tuple<TArgs...> {
  using std::tuple<TArgs...>::tuple;
};

template <class... TArgs>
foo(TArgs...) -> foo<TArgs...>;

template <class... TArgs>
struct fmt::formatter<foo<TArgs...>> {
  template <class TParseContext>
  constexpr auto parse(TParseContext &ctx) {
    return std::begin(ctx);
  }

  template <class FormatContext>
  auto format(foo<TArgs...> f, FormatContext& ctx) {
    if constexpr (sizeof...(TArgs) == 0) {
      return fmt::format_to(ctx.out(), "foo()");
    }

    fmt::format_to(ctx.out(), "foo( ");
    [&]<auto... Is>(std::index_sequence<Is...>) {
      (fmt::format_to(ctx.out(), "{} ", std::get<Is>(f)), ...);
    }(std::index_sequence_for<TArgs...>{});
    return fmt::format_to(ctx.out(), ")");
  }
};

https://godbolt.org/z/6rKar4

template<class... Args>
struct foo : std::tuple<Args...> {
    using std::tuple<Args...>::tuple;
};

template<class...Args> foo(Args...) -> foo<Args...>;

template<class...T>
struct fmt::formatter<foo<T...>> : fmt::formatter<int> {
    template<class FormatContext>
    auto format(foo<T...> f, FormatContext& ctx) {
        std::string fmtString = sizeof...(T) > 0 ? "foo( " : "foo(";
        for (int i=0; i<sizeof...(T); i++)
            fmtString += "{} ";
        fmtString += ")";
        auto formatOut = [&](auto...args) { return fmt::format_to(ctx.out(), fmtString, args...); };
        return std::apply(formatOut, static_cast<std::tuple<T...>>(f));
    }
};

https://godbolt.org/z/a6Eb9h

template<class... Ts>
struct foo : std::tuple<Ts...> {
  constexpr explicit(true) foo(Ts... ts) : std::tuple<Ts...>(ts...) {}
};

template<class... Ts>
struct fmt::formatter<foo<Ts...>> {
  constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }

  template<class TCtx>
  auto format(foo<Ts...> f, TCtx& ctx) {
    if constexpr(sizeof...(Ts) == 0) {
      return format_to(ctx.out(), "foo()");
    } else {
      return std::apply([&](auto... args) {
        return format_to(
            ctx.out(),
            std::string{"foo( "} + ((args, std::string{"{} "}) + ...) + ")",
            args...);
      }, static_cast<const std::tuple<Ts...>&>(f));
    }
  }
};

https://godbolt.org/z/5E9KKW