Skip to content

Commit

Permalink
Added range_format_string and range_format_debug_string formatters
Browse files Browse the repository at this point in the history
  • Loading branch information
matt77hias committed May 29, 2024
1 parent 33eba10 commit a4d947c
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 21 deletions.
89 changes: 68 additions & 21 deletions include/fmt/ranges.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
# include <tuple>
# include <type_traits>
# include <utility>
# ifdef __cpp_lib_ranges
# include <ranges>
# endif
#endif

#include "format.h"
Expand Down Expand Up @@ -171,11 +174,12 @@ template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
static auto all_true(...) -> std::false_type;

template <size_t... Is>
static auto check(index_sequence<Is...>) -> decltype(all_true(
index_sequence<Is...>{},
integer_sequence<bool,
(is_formattable<typename std::tuple_element<Is, T>::type,
C>::value)...>{}));
static auto check(index_sequence<Is...>)
-> decltype(all_true(
index_sequence<Is...>{},
integer_sequence<
bool, (is_formattable<typename std::tuple_element<Is, T>::type,
C>::value)...>{}));

public:
static constexpr const bool value =
Expand Down Expand Up @@ -335,8 +339,8 @@ struct formatter<Tuple, Char,
}

template <typename FormatContext>
auto format(const Tuple& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto format(const Tuple& value,
FormatContext& ctx) const -> decltype(ctx.out()) {
ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
detail::for_each2(
formatters_, value,
Expand Down Expand Up @@ -516,9 +520,11 @@ template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<conjunction<
bool_constant<range_format_kind<R, Char>::value !=
range_format::disabled &&
range_format_kind<R, Char>::value != range_format::map>
bool_constant<
range_format_kind<R, Char>::value != range_format::disabled &&
range_format_kind<R, Char>::value != range_format::map &&
range_format_kind<R, Char>::value != range_format::string &&
range_format_kind<R, Char>::value != range_format::debug_string>
// Workaround a bug in MSVC 2015 and earlier.
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
,
Expand All @@ -544,8 +550,8 @@ struct formatter<
}

template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto format(range_type& range,
FormatContext& ctx) const -> decltype(ctx.out()) {
return range_formatter_.format(range, ctx);
}
};
Expand Down Expand Up @@ -607,6 +613,47 @@ struct formatter<
}
};

#ifdef __cpp_lib_ranges
// A (debug_)string formatter.
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<range_format_kind<R, Char>::value == range_format::string ||
range_format_kind<R, Char>::value ==
range_format::debug_string>> {
private:
using range_type = detail::maybe_const_range<R>;
using string_type = conditional_t<
std::is_constructible_v<std::basic_string_view<Char>,
std::ranges::iterator_t<range_type>,
std::ranges::sentinel_t<range_type>>,
std::basic_string_view<Char>, std::basic_string<Char>>;

formatter<string_type, Char> underlying_;

public:
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return underlying_.parse(ctx);
}

template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if constexpr (range_format_kind<R, Char>::value ==
range_format::debug_string)
*out++ = '\"';
out = underlying_.format(
string_type{std::ranges::begin(range), std::ranges::end(range)}, ctx);
if constexpr (range_format_kind<R, Char>::value ==
range_format::debug_string)
*out++ = '\"';
return out;
}
};
#endif

template <typename It, typename Sentinel, typename Char = char>
struct join_view : detail::view {
It begin;
Expand Down Expand Up @@ -641,8 +688,8 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
}

template <typename FormatContext>
auto format(view_ref& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto format(view_ref& value,
FormatContext& ctx) const -> decltype(ctx.out()) {
auto it = std::forward<view_ref>(value).begin;
auto out = ctx.out();
if (it == value.end) return out;
Expand Down Expand Up @@ -684,9 +731,9 @@ auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
\endrst
*/
template <typename Range>
auto join(Range&& r, string_view sep)
-> join_view<decltype(detail::range_begin(r)),
decltype(detail::range_end(r))> {
auto join(Range&& r,
string_view sep) -> join_view<decltype(detail::range_begin(r)),
decltype(detail::range_end(r))> {
return {detail::range_begin(r), detail::range_end(r), sep};
}

Expand Down Expand Up @@ -817,8 +864,8 @@ FMT_BEGIN_EXPORT
\endrst
*/
template <typename... T>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
-> tuple_join_view<char, T...> {
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
string_view sep) -> tuple_join_view<char, T...> {
return {tuple, sep};
}

Expand All @@ -834,8 +881,8 @@ FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
\endrst
*/
template <typename T>
auto join(std::initializer_list<T> list, string_view sep)
-> join_view<const T*, const T*> {
auto join(std::initializer_list<T> list,
string_view sep) -> join_view<const T*, const T*> {
return join(std::begin(list), std::end(list), sep);
}

Expand Down
29 changes: 29 additions & 0 deletions test/ranges-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,35 @@ TEST(ranges_test, disabled_range_formatting_of_path) {
fmt::range_format::disabled);
}

struct vector_string : std::vector<char> {
using base = std::vector<char>;
using base::base;
};
struct vector_debug_string : std::vector<char> {
using base = std::vector<char>;
using base::base;
};
FMT_BEGIN_NAMESPACE
template <>
struct range_format_kind<vector_string, char>
: std::integral_constant<range_format, range_format::string> {};
template <>
struct range_format_kind<vector_debug_string, char>
: std::integral_constant<range_format, range_format::debug_string> {};
FMT_END_NAMESPACE

#ifdef __cpp_lib_ranges
TEST(ranges_test, range_format_string) {
const vector_string v{'f', 'o', 'o'};
EXPECT_EQ(fmt::format("{}", v), "foo");
}

TEST(ranges_test, range_format_debug_string) {
const vector_debug_string v{'f', 'o', 'o'};
EXPECT_EQ(fmt::format("{}", v), "\"foo\"");
}
#endif

// A range that provides non-const only begin()/end() to test fmt::join
// handles that.
//
Expand Down

0 comments on commit a4d947c

Please sign in to comment.