Adding sentinel support to fmt::join(). (#1689)

This commit is contained in:
Barry Revzin 2020-05-18 08:31:01 -05:00 committed by GitHub
parent 6d66de3805
commit c66aae1652
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 39 additions and 19 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
@ -3215,19 +3216,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;
@ -3248,13 +3251,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};
}
@ -3275,14 +3278,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

@ -261,7 +261,9 @@ struct formatter<RangeT, Char,
FormatContext& ctx) {
auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
auto it = values.begin();
auto end = values.end();
for (; it != end; ++it) {
if (i > 0) {
if (formatting.add_prepostfix_space) *out++ = ' ';
out = detail::copy(formatting.delimiter, out);
@ -368,14 +370,14 @@ 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(
std::initializer_list<T> list, string_view sep) {
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(
std::initializer_list<T> list, wstring_view sep) {
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,17 @@ 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, "_")));
}