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 e8d9b17
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 3 deletions.
52 changes: 49 additions & 3 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 @@ -516,9 +519,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 Down Expand Up @@ -607,6 +612,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
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 e8d9b17

Please sign in to comment.