mirror of
https://github.com/fmtlib/fmt.git
synced 2025-08-01 11:44:46 +02:00
Move misplaced join overloads to fmt/ranges.h
This commit is contained in:
@@ -321,8 +321,8 @@ template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
|
|||||||
constexpr FMT_INLINE auto is_constant_evaluated(
|
constexpr FMT_INLINE auto is_constant_evaluated(
|
||||||
bool default_value = false) noexcept -> bool {
|
bool default_value = false) noexcept -> bool {
|
||||||
// Workaround for incompatibility between libstdc++ consteval-based
|
// Workaround for incompatibility between libstdc++ consteval-based
|
||||||
// std::is_constant_evaluated() implementation and clang-14.
|
// std::is_constant_evaluated() implementation and clang-14:
|
||||||
// https://github.com/fmtlib/fmt/issues/3247
|
// https://github.com/fmtlib/fmt/issues/3247.
|
||||||
#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \
|
#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \
|
||||||
_GLIBCXX_RELEASE >= 12 && \
|
_GLIBCXX_RELEASE >= 12 && \
|
||||||
(FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500)
|
(FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500)
|
||||||
|
@@ -4224,84 +4224,6 @@ template <typename T> struct nested_formatter {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// DEPRECATED! join_view will be moved to ranges.h.
|
|
||||||
template <typename It, typename Sentinel, typename Char = char>
|
|
||||||
struct join_view : detail::view {
|
|
||||||
It begin;
|
|
||||||
Sentinel end;
|
|
||||||
basic_string_view<Char> sep;
|
|
||||||
|
|
||||||
join_view(It b, Sentinel e, basic_string_view<Char> s)
|
|
||||||
: begin(b), end(e), sep(s) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename It, typename Sentinel, typename Char>
|
|
||||||
struct formatter<join_view<It, Sentinel, Char>, Char> {
|
|
||||||
private:
|
|
||||||
using value_type =
|
|
||||||
#ifdef __cpp_lib_ranges
|
|
||||||
std::iter_value_t<It>;
|
|
||||||
#else
|
|
||||||
typename std::iterator_traits<It>::value_type;
|
|
||||||
#endif
|
|
||||||
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
|
|
||||||
return value_formatter_.parse(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(const join_view<It, Sentinel, Char>& value,
|
|
||||||
FormatContext& ctx) const -> decltype(ctx.out()) {
|
|
||||||
auto it = value.begin;
|
|
||||||
auto out = ctx.out();
|
|
||||||
if (it != value.end) {
|
|
||||||
out = value_formatter_.format(*it, ctx);
|
|
||||||
++it;
|
|
||||||
while (it != value.end) {
|
|
||||||
out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
|
|
||||||
ctx.advance_to(out);
|
|
||||||
out = value_formatter_.format(*it, ctx);
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
Returns a view that formats the iterator range `[begin, end)` with elements
|
|
||||||
separated by `sep`.
|
|
||||||
*/
|
|
||||||
template <typename It, typename Sentinel>
|
|
||||||
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
|
|
||||||
return {begin, end, sep};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Returns a view that formats `range` with elements separated by `sep`.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
std::vector<int> v = {1, 2, 3};
|
|
||||||
fmt::print("{}", fmt::join(v, ", "));
|
|
||||||
// Output: "1, 2, 3"
|
|
||||||
|
|
||||||
``fmt::join`` applies passed format specifiers to the range elements::
|
|
||||||
|
|
||||||
fmt::print("{:02}", fmt::join(v, ", "));
|
|
||||||
// Output: "01, 02, 03"
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename Range>
|
|
||||||
auto join(Range&& range, string_view sep)
|
|
||||||
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> {
|
|
||||||
return join(std::begin(range), std::end(range), sep);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Converts *value* to ``std::string`` using the default format for type *T*.
|
Converts *value* to ``std::string`` using the default format for type *T*.
|
||||||
|
@@ -574,6 +574,83 @@ struct formatter<
|
|||||||
Char> {
|
Char> {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename It, typename Sentinel, typename Char = char>
|
||||||
|
struct join_view : detail::view {
|
||||||
|
It begin;
|
||||||
|
Sentinel end;
|
||||||
|
basic_string_view<Char> sep;
|
||||||
|
|
||||||
|
join_view(It b, Sentinel e, basic_string_view<Char> s)
|
||||||
|
: begin(b), end(e), sep(s) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename It, typename Sentinel, typename Char>
|
||||||
|
struct formatter<join_view<It, Sentinel, Char>, Char> {
|
||||||
|
private:
|
||||||
|
using value_type =
|
||||||
|
#ifdef __cpp_lib_ranges
|
||||||
|
std::iter_value_t<It>;
|
||||||
|
#else
|
||||||
|
typename std::iterator_traits<It>::value_type;
|
||||||
|
#endif
|
||||||
|
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
|
||||||
|
return value_formatter_.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const join_view<It, Sentinel, Char>& value,
|
||||||
|
FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||||
|
auto it = value.begin;
|
||||||
|
auto out = ctx.out();
|
||||||
|
if (it != value.end) {
|
||||||
|
out = value_formatter_.format(*it, ctx);
|
||||||
|
++it;
|
||||||
|
while (it != value.end) {
|
||||||
|
out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
|
||||||
|
ctx.advance_to(out);
|
||||||
|
out = value_formatter_.format(*it, ctx);
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a view that formats the iterator range `[begin, end)` with elements
|
||||||
|
separated by `sep`.
|
||||||
|
*/
|
||||||
|
template <typename It, typename Sentinel>
|
||||||
|
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
|
||||||
|
return {begin, end, sep};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Returns a view that formats `range` with elements separated by `sep`.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
std::vector<int> v = {1, 2, 3};
|
||||||
|
fmt::print("{}", fmt::join(v, ", "));
|
||||||
|
// Output: "1, 2, 3"
|
||||||
|
|
||||||
|
``fmt::join`` applies passed format specifiers to the range elements::
|
||||||
|
|
||||||
|
fmt::print("{:02}", fmt::join(v, ", "));
|
||||||
|
// Output: "01, 02, 03"
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename Range>
|
||||||
|
auto join(Range&& range, string_view sep)
|
||||||
|
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> {
|
||||||
|
return join(std::begin(range), std::end(range), sep);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
||||||
const std::tuple<T...>& tuple;
|
const std::tuple<T...>& tuple;
|
||||||
basic_string_view<Char> sep;
|
basic_string_view<Char> sep;
|
||||||
@@ -708,13 +785,6 @@ FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
|
|||||||
return {tuple, sep};
|
return {tuple, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... T>
|
|
||||||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
|
|
||||||
basic_string_view<wchar_t> sep)
|
|
||||||
-> tuple_join_view<wchar_t, T...> {
|
|
||||||
return {tuple, sep};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Returns an object that formats `initializer_list` with elements separated by
|
Returns an object that formats `initializer_list` with elements separated by
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
#include <cwchar>
|
#include <cwchar>
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
#include "ranges.h"
|
||||||
|
|
||||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
# include <locale>
|
# include <locale>
|
||||||
@@ -96,6 +97,12 @@ auto join(std::initializer_list<T> list, wstring_view sep)
|
|||||||
return join(std::begin(list), std::end(list), sep);
|
return join(std::begin(list), std::end(list), sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
auto join(const std::tuple<T...>& tuple, basic_string_view<wchar_t> sep)
|
||||||
|
-> tuple_join_view<wchar_t, T...> {
|
||||||
|
return {tuple, sep};
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
auto vformat(basic_string_view<Char> format_str,
|
auto vformat(basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "fmt/chrono.h"
|
#include "fmt/chrono.h"
|
||||||
|
#include "fmt/ranges.h"
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
|
|
||||||
|
@@ -1791,40 +1791,6 @@ TEST(format_test, nested_formatter) {
|
|||||||
enum test_enum { foo, bar };
|
enum test_enum { foo, bar };
|
||||||
auto format_as(test_enum e) -> int { return e; }
|
auto format_as(test_enum e) -> int { return e; }
|
||||||
|
|
||||||
TEST(format_test, join) {
|
|
||||||
using fmt::join;
|
|
||||||
int v1[3] = {1, 2, 3};
|
|
||||||
auto v2 = std::vector<float>();
|
|
||||||
v2.push_back(1.2f);
|
|
||||||
v2.push_back(3.4f);
|
|
||||||
void* v3[2] = {&v1[0], &v1[1]};
|
|
||||||
|
|
||||||
EXPECT_EQ(fmt::format("({})", join(v1, v1 + 3, ", ")), "(1, 2, 3)");
|
|
||||||
EXPECT_EQ(fmt::format("({})", join(v1, v1 + 1, ", ")), "(1)");
|
|
||||||
EXPECT_EQ(fmt::format("({})", join(v1, v1, ", ")), "()");
|
|
||||||
EXPECT_EQ(fmt::format("({:03})", join(v1, v1 + 3, ", ")), "(001, 002, 003)");
|
|
||||||
EXPECT_EQ("(+01.20, +03.40)",
|
|
||||||
fmt::format("({:+06.2f})", join(v2.begin(), v2.end(), ", ")));
|
|
||||||
|
|
||||||
EXPECT_EQ(fmt::format("{0:{1}}", join(v1, v1 + 3, ", "), 1), "1, 2, 3");
|
|
||||||
|
|
||||||
EXPECT_EQ(fmt::format("{}, {}", v3[0], v3[1]),
|
|
||||||
fmt::format("{}", join(v3, v3 + 2, ", ")));
|
|
||||||
|
|
||||||
EXPECT_EQ(fmt::format("({})", join(v1, ", ")), "(1, 2, 3)");
|
|
||||||
EXPECT_EQ(fmt::format("({:+06.2f})", join(v2, ", ")), "(+01.20, +03.40)");
|
|
||||||
|
|
||||||
auto v4 = std::vector<test_enum>{foo, bar, foo};
|
|
||||||
EXPECT_EQ(fmt::format("{}", join(v4, " ")), "0 1 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cpp_lib_byte
|
|
||||||
TEST(format_test, join_bytes) {
|
|
||||||
auto v = std::vector<std::byte>{std::byte(1), std::byte(2), std::byte(3)};
|
|
||||||
EXPECT_EQ(fmt::format("{}", fmt::join(v, ", ")), "1, 2, 3");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::string vformat_message(int id, const char* format, fmt::format_args args) {
|
std::string vformat_message(int id, const char* format, fmt::format_args args) {
|
||||||
auto buffer = fmt::memory_buffer();
|
auto buffer = fmt::memory_buffer();
|
||||||
fmt::format_to(fmt::appender(buffer), "[{}] ", id);
|
fmt::format_to(fmt::appender(buffer), "[{}] ", id);
|
||||||
|
@@ -252,8 +252,7 @@ TEST(ostream_test, move_while_holding_data) {
|
|||||||
|
|
||||||
TEST(ostream_test, print) {
|
TEST(ostream_test, print) {
|
||||||
fmt::ostream out = fmt::output_file("test-file");
|
fmt::ostream out = fmt::output_file("test-file");
|
||||||
out.print("The answer is {}.\n",
|
out.print("The answer is {}.\n", 42);
|
||||||
fmt::join(std::initializer_list<int>{42}, ", "));
|
|
||||||
out.close();
|
out.close();
|
||||||
file in("test-file", file::RDONLY);
|
file in("test-file", file::RDONLY);
|
||||||
EXPECT_READ(in, "The answer is 42.\n");
|
EXPECT_READ(in, "The answer is 42.\n");
|
||||||
|
@@ -287,7 +287,7 @@ TEST(ranges_test, range) {
|
|||||||
EXPECT_EQ(fmt::format("{}", z), "[0, 0, 0]");
|
EXPECT_EQ(fmt::format("{}", z), "[0, 0, 0]");
|
||||||
}
|
}
|
||||||
|
|
||||||
enum test_enum { foo };
|
enum test_enum { foo, bar };
|
||||||
auto format_as(test_enum e) -> int { return e; }
|
auto format_as(test_enum e) -> int { return e; }
|
||||||
|
|
||||||
TEST(ranges_test, enum_range) {
|
TEST(ranges_test, enum_range) {
|
||||||
@@ -302,6 +302,40 @@ TEST(ranges_test, unformattable_range) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
TEST(ranges_test, join) {
|
||||||
|
using fmt::join;
|
||||||
|
int v1[3] = {1, 2, 3};
|
||||||
|
auto v2 = std::vector<float>();
|
||||||
|
v2.push_back(1.2f);
|
||||||
|
v2.push_back(3.4f);
|
||||||
|
void* v3[2] = {&v1[0], &v1[1]};
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("({})", join(v1, v1 + 3, ", ")), "(1, 2, 3)");
|
||||||
|
EXPECT_EQ(fmt::format("({})", join(v1, v1 + 1, ", ")), "(1)");
|
||||||
|
EXPECT_EQ(fmt::format("({})", join(v1, v1, ", ")), "()");
|
||||||
|
EXPECT_EQ(fmt::format("({:03})", join(v1, v1 + 3, ", ")), "(001, 002, 003)");
|
||||||
|
EXPECT_EQ("(+01.20, +03.40)",
|
||||||
|
fmt::format("({:+06.2f})", join(v2.begin(), v2.end(), ", ")));
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{0:{1}}", join(v1, v1 + 3, ", "), 1), "1, 2, 3");
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{}, {}", v3[0], v3[1]),
|
||||||
|
fmt::format("{}", join(v3, v3 + 2, ", ")));
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("({})", join(v1, ", ")), "(1, 2, 3)");
|
||||||
|
EXPECT_EQ(fmt::format("({:+06.2f})", join(v2, ", ")), "(+01.20, +03.40)");
|
||||||
|
|
||||||
|
auto v4 = std::vector<test_enum>{foo, bar, foo};
|
||||||
|
EXPECT_EQ(fmt::format("{}", join(v4, " ")), "0 1 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cpp_lib_byte
|
||||||
|
TEST(ranges_test, join_bytes) {
|
||||||
|
auto v = std::vector<std::byte>{std::byte(1), std::byte(2), std::byte(3)};
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join(v, ", ")), "1, 2, 3");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef FMT_RANGES_TEST_ENABLE_JOIN
|
#ifdef FMT_RANGES_TEST_ENABLE_JOIN
|
||||||
TEST(ranges_test, join_tuple) {
|
TEST(ranges_test, join_tuple) {
|
||||||
// Value tuple args.
|
// Value tuple args.
|
||||||
|
Reference in New Issue
Block a user