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

Code before:
  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>
  auto format_subroutine(const tuple_join_view<Char, T...>& value,
                         FormatContext& ctx, index_helper<N>) const ->
      typename FormatContext::iterator {
    auto out = std::get<sizeof...(T) - N>(formatters)
                   .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
    if (N > 1) {
      out = std::copy(value.sep.begin(), value.sep.end(), out);
      ctx.advance_to(out);
      return format_subroutine(value, ctx, index_helper<N - 1>{});
    }
    return out;
  }
};

Review comment (suggestion):
I suggest rephrasing the error message as something like "incompatible format specs for tuple elements".

Code after:
  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>
  auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
                 std::integral_constant<size_t, N>) const ->
      typename FormatContext::iterator {
    auto out = std::get<sizeof...(T) - N>(formatters_)
                   .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
    if (N > 1) {
      out = std::copy(value.sep.begin(), value.sep.end(), out);
      ctx.advance_to(out);
      return do_format(value, ctx, std::integral_constant<size_t, N - 1>{});
    }
    return out;
  }
};