1 // Formatting library for C++ - experimental range support
3 // Copyright (c) 2012 - present, Victor Zverovich
4 // All rights reserved.
6 // For the license information refer to format.h.
8 // Copyright (c) 2018 - present, Remotion (Igor Schulz)
10 // {fmt} support for ranges, containers and types tuple interface.
15 #include <initializer_list>
16 #include <type_traits>
20 // output only up to N items from the range.
21 #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
22 # define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
27 template <typename Char
> struct formatting_base
{
28 template <typename ParseContext
>
29 FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) -> decltype(ctx
.begin()) {
34 template <typename Char
, typename Enable
= void>
35 struct formatting_range
: formatting_base
<Char
> {
36 static FMT_CONSTEXPR_DECL
const size_t range_length_limit
=
37 FMT_RANGE_OUTPUT_LENGTH_LIMIT
; // output only up to N items from the
42 formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
43 static FMT_CONSTEXPR_DECL
const bool add_delimiter_spaces
= true;
44 static FMT_CONSTEXPR_DECL
const bool add_prepostfix_space
= false;
47 template <typename Char
, typename Enable
= void>
48 struct formatting_tuple
: formatting_base
<Char
> {
52 formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
53 static FMT_CONSTEXPR_DECL
const bool add_delimiter_spaces
= true;
54 static FMT_CONSTEXPR_DECL
const bool add_prepostfix_space
= false;
59 template <typename RangeT
, typename OutputIterator
>
60 OutputIterator
copy(const RangeT
& range
, OutputIterator out
) {
61 for (auto it
= range
.begin(), end
= range
.end(); it
!= end
; ++it
)
66 template <typename OutputIterator
>
67 OutputIterator
copy(const char* str
, OutputIterator out
) {
68 while (*str
) *out
++ = *str
++;
72 template <typename OutputIterator
>
73 OutputIterator
copy(char ch
, OutputIterator out
) {
78 /// Return true value if T has std::string interface, like std::string_view.
79 template <typename T
> class is_like_std_string
{
81 static auto check(U
* p
)
82 -> decltype((void)p
->find('a'), p
->length(), (void)p
->data(), int());
83 template <typename
> static void check(...);
86 static FMT_CONSTEXPR_DECL
const bool value
=
87 is_string
<T
>::value
|| !std::is_void
<decltype(check
<T
>(nullptr))>::value
;
90 template <typename Char
>
91 struct is_like_std_string
<fmt::basic_string_view
<Char
>> : std::true_type
{};
93 template <typename
... Ts
> struct conditional_helper
{};
95 template <typename T
, typename _
= void> struct is_range_
: std::false_type
{};
97 #if !FMT_MSC_VER || FMT_MSC_VER > 1800
100 T
, conditional_t
<false,
101 conditional_helper
<decltype(std::declval
<T
>().begin()),
102 decltype(std::declval
<T
>().end())>,
103 void>> : std::true_type
{};
106 /// tuple_size and tuple_element check.
107 template <typename T
> class is_tuple_like_
{
108 template <typename U
>
109 static auto check(U
* p
) -> decltype(std::tuple_size
<U
>::value
, int());
110 template <typename
> static void check(...);
113 static FMT_CONSTEXPR_DECL
const bool value
=
114 !std::is_void
<decltype(check
<T
>(nullptr))>::value
;
117 // Check for integer_sequence
118 #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
119 template <typename T
, T
... N
>
120 using integer_sequence
= std::integer_sequence
<T
, N
...>;
121 template <size_t... N
> using index_sequence
= std::index_sequence
<N
...>;
122 template <size_t N
> using make_index_sequence
= std::make_index_sequence
<N
>;
124 template <typename T
, T
... N
> struct integer_sequence
{
125 using value_type
= T
;
127 static FMT_CONSTEXPR
size_t size() { return sizeof...(N
); }
130 template <size_t... N
> using index_sequence
= integer_sequence
<size_t, N
...>;
132 template <typename T
, size_t N
, T
... Ns
>
133 struct make_integer_sequence
: make_integer_sequence
<T
, N
- 1, N
- 1, Ns
...> {};
134 template <typename T
, T
... Ns
>
135 struct make_integer_sequence
<T
, 0, Ns
...> : integer_sequence
<T
, Ns
...> {};
138 using make_index_sequence
= make_integer_sequence
<size_t, N
>;
141 template <class Tuple
, class F
, size_t... Is
>
142 void for_each(index_sequence
<Is
...>, Tuple
&& tup
, F
&& f
) FMT_NOEXCEPT
{
144 // using free function get<I>(T) now.
145 const int _
[] = {0, ((void)f(get
<Is
>(tup
)), 0)...};
146 (void)_
; // blocks warnings
150 FMT_CONSTEXPR make_index_sequence
<std::tuple_size
<T
>::value
> get_indexes(
155 template <class Tuple
, class F
> void for_each(Tuple
&& tup
, F
&& f
) {
156 const auto indexes
= get_indexes(tup
);
157 for_each(indexes
, std::forward
<Tuple
>(tup
), std::forward
<F
>(f
));
160 template <typename Arg
, FMT_ENABLE_IF(!is_like_std_string
<
161 typename
std::decay
<Arg
>::type
>::value
)>
162 FMT_CONSTEXPR
const char* format_str_quoted(bool add_space
, const Arg
&) {
163 return add_space
? " {}" : "{}";
166 template <typename Arg
, FMT_ENABLE_IF(is_like_std_string
<
167 typename
std::decay
<Arg
>::type
>::value
)>
168 FMT_CONSTEXPR
const char* format_str_quoted(bool add_space
, const Arg
&) {
169 return add_space
? " \"{}\"" : "\"{}\"";
172 FMT_CONSTEXPR
const char* format_str_quoted(bool add_space
, const char*) {
173 return add_space
? " \"{}\"" : "\"{}\"";
175 FMT_CONSTEXPR
const wchar_t* format_str_quoted(bool add_space
, const wchar_t*) {
176 return add_space
? L
" \"{}\"" : L
"\"{}\"";
179 FMT_CONSTEXPR
const char* format_str_quoted(bool add_space
, const char) {
180 return add_space
? " '{}'" : "'{}'";
182 FMT_CONSTEXPR
const wchar_t* format_str_quoted(bool add_space
, const wchar_t) {
183 return add_space
? L
" '{}'" : L
"'{}'";
186 } // namespace detail
188 template <typename T
> struct is_tuple_like
{
189 static FMT_CONSTEXPR_DECL
const bool value
=
190 detail::is_tuple_like_
<T
>::value
&& !detail::is_range_
<T
>::value
;
193 template <typename TupleT
, typename Char
>
194 struct formatter
<TupleT
, Char
, enable_if_t
<fmt::is_tuple_like
<TupleT
>::value
>> {
196 // C++11 generic lambda for format()
197 template <typename FormatContext
> struct format_each
{
198 template <typename T
> void operator()(const T
& v
) {
200 if (formatting
.add_prepostfix_space
) {
203 out
= detail::copy(formatting
.delimiter
, out
);
206 detail::format_str_quoted(
207 (formatting
.add_delimiter_spaces
&& i
> 0), v
),
212 formatting_tuple
<Char
>& formatting
;
214 typename
std::add_lvalue_reference
<decltype(
215 std::declval
<FormatContext
>().out())>::type out
;
219 formatting_tuple
<Char
> formatting
;
221 template <typename ParseContext
>
222 FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) -> decltype(ctx
.begin()) {
223 return formatting
.parse(ctx
);
226 template <typename FormatContext
= format_context
>
227 auto format(const TupleT
& values
, FormatContext
& ctx
) -> decltype(ctx
.out()) {
228 auto out
= ctx
.out();
230 detail::copy(formatting
.prefix
, out
);
232 detail::for_each(values
, format_each
<FormatContext
>{formatting
, i
, out
});
233 if (formatting
.add_prepostfix_space
) {
236 detail::copy(formatting
.postfix
, out
);
242 template <typename T
, typename Char
> struct is_range
{
243 static FMT_CONSTEXPR_DECL
const bool value
=
244 detail::is_range_
<T
>::value
&& !detail::is_like_std_string
<T
>::value
&&
245 !std::is_convertible
<T
, std::basic_string
<Char
>>::value
&&
246 !std::is_constructible
<detail::std_string_view
<Char
>, T
>::value
;
249 template <typename RangeT
, typename Char
>
250 struct formatter
<RangeT
, Char
,
251 enable_if_t
<fmt::is_range
<RangeT
, Char
>::value
>> {
252 formatting_range
<Char
> formatting
;
254 template <typename ParseContext
>
255 FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) -> decltype(ctx
.begin()) {
256 return formatting
.parse(ctx
);
259 template <typename FormatContext
>
260 typename
FormatContext::iterator
format(const RangeT
& values
,
261 FormatContext
& ctx
) {
262 auto out
= detail::copy(formatting
.prefix
, ctx
.out());
264 auto it
= values
.begin();
265 auto end
= values
.end();
266 for (; it
!= end
; ++it
) {
268 if (formatting
.add_prepostfix_space
) *out
++ = ' ';
269 out
= detail::copy(formatting
.delimiter
, out
);
272 detail::format_str_quoted(
273 (formatting
.add_delimiter_spaces
&& i
> 0), *it
),
275 if (++i
> formatting
.range_length_limit
) {
276 out
= format_to(out
, " ... <other elements>");
280 if (formatting
.add_prepostfix_space
) *out
++ = ' ';
281 return detail::copy(formatting
.postfix
, out
);
285 template <typename Char
, typename
... T
> struct tuple_arg_join
: detail::view
{
286 const std::tuple
<T
...>& tuple
;
287 basic_string_view
<Char
> sep
;
289 tuple_arg_join(const std::tuple
<T
...>& t
, basic_string_view
<Char
> s
)
290 : tuple
{t
}, sep
{s
} {}
293 template <typename Char
, typename
... T
>
294 struct formatter
<tuple_arg_join
<Char
, T
...>, Char
> {
295 template <typename ParseContext
>
296 FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) -> decltype(ctx
.begin()) {
300 template <typename FormatContext
>
301 typename
FormatContext::iterator
format(
302 const tuple_arg_join
<Char
, T
...>& value
, FormatContext
& ctx
) {
303 return format(value
, ctx
, detail::make_index_sequence
<sizeof...(T
)>{});
307 template <typename FormatContext
, size_t... N
>
308 typename
FormatContext::iterator
format(
309 const tuple_arg_join
<Char
, T
...>& value
, FormatContext
& ctx
,
310 detail::index_sequence
<N
...>) {
311 return format_args(value
, ctx
, std::get
<N
>(value
.tuple
)...);
314 template <typename FormatContext
>
315 typename
FormatContext::iterator
format_args(
316 const tuple_arg_join
<Char
, T
...>&, FormatContext
& ctx
) {
317 // NOTE: for compilers that support C++17, this empty function instantiation
318 // can be replaced with a constexpr branch in the variadic overload.
322 template <typename FormatContext
, typename Arg
, typename
... Args
>
323 typename
FormatContext::iterator
format_args(
324 const tuple_arg_join
<Char
, T
...>& value
, FormatContext
& ctx
,
325 const Arg
& arg
, const Args
&... args
) {
326 using base
= formatter
<typename
std::decay
<Arg
>::type
, Char
>;
327 auto out
= ctx
.out();
328 out
= base
{}.format(arg
, ctx
);
329 if (sizeof...(Args
) > 0) {
330 out
= std::copy(value
.sep
.begin(), value
.sep
.end(), out
);
332 return format_args(value
, ctx
, args
...);
340 Returns an object that formats `tuple` with elements separated by `sep`.
344 std::tuple<int, char> t = {1, 'a'};
345 fmt::print("{}", fmt::join(t, ", "));
349 template <typename
... T
>
350 FMT_CONSTEXPR tuple_arg_join
<char, T
...> join(const std::tuple
<T
...>& tuple
,
355 template <typename
... T
>
356 FMT_CONSTEXPR tuple_arg_join
<wchar_t, T
...> join(const std::tuple
<T
...>& tuple
,
363 Returns an object that formats `initializer_list` with elements separated by
368 fmt::print("{}", fmt::join({1, 2, 3}, ", "));
372 template <typename T
>
373 arg_join
<const T
*, const T
*, char> join(std::initializer_list
<T
> list
,
375 return join(std::begin(list
), std::end(list
), sep
);
378 template <typename T
>
379 arg_join
<const T
*, const T
*, wchar_t> join(std::initializer_list
<T
> list
,
381 return join(std::begin(list
), std::end(list
), sep
);
386 #endif // FMT_RANGES_H_