Repository: fmtlib/fmt
File: include/fmt/ranges.h (C/C++)

Code before:
      : tuple(t), sep{s} {}
};

template <typename Char, typename... T>
using tuple_arg_join = tuple_join_view<Char, T...>;

template <typename Char, typename... T>
struct formatter<tuple_join_view<Char, T...>, Char> {
  template <typename ParseContext>
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
    return parse_subroutine(ctx, index_helper<sizeof...(T)>{});
  }

  template <typename FormatContext>
  auto format(const tuple_join_view<Char, T...>& value,
              FormatContext& ctx) const -> typename FormatContext::iterator {
    return format_subroutine(value, ctx, index_helper<sizeof...(T)>{});
  }

 private:
  std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters;

  template <size_t> struct index_helper {};

  template <typename ParseContext>
  FMT_CONSTEXPR auto parse_subroutine(ParseContext& ctx, index_helper<0>)
      -> decltype(ctx.begin()) {
    return ctx.begin();
  }

  template <typename ParseContext, size_t N>
  FMT_CONSTEXPR auto parse_subroutine(ParseContext& ctx, index_helper<N>)
      -> decltype(ctx.begin()) {
    auto end = std::get<sizeof...(T) - N>(formatters).parse(ctx);
    if (N > 1) {
      auto end1 = parse_subroutine(ctx, index_helper<N - 1>{});
      if (end != end1)
        throw fmt::format_error{
            "specs have different end for different types in tuple"};
    }
    return end;
  }

  template <typename FormatContext>
  auto format_subroutine(const tuple_join_view<Char, T...>&, FormatContext& ctx,
                         index_helper<0>) const ->
      typename FormatContext::iterator {
    return ctx.out();
  }

  template <typename FormatContext, size_t N>

Review comment (suggestion):
This function is reserved for type `std::tuple<>`. It will not be entered if `sizeof...(T)>0`.

Code after:
      : tuple(t), sep{s} {}
};

template <typename Char, typename... T>
using tuple_arg_join = tuple_join_view<Char, T...>;

template <typename Char, typename... T>
struct formatter<tuple_join_view<Char, T...>, Char> {
  template <typename ParseContext>
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
    return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>{});
  }

  template <typename FormatContext>
  auto format(const tuple_join_view<Char, T...>& value,
              FormatContext& ctx) const -> typename FormatContext::iterator {
    return do_format(value, ctx,
                     std::integral_constant<size_t, sizeof...(T)>{});
  }

 private:
  std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;

  template <typename ParseContext>
  FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
                              std::integral_constant<size_t, 0>)
      -> decltype(ctx.begin()) {
    return ctx.begin();
  }

  template <typename ParseContext, size_t N>
  FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
                              std::integral_constant<size_t, N>)
      -> decltype(ctx.begin()) {
    auto end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
    if (N > 1) {
      auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>{});
      if (end != end1)
        FMT_THROW(format_error("incompatible format specs for tuple elements"));
    }
    return end;
  }

  template <typename FormatContext>
  auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
                 std::integral_constant<size_t, 0>) const ->
      typename FormatContext::iterator {
    return ctx.out();
  }

  template <typename FormatContext, size_t N>