Adding sentinel support to fmt::join().

This commit is contained in:
Barry Revzin 2020-05-17 10:30:57 -05:00
parent 6b219a58db
commit 81cd096fdf
3 changed files with 35 additions and 16 deletions

View File

@ -291,6 +291,7 @@ template <> constexpr int num_bits<fallback_uintptr>() {
// An approximation of iterator_t for pre-C++20 systems.
template <typename T>
using iterator_t = decltype(std::begin(std::declval<T&>()));
template <typename T> using sentinel_t = decltype(std::end(std::declval<T&>()));
// Detect the iterator category of *any* given type in a SFINAE-friendly way.
// Unfortunately, older implementations of std::iterator_traits are not safe
@ -3209,19 +3210,21 @@ template <> struct formatter<bytes> {
detail::dynamic_format_specs<char> specs_;
};
template <typename It, typename Char> struct arg_join : detail::view {
template <typename It, typename Sentinel, typename Char>
struct arg_join : detail::view {
It begin;
It end;
Sentinel end;
basic_string_view<Char> sep;
arg_join(It b, It e, basic_string_view<Char> s) : begin(b), end(e), sep(s) {}
arg_join(It b, Sentinel e, basic_string_view<Char> s)
: begin(b), end(e), sep(s) {}
};
template <typename It, typename Char>
struct formatter<arg_join<It, Char>, Char>
template <typename It, typename Sentinel, typename Char>
struct formatter<arg_join<It, Sentinel, Char>, Char>
: formatter<typename std::iterator_traits<It>::value_type, Char> {
template <typename FormatContext>
auto format(const arg_join<It, Char>& value, FormatContext& ctx)
auto format(const arg_join<It, Sentinel, Char>& value, FormatContext& ctx)
-> decltype(ctx.out()) {
using base = formatter<typename std::iterator_traits<It>::value_type, Char>;
auto it = value.begin;
@ -3242,13 +3245,13 @@ struct formatter<arg_join<It, Char>, Char>
Returns an object that formats the iterator range `[begin, end)` with elements
separated by `sep`.
*/
template <typename It>
arg_join<It, char> join(It begin, It end, string_view sep) {
template <typename It, typename Sentinel>
arg_join<It, Sentinel, char> join(It begin, Sentinel end, string_view sep) {
return {begin, end, sep};
}
template <typename It>
arg_join<It, wchar_t> join(It begin, It end, wstring_view sep) {
template <typename It, typename Sentinel>
arg_join<It, Sentinel, wchar_t> join(It begin, Sentinel end, wstring_view sep) {
return {begin, end, sep};
}
@ -3269,14 +3272,15 @@ arg_join<It, wchar_t> join(It begin, It end, wstring_view sep) {
\endrst
*/
template <typename Range>
arg_join<detail::iterator_t<const Range>, char> join(const Range& range,
string_view sep) {
arg_join<detail::iterator_t<const Range>, detail::sentinel_t<const Range>, char>
join(const Range& range, string_view sep) {
return join(std::begin(range), std::end(range), sep);
}
template <typename Range>
arg_join<detail::iterator_t<const Range>, wchar_t> join(const Range& range,
wstring_view sep) {
arg_join<detail::iterator_t<const Range>, detail::sentinel_t<const Range>,
wchar_t>
join(const Range& range, wstring_view sep) {
return join(std::begin(range), std::end(range), sep);
}

View File

@ -368,13 +368,13 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
\endrst
*/
template <typename T>
arg_join<detail::iterator_t<const std::initializer_list<T>>, char> join(
arg_join<const T*, const T*, char> join(
std::initializer_list<T> list, string_view sep) {
return join(std::begin(list), std::end(list), sep);
}
template <typename T>
arg_join<detail::iterator_t<const std::initializer_list<T>>, wchar_t> join(
arg_join<const T*, const T*, wchar_t> join(
std::initializer_list<T> list, wstring_view sep) {
return join(std::begin(list), std::end(list), sep);
}

View File

@ -139,3 +139,18 @@ TEST(RangesTest, FormatStringLike) {
EXPECT_EQ("foo", fmt::format("{}", string_like()));
}
#endif // FMT_USE_STRING_VIEW
struct zstring_sentinel {};
bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
struct zstring {
const char* p;
const char* begin() const { return p; }
zstring_sentinel end() const { return {}; }
};
TEST(RangesTest, JoinSentinel) {
zstring hello{"hello"};
EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello));
EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_")));
}