mirror of
https://github.com/fmtlib/fmt.git
synced 2025-02-24 21:39:49 +00:00
Adding sentinel support to fmt::join(). (#1689)
This commit is contained in:
parent
6d66de3805
commit
c66aae1652
@ -291,6 +291,7 @@ template <> constexpr int num_bits<fallback_uintptr>() {
|
|||||||
// An approximation of iterator_t for pre-C++20 systems.
|
// An approximation of iterator_t for pre-C++20 systems.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using iterator_t = decltype(std::begin(std::declval<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.
|
// Detect the iterator category of *any* given type in a SFINAE-friendly way.
|
||||||
// Unfortunately, older implementations of std::iterator_traits are not safe
|
// Unfortunately, older implementations of std::iterator_traits are not safe
|
||||||
@ -3215,19 +3216,21 @@ template <> struct formatter<bytes> {
|
|||||||
detail::dynamic_format_specs<char> specs_;
|
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 begin;
|
||||||
It end;
|
Sentinel end;
|
||||||
basic_string_view<Char> sep;
|
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>
|
template <typename It, typename Sentinel, typename Char>
|
||||||
struct formatter<arg_join<It, Char>, Char>
|
struct formatter<arg_join<It, Sentinel, Char>, Char>
|
||||||
: formatter<typename std::iterator_traits<It>::value_type, Char> {
|
: formatter<typename std::iterator_traits<It>::value_type, Char> {
|
||||||
template <typename FormatContext>
|
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()) {
|
-> decltype(ctx.out()) {
|
||||||
using base = formatter<typename std::iterator_traits<It>::value_type, Char>;
|
using base = formatter<typename std::iterator_traits<It>::value_type, Char>;
|
||||||
auto it = value.begin;
|
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
|
Returns an object that formats the iterator range `[begin, end)` with elements
|
||||||
separated by `sep`.
|
separated by `sep`.
|
||||||
*/
|
*/
|
||||||
template <typename It>
|
template <typename It, typename Sentinel>
|
||||||
arg_join<It, char> join(It begin, It end, string_view sep) {
|
arg_join<It, Sentinel, char> join(It begin, Sentinel end, string_view sep) {
|
||||||
return {begin, end, sep};
|
return {begin, end, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename It>
|
template <typename It, typename Sentinel>
|
||||||
arg_join<It, wchar_t> join(It begin, It end, wstring_view sep) {
|
arg_join<It, Sentinel, wchar_t> join(It begin, Sentinel end, wstring_view sep) {
|
||||||
return {begin, end, sep};
|
return {begin, end, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3275,14 +3278,15 @@ arg_join<It, wchar_t> join(It begin, It end, wstring_view sep) {
|
|||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename Range>
|
template <typename Range>
|
||||||
arg_join<detail::iterator_t<const Range>, char> join(const Range& range,
|
arg_join<detail::iterator_t<const Range>, detail::sentinel_t<const Range>, char>
|
||||||
string_view sep) {
|
join(const Range& range, string_view sep) {
|
||||||
return join(std::begin(range), std::end(range), sep);
|
return join(std::begin(range), std::end(range), sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Range>
|
template <typename Range>
|
||||||
arg_join<detail::iterator_t<const Range>, wchar_t> join(const Range& range,
|
arg_join<detail::iterator_t<const Range>, detail::sentinel_t<const Range>,
|
||||||
wstring_view sep) {
|
wchar_t>
|
||||||
|
join(const Range& range, wstring_view sep) {
|
||||||
return join(std::begin(range), std::end(range), sep);
|
return join(std::begin(range), std::end(range), sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +261,9 @@ struct formatter<RangeT, Char,
|
|||||||
FormatContext& ctx) {
|
FormatContext& ctx) {
|
||||||
auto out = detail::copy(formatting.prefix, ctx.out());
|
auto out = detail::copy(formatting.prefix, ctx.out());
|
||||||
size_t i = 0;
|
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 (i > 0) {
|
||||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||||
out = detail::copy(formatting.delimiter, 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
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
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,
|
||||||
std::initializer_list<T> list, string_view sep) {
|
string_view sep) {
|
||||||
return join(std::begin(list), std::end(list), sep);
|
return join(std::begin(list), std::end(list), sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
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,
|
||||||
std::initializer_list<T> list, wstring_view sep) {
|
wstring_view sep) {
|
||||||
return join(std::begin(list), std::end(list), sep);
|
return join(std::begin(list), std::end(list), sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,3 +139,17 @@ TEST(RangesTest, FormatStringLike) {
|
|||||||
EXPECT_EQ("foo", fmt::format("{}", string_like()));
|
EXPECT_EQ("foo", fmt::format("{}", string_like()));
|
||||||
}
|
}
|
||||||
#endif // FMT_USE_STRING_VIEW
|
#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, "_")));
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user