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 <type_traits>
18 // output only up to N items from the range.
19 #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
20 # define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
25 template <typename Char
> struct formatting_base
{
26 template <typename ParseContext
>
27 FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) -> decltype(ctx
.begin()) {
32 template <typename Char
, typename Enable
= void>
33 struct formatting_range
: formatting_base
<Char
> {
34 static FMT_CONSTEXPR_DECL
const std::size_t range_length_limit
=
35 FMT_RANGE_OUTPUT_LENGTH_LIMIT
; // output only up to N items from the
40 formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
41 static FMT_CONSTEXPR_DECL
const bool add_delimiter_spaces
= true;
42 static FMT_CONSTEXPR_DECL
const bool add_prepostfix_space
= false;
45 template <typename Char
, typename Enable
= void>
46 struct formatting_tuple
: formatting_base
<Char
> {
50 formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
51 static FMT_CONSTEXPR_DECL
const bool add_delimiter_spaces
= true;
52 static FMT_CONSTEXPR_DECL
const bool add_prepostfix_space
= false;
57 template <typename RangeT
, typename OutputIterator
>
58 OutputIterator
copy(const RangeT
& range
, OutputIterator out
) {
59 for (auto it
= range
.begin(), end
= range
.end(); it
!= end
; ++it
)
64 template <typename OutputIterator
>
65 OutputIterator
copy(const char* str
, OutputIterator out
) {
66 while (*str
) *out
++ = *str
++;
70 template <typename OutputIterator
>
71 OutputIterator
copy(char ch
, OutputIterator out
) {
76 /// Return true value if T has std::string interface, like std::string_view.
77 template <typename T
> class is_like_std_string
{
79 static auto check(U
* p
)
80 -> decltype((void)p
->find('a'), p
->length(), (void)p
->data(), int());
81 template <typename
> static void check(...);
84 static FMT_CONSTEXPR_DECL
const bool value
=
85 !std::is_void
<decltype(check
<T
>(FMT_NULL
))>::value
;
88 template <typename Char
>
89 struct is_like_std_string
<fmt::basic_string_view
<Char
>> : std::true_type
{};
91 template <typename
... Ts
> struct conditional_helper
{};
93 template <typename T
, typename _
= void> struct is_range_
: std::false_type
{};
95 #if !FMT_MSC_VER || FMT_MSC_VER > 1800
98 T
, typename
std::conditional
<
100 conditional_helper
<decltype(internal::declval
<T
>().begin()),
101 decltype(internal::declval
<T
>().end())>,
102 void>::type
> : std::true_type
{};
105 /// tuple_size and tuple_element check.
106 template <typename T
> class is_tuple_like_
{
107 template <typename U
>
108 static auto check(U
* p
) -> decltype(
109 std::tuple_size
<U
>::value
,
110 (void)internal::declval
<typename
std::tuple_element
<0, U
>::type
>(),
112 template <typename
> static void check(...);
115 static FMT_CONSTEXPR_DECL
const bool value
=
116 !std::is_void
<decltype(check
<T
>(FMT_NULL
))>::value
;
119 // Check for integer_sequence
120 #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
121 template <typename T
, T
... N
>
122 using integer_sequence
= std::integer_sequence
<T
, N
...>;
123 template <std::size_t... N
> using index_sequence
= std::index_sequence
<N
...>;
124 template <std::size_t N
>
125 using make_index_sequence
= std::make_index_sequence
<N
>;
127 template <typename T
, T
... N
> struct integer_sequence
{
128 typedef T value_type
;
130 static FMT_CONSTEXPR
std::size_t size() { return sizeof...(N
); }
133 template <std::size_t... N
>
134 using index_sequence
= integer_sequence
<std::size_t, N
...>;
136 template <typename T
, std::size_t N
, T
... Ns
>
137 struct make_integer_sequence
: make_integer_sequence
<T
, N
- 1, N
- 1, Ns
...> {};
138 template <typename T
, T
... Ns
>
139 struct make_integer_sequence
<T
, 0, Ns
...> : integer_sequence
<T
, Ns
...> {};
141 template <std::size_t N
>
142 using make_index_sequence
= make_integer_sequence
<std::size_t, N
>;
145 template <class Tuple
, class F
, size_t... Is
>
146 void for_each(index_sequence
<Is
...>, Tuple
&& tup
, F
&& f
) FMT_NOEXCEPT
{
148 // using free function get<I>(T) now.
149 const int _
[] = {0, ((void)f(get
<Is
>(tup
)), 0)...};
150 (void)_
; // blocks warnings
154 FMT_CONSTEXPR make_index_sequence
<std::tuple_size
<T
>::value
> get_indexes(
159 template <class Tuple
, class F
> void for_each(Tuple
&& tup
, F
&& f
) {
160 const auto indexes
= get_indexes(tup
);
161 for_each(indexes
, std::forward
<Tuple
>(tup
), std::forward
<F
>(f
));
164 template <typename Arg
, FMT_ENABLE_IF(!is_like_std_string
<
165 typename
std::decay
<Arg
>::type
>::value
)>
166 FMT_CONSTEXPR
const char* format_str_quoted(bool add_space
, const Arg
&) {
167 return add_space
? " {}" : "{}";
170 template <typename Arg
, FMT_ENABLE_IF(is_like_std_string
<
171 typename
std::decay
<Arg
>::type
>::value
)>
172 FMT_CONSTEXPR
const char* format_str_quoted(bool add_space
, const Arg
&) {
173 return add_space
? " \"{}\"" : "\"{}\"";
176 FMT_CONSTEXPR
const char* format_str_quoted(bool add_space
, const char*) {
177 return add_space
? " \"{}\"" : "\"{}\"";
179 FMT_CONSTEXPR
const wchar_t* format_str_quoted(bool add_space
, const wchar_t*) {
180 return add_space
? L
" \"{}\"" : L
"\"{}\"";
183 FMT_CONSTEXPR
const char* format_str_quoted(bool add_space
, const char) {
184 return add_space
? " '{}'" : "'{}'";
186 FMT_CONSTEXPR
const wchar_t* format_str_quoted(bool add_space
, const wchar_t) {
187 return add_space
? L
" '{}'" : L
"'{}'";
190 } // namespace internal
192 template <typename T
> struct is_tuple_like
{
193 static FMT_CONSTEXPR_DECL
const bool value
=
194 internal::is_tuple_like_
<T
>::value
&& !internal::is_range_
<T
>::value
;
197 template <typename TupleT
, typename Char
>
200 typename
std::enable_if
<fmt::is_tuple_like
<TupleT
>::value
>::type
> {
202 // C++11 generic lambda for format()
203 template <typename FormatContext
> struct format_each
{
204 template <typename T
> void operator()(const T
& v
) {
206 if (formatting
.add_prepostfix_space
) {
209 out
= internal::copy(formatting
.delimiter
, out
);
212 internal::format_str_quoted(
213 (formatting
.add_delimiter_spaces
&& i
> 0), v
),
218 formatting_tuple
<Char
>& formatting
;
220 typename
std::add_lvalue_reference
<decltype(
221 std::declval
<FormatContext
>().out())>::type out
;
225 formatting_tuple
<Char
> formatting
;
227 template <typename ParseContext
>
228 FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) -> decltype(ctx
.begin()) {
229 return formatting
.parse(ctx
);
232 template <typename FormatContext
= format_context
>
233 auto format(const TupleT
& values
, FormatContext
& ctx
) -> decltype(ctx
.out()) {
234 auto out
= ctx
.out();
236 internal::copy(formatting
.prefix
, out
);
238 internal::for_each(values
, format_each
<FormatContext
>{formatting
, i
, out
});
239 if (formatting
.add_prepostfix_space
) {
242 internal::copy(formatting
.postfix
, out
);
248 template <typename T
> struct is_range
{
249 static FMT_CONSTEXPR_DECL
const bool value
=
250 internal::is_range_
<T
>::value
&& !internal::is_like_std_string
<T
>::value
;
253 template <typename RangeT
, typename Char
>
254 struct formatter
<RangeT
, Char
,
255 typename
std::enable_if
<fmt::is_range
<RangeT
>::value
>::type
> {
256 formatting_range
<Char
> formatting
;
258 template <typename ParseContext
>
259 FMT_CONSTEXPR
auto parse(ParseContext
& ctx
) -> decltype(ctx
.begin()) {
260 return formatting
.parse(ctx
);
263 template <typename FormatContext
>
264 typename
FormatContext::iterator
format(const RangeT
& values
,
265 FormatContext
& ctx
) {
266 auto out
= internal::copy(formatting
.prefix
, ctx
.out());
268 for (auto it
= values
.begin(), end
= values
.end(); it
!= end
; ++it
) {
270 if (formatting
.add_prepostfix_space
) *out
++ = ' ';
271 out
= internal::copy(formatting
.delimiter
, out
);
274 internal::format_str_quoted(
275 (formatting
.add_delimiter_spaces
&& i
> 0), *it
),
277 if (++i
> formatting
.range_length_limit
) {
278 out
= format_to(out
, " ... <other elements>");
282 if (formatting
.add_prepostfix_space
) *out
++ = ' ';
283 return internal::copy(formatting
.postfix
, out
);
289 #endif // FMT_RANGES_H_