Skip to content

Commit

Permalink
Added range_format::(debug_)string formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
matt77hias committed Jun 13, 2024
1 parent 3e3062c commit 1eb4c76
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 3 deletions.
55 changes: 52 additions & 3 deletions include/fmt/ranges.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@
#ifndef FMT_IMPORT_STD
# include <initializer_list>
# include <iterator>
# include <string>
# include <tuple>
# include <type_traits>
# include <utility>
# ifdef __cpp_lib_ranges
# include <ranges>
# endif
#endif

#include "format.h"
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 Down Expand Up @@ -607,6 +613,49 @@ struct formatter<
}
};

// 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>;
#ifdef __cpp_lib_ranges
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>>;
#else
using string_type = std::basic_string<Char>;
#endif

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 (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
out = underlying_.format(
string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
return out;
}
};

template <typename It, typename Sentinel, typename Char = char>
struct join_view : detail::view {
It begin;
Expand Down
27 changes: 27 additions & 0 deletions test/ranges-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,33 @@ 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

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\"");
}

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

0 comments on commit 1eb4c76

Please sign in to comment.