1*5c90c05cSAndroid Build Coastguard Worker // Formatting library for C++ - range and tuple support 2*5c90c05cSAndroid Build Coastguard Worker // 3*5c90c05cSAndroid Build Coastguard Worker // Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors 4*5c90c05cSAndroid Build Coastguard Worker // All rights reserved. 5*5c90c05cSAndroid Build Coastguard Worker // 6*5c90c05cSAndroid Build Coastguard Worker // For the license information refer to format.h. 7*5c90c05cSAndroid Build Coastguard Worker 8*5c90c05cSAndroid Build Coastguard Worker #ifndef FMT_RANGES_H_ 9*5c90c05cSAndroid Build Coastguard Worker #define FMT_RANGES_H_ 10*5c90c05cSAndroid Build Coastguard Worker 11*5c90c05cSAndroid Build Coastguard Worker #ifndef FMT_MODULE 12*5c90c05cSAndroid Build Coastguard Worker # include <initializer_list> 13*5c90c05cSAndroid Build Coastguard Worker # include <iterator> 14*5c90c05cSAndroid Build Coastguard Worker # include <string> 15*5c90c05cSAndroid Build Coastguard Worker # include <tuple> 16*5c90c05cSAndroid Build Coastguard Worker # include <type_traits> 17*5c90c05cSAndroid Build Coastguard Worker # include <utility> 18*5c90c05cSAndroid Build Coastguard Worker #endif 19*5c90c05cSAndroid Build Coastguard Worker 20*5c90c05cSAndroid Build Coastguard Worker #include "format.h" 21*5c90c05cSAndroid Build Coastguard Worker 22*5c90c05cSAndroid Build Coastguard Worker FMT_BEGIN_NAMESPACE 23*5c90c05cSAndroid Build Coastguard Worker 24*5c90c05cSAndroid Build Coastguard Worker FMT_EXPORT 25*5c90c05cSAndroid Build Coastguard Worker enum class range_format { disabled, map, set, sequence, string, debug_string }; 26*5c90c05cSAndroid Build Coastguard Worker 27*5c90c05cSAndroid Build Coastguard Worker namespace detail { 28*5c90c05cSAndroid Build Coastguard Worker 29*5c90c05cSAndroid Build Coastguard Worker template <typename T> class is_map { 30*5c90c05cSAndroid Build Coastguard Worker template <typename U> static auto check(U*) -> typename U::mapped_type; 31*5c90c05cSAndroid Build Coastguard Worker template <typename> static void check(...); 32*5c90c05cSAndroid Build Coastguard Worker 33*5c90c05cSAndroid Build Coastguard Worker public: 34*5c90c05cSAndroid Build Coastguard Worker static constexpr const bool value = 35*5c90c05cSAndroid Build Coastguard Worker !std::is_void<decltype(check<T>(nullptr))>::value; 36*5c90c05cSAndroid Build Coastguard Worker }; 37*5c90c05cSAndroid Build Coastguard Worker 38*5c90c05cSAndroid Build Coastguard Worker template <typename T> class is_set { 39*5c90c05cSAndroid Build Coastguard Worker template <typename U> static auto check(U*) -> typename U::key_type; 40*5c90c05cSAndroid Build Coastguard Worker template <typename> static void check(...); 41*5c90c05cSAndroid Build Coastguard Worker 42*5c90c05cSAndroid Build Coastguard Worker public: 43*5c90c05cSAndroid Build Coastguard Worker static constexpr const bool value = 44*5c90c05cSAndroid Build Coastguard Worker !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value; 45*5c90c05cSAndroid Build Coastguard Worker }; 46*5c90c05cSAndroid Build Coastguard Worker 47*5c90c05cSAndroid Build Coastguard Worker // C array overload 48*5c90c05cSAndroid Build Coastguard Worker template <typename T, std::size_t N> 49*5c90c05cSAndroid Build Coastguard Worker auto range_begin(const T (&arr)[N]) -> const T* { 50*5c90c05cSAndroid Build Coastguard Worker return arr; 51*5c90c05cSAndroid Build Coastguard Worker } 52*5c90c05cSAndroid Build Coastguard Worker template <typename T, std::size_t N> 53*5c90c05cSAndroid Build Coastguard Worker auto range_end(const T (&arr)[N]) -> const T* { 54*5c90c05cSAndroid Build Coastguard Worker return arr + N; 55*5c90c05cSAndroid Build Coastguard Worker } 56*5c90c05cSAndroid Build Coastguard Worker 57*5c90c05cSAndroid Build Coastguard Worker template <typename T, typename Enable = void> 58*5c90c05cSAndroid Build Coastguard Worker struct has_member_fn_begin_end_t : std::false_type {}; 59*5c90c05cSAndroid Build Coastguard Worker 60*5c90c05cSAndroid Build Coastguard Worker template <typename T> 61*5c90c05cSAndroid Build Coastguard Worker struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()), 62*5c90c05cSAndroid Build Coastguard Worker decltype(std::declval<T>().end())>> 63*5c90c05cSAndroid Build Coastguard Worker : std::true_type {}; 64*5c90c05cSAndroid Build Coastguard Worker 65*5c90c05cSAndroid Build Coastguard Worker // Member function overloads. 66*5c90c05cSAndroid Build Coastguard Worker template <typename T> 67*5c90c05cSAndroid Build Coastguard Worker auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) { 68*5c90c05cSAndroid Build Coastguard Worker return static_cast<T&&>(rng).begin(); 69*5c90c05cSAndroid Build Coastguard Worker } 70*5c90c05cSAndroid Build Coastguard Worker template <typename T> 71*5c90c05cSAndroid Build Coastguard Worker auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) { 72*5c90c05cSAndroid Build Coastguard Worker return static_cast<T&&>(rng).end(); 73*5c90c05cSAndroid Build Coastguard Worker } 74*5c90c05cSAndroid Build Coastguard Worker 75*5c90c05cSAndroid Build Coastguard Worker // ADL overloads. Only participate in overload resolution if member functions 76*5c90c05cSAndroid Build Coastguard Worker // are not found. 77*5c90c05cSAndroid Build Coastguard Worker template <typename T> 78*5c90c05cSAndroid Build Coastguard Worker auto range_begin(T&& rng) 79*5c90c05cSAndroid Build Coastguard Worker -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value, 80*5c90c05cSAndroid Build Coastguard Worker decltype(begin(static_cast<T&&>(rng)))> { 81*5c90c05cSAndroid Build Coastguard Worker return begin(static_cast<T&&>(rng)); 82*5c90c05cSAndroid Build Coastguard Worker } 83*5c90c05cSAndroid Build Coastguard Worker template <typename T> 84*5c90c05cSAndroid Build Coastguard Worker auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value, 85*5c90c05cSAndroid Build Coastguard Worker decltype(end(static_cast<T&&>(rng)))> { 86*5c90c05cSAndroid Build Coastguard Worker return end(static_cast<T&&>(rng)); 87*5c90c05cSAndroid Build Coastguard Worker } 88*5c90c05cSAndroid Build Coastguard Worker 89*5c90c05cSAndroid Build Coastguard Worker template <typename T, typename Enable = void> 90*5c90c05cSAndroid Build Coastguard Worker struct has_const_begin_end : std::false_type {}; 91*5c90c05cSAndroid Build Coastguard Worker template <typename T, typename Enable = void> 92*5c90c05cSAndroid Build Coastguard Worker struct has_mutable_begin_end : std::false_type {}; 93*5c90c05cSAndroid Build Coastguard Worker 94*5c90c05cSAndroid Build Coastguard Worker template <typename T> 95*5c90c05cSAndroid Build Coastguard Worker struct has_const_begin_end< 96*5c90c05cSAndroid Build Coastguard Worker T, void_t<decltype(*detail::range_begin( 97*5c90c05cSAndroid Build Coastguard Worker std::declval<const remove_cvref_t<T>&>())), 98*5c90c05cSAndroid Build Coastguard Worker decltype(detail::range_end( 99*5c90c05cSAndroid Build Coastguard Worker std::declval<const remove_cvref_t<T>&>()))>> 100*5c90c05cSAndroid Build Coastguard Worker : std::true_type {}; 101*5c90c05cSAndroid Build Coastguard Worker 102*5c90c05cSAndroid Build Coastguard Worker template <typename T> 103*5c90c05cSAndroid Build Coastguard Worker struct has_mutable_begin_end< 104*5c90c05cSAndroid Build Coastguard Worker T, void_t<decltype(*detail::range_begin(std::declval<T&>())), 105*5c90c05cSAndroid Build Coastguard Worker decltype(detail::range_end(std::declval<T&>())), 106*5c90c05cSAndroid Build Coastguard Worker // the extra int here is because older versions of MSVC don't 107*5c90c05cSAndroid Build Coastguard Worker // SFINAE properly unless there are distinct types 108*5c90c05cSAndroid Build Coastguard Worker int>> : std::true_type {}; 109*5c90c05cSAndroid Build Coastguard Worker 110*5c90c05cSAndroid Build Coastguard Worker template <typename T, typename _ = void> struct is_range_ : std::false_type {}; 111*5c90c05cSAndroid Build Coastguard Worker template <typename T> 112*5c90c05cSAndroid Build Coastguard Worker struct is_range_<T, void> 113*5c90c05cSAndroid Build Coastguard Worker : std::integral_constant<bool, (has_const_begin_end<T>::value || 114*5c90c05cSAndroid Build Coastguard Worker has_mutable_begin_end<T>::value)> {}; 115*5c90c05cSAndroid Build Coastguard Worker 116*5c90c05cSAndroid Build Coastguard Worker // tuple_size and tuple_element check. 117*5c90c05cSAndroid Build Coastguard Worker template <typename T> class is_tuple_like_ { 118*5c90c05cSAndroid Build Coastguard Worker template <typename U, typename V = typename std::remove_cv<U>::type> 119*5c90c05cSAndroid Build Coastguard Worker static auto check(U* p) -> decltype(std::tuple_size<V>::value, 0); 120*5c90c05cSAndroid Build Coastguard Worker template <typename> static void check(...); 121*5c90c05cSAndroid Build Coastguard Worker 122*5c90c05cSAndroid Build Coastguard Worker public: 123*5c90c05cSAndroid Build Coastguard Worker static constexpr const bool value = 124*5c90c05cSAndroid Build Coastguard Worker !std::is_void<decltype(check<T>(nullptr))>::value; 125*5c90c05cSAndroid Build Coastguard Worker }; 126*5c90c05cSAndroid Build Coastguard Worker 127*5c90c05cSAndroid Build Coastguard Worker // Check for integer_sequence 128*5c90c05cSAndroid Build Coastguard Worker #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900 129*5c90c05cSAndroid Build Coastguard Worker template <typename T, T... N> 130*5c90c05cSAndroid Build Coastguard Worker using integer_sequence = std::integer_sequence<T, N...>; 131*5c90c05cSAndroid Build Coastguard Worker template <size_t... N> using index_sequence = std::index_sequence<N...>; 132*5c90c05cSAndroid Build Coastguard Worker template <size_t N> using make_index_sequence = std::make_index_sequence<N>; 133*5c90c05cSAndroid Build Coastguard Worker #else 134*5c90c05cSAndroid Build Coastguard Worker template <typename T, T... N> struct integer_sequence { 135*5c90c05cSAndroid Build Coastguard Worker using value_type = T; 136*5c90c05cSAndroid Build Coastguard Worker 137*5c90c05cSAndroid Build Coastguard Worker static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); } 138*5c90c05cSAndroid Build Coastguard Worker }; 139*5c90c05cSAndroid Build Coastguard Worker 140*5c90c05cSAndroid Build Coastguard Worker template <size_t... N> using index_sequence = integer_sequence<size_t, N...>; 141*5c90c05cSAndroid Build Coastguard Worker 142*5c90c05cSAndroid Build Coastguard Worker template <typename T, size_t N, T... Ns> 143*5c90c05cSAndroid Build Coastguard Worker struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {}; 144*5c90c05cSAndroid Build Coastguard Worker template <typename T, T... Ns> 145*5c90c05cSAndroid Build Coastguard Worker struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {}; 146*5c90c05cSAndroid Build Coastguard Worker 147*5c90c05cSAndroid Build Coastguard Worker template <size_t N> 148*5c90c05cSAndroid Build Coastguard Worker using make_index_sequence = make_integer_sequence<size_t, N>; 149*5c90c05cSAndroid Build Coastguard Worker #endif 150*5c90c05cSAndroid Build Coastguard Worker 151*5c90c05cSAndroid Build Coastguard Worker template <typename T> 152*5c90c05cSAndroid Build Coastguard Worker using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>; 153*5c90c05cSAndroid Build Coastguard Worker 154*5c90c05cSAndroid Build Coastguard Worker template <typename T, typename C, bool = is_tuple_like_<T>::value> 155*5c90c05cSAndroid Build Coastguard Worker class is_tuple_formattable_ { 156*5c90c05cSAndroid Build Coastguard Worker public: 157*5c90c05cSAndroid Build Coastguard Worker static constexpr const bool value = false; 158*5c90c05cSAndroid Build Coastguard Worker }; 159*5c90c05cSAndroid Build Coastguard Worker template <typename T, typename C> class is_tuple_formattable_<T, C, true> { 160*5c90c05cSAndroid Build Coastguard Worker template <size_t... Is> 161*5c90c05cSAndroid Build Coastguard Worker static auto all_true(index_sequence<Is...>, 162*5c90c05cSAndroid Build Coastguard Worker integer_sequence<bool, (Is >= 0)...>) -> std::true_type; 163*5c90c05cSAndroid Build Coastguard Worker static auto all_true(...) -> std::false_type; 164*5c90c05cSAndroid Build Coastguard Worker 165*5c90c05cSAndroid Build Coastguard Worker template <size_t... Is> 166*5c90c05cSAndroid Build Coastguard Worker static auto check(index_sequence<Is...>) -> decltype(all_true( 167*5c90c05cSAndroid Build Coastguard Worker index_sequence<Is...>{}, 168*5c90c05cSAndroid Build Coastguard Worker integer_sequence<bool, 169*5c90c05cSAndroid Build Coastguard Worker (is_formattable<typename std::tuple_element<Is, T>::type, 170*5c90c05cSAndroid Build Coastguard Worker C>::value)...>{})); 171*5c90c05cSAndroid Build Coastguard Worker 172*5c90c05cSAndroid Build Coastguard Worker public: 173*5c90c05cSAndroid Build Coastguard Worker static constexpr const bool value = 174*5c90c05cSAndroid Build Coastguard Worker decltype(check(tuple_index_sequence<T>{}))::value; 175*5c90c05cSAndroid Build Coastguard Worker }; 176*5c90c05cSAndroid Build Coastguard Worker 177*5c90c05cSAndroid Build Coastguard Worker template <typename Tuple, typename F, size_t... Is> 178*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) { 179*5c90c05cSAndroid Build Coastguard Worker using std::get; 180*5c90c05cSAndroid Build Coastguard Worker // Using a free function get<Is>(Tuple) now. 181*5c90c05cSAndroid Build Coastguard Worker const int unused[] = {0, ((void)f(get<Is>(t)), 0)...}; 182*5c90c05cSAndroid Build Coastguard Worker ignore_unused(unused); 183*5c90c05cSAndroid Build Coastguard Worker } 184*5c90c05cSAndroid Build Coastguard Worker 185*5c90c05cSAndroid Build Coastguard Worker template <typename Tuple, typename F> 186*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) { 187*5c90c05cSAndroid Build Coastguard Worker for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(), 188*5c90c05cSAndroid Build Coastguard Worker std::forward<Tuple>(t), std::forward<F>(f)); 189*5c90c05cSAndroid Build Coastguard Worker } 190*5c90c05cSAndroid Build Coastguard Worker 191*5c90c05cSAndroid Build Coastguard Worker template <typename Tuple1, typename Tuple2, typename F, size_t... Is> 192*5c90c05cSAndroid Build Coastguard Worker void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) { 193*5c90c05cSAndroid Build Coastguard Worker using std::get; 194*5c90c05cSAndroid Build Coastguard Worker const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...}; 195*5c90c05cSAndroid Build Coastguard Worker ignore_unused(unused); 196*5c90c05cSAndroid Build Coastguard Worker } 197*5c90c05cSAndroid Build Coastguard Worker 198*5c90c05cSAndroid Build Coastguard Worker template <typename Tuple1, typename Tuple2, typename F> 199*5c90c05cSAndroid Build Coastguard Worker void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) { 200*5c90c05cSAndroid Build Coastguard Worker for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(), 201*5c90c05cSAndroid Build Coastguard Worker std::forward<Tuple1>(t1), std::forward<Tuple2>(t2), 202*5c90c05cSAndroid Build Coastguard Worker std::forward<F>(f)); 203*5c90c05cSAndroid Build Coastguard Worker } 204*5c90c05cSAndroid Build Coastguard Worker 205*5c90c05cSAndroid Build Coastguard Worker namespace tuple { 206*5c90c05cSAndroid Build Coastguard Worker // Workaround a bug in MSVC 2019 (v140). 207*5c90c05cSAndroid Build Coastguard Worker template <typename Char, typename... T> 208*5c90c05cSAndroid Build Coastguard Worker using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>; 209*5c90c05cSAndroid Build Coastguard Worker 210*5c90c05cSAndroid Build Coastguard Worker using std::get; 211*5c90c05cSAndroid Build Coastguard Worker template <typename Tuple, typename Char, std::size_t... Is> 212*5c90c05cSAndroid Build Coastguard Worker auto get_formatters(index_sequence<Is...>) 213*5c90c05cSAndroid Build Coastguard Worker -> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>; 214*5c90c05cSAndroid Build Coastguard Worker } // namespace tuple 215*5c90c05cSAndroid Build Coastguard Worker 216*5c90c05cSAndroid Build Coastguard Worker #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 217*5c90c05cSAndroid Build Coastguard Worker // Older MSVC doesn't get the reference type correctly for arrays. 218*5c90c05cSAndroid Build Coastguard Worker template <typename R> struct range_reference_type_impl { 219*5c90c05cSAndroid Build Coastguard Worker using type = decltype(*detail::range_begin(std::declval<R&>())); 220*5c90c05cSAndroid Build Coastguard Worker }; 221*5c90c05cSAndroid Build Coastguard Worker 222*5c90c05cSAndroid Build Coastguard Worker template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> { 223*5c90c05cSAndroid Build Coastguard Worker using type = T&; 224*5c90c05cSAndroid Build Coastguard Worker }; 225*5c90c05cSAndroid Build Coastguard Worker 226*5c90c05cSAndroid Build Coastguard Worker template <typename T> 227*5c90c05cSAndroid Build Coastguard Worker using range_reference_type = typename range_reference_type_impl<T>::type; 228*5c90c05cSAndroid Build Coastguard Worker #else 229*5c90c05cSAndroid Build Coastguard Worker template <typename Range> 230*5c90c05cSAndroid Build Coastguard Worker using range_reference_type = 231*5c90c05cSAndroid Build Coastguard Worker decltype(*detail::range_begin(std::declval<Range&>())); 232*5c90c05cSAndroid Build Coastguard Worker #endif 233*5c90c05cSAndroid Build Coastguard Worker 234*5c90c05cSAndroid Build Coastguard Worker // We don't use the Range's value_type for anything, but we do need the Range's 235*5c90c05cSAndroid Build Coastguard Worker // reference type, with cv-ref stripped. 236*5c90c05cSAndroid Build Coastguard Worker template <typename Range> 237*5c90c05cSAndroid Build Coastguard Worker using uncvref_type = remove_cvref_t<range_reference_type<Range>>; 238*5c90c05cSAndroid Build Coastguard Worker 239*5c90c05cSAndroid Build Coastguard Worker template <typename Formatter> 240*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set) 241*5c90c05cSAndroid Build Coastguard Worker -> decltype(f.set_debug_format(set)) { 242*5c90c05cSAndroid Build Coastguard Worker f.set_debug_format(set); 243*5c90c05cSAndroid Build Coastguard Worker } 244*5c90c05cSAndroid Build Coastguard Worker template <typename Formatter> 245*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} 246*5c90c05cSAndroid Build Coastguard Worker 247*5c90c05cSAndroid Build Coastguard Worker template <typename T> 248*5c90c05cSAndroid Build Coastguard Worker struct range_format_kind_ 249*5c90c05cSAndroid Build Coastguard Worker : std::integral_constant<range_format, 250*5c90c05cSAndroid Build Coastguard Worker std::is_same<uncvref_type<T>, T>::value 251*5c90c05cSAndroid Build Coastguard Worker ? range_format::disabled 252*5c90c05cSAndroid Build Coastguard Worker : is_map<T>::value ? range_format::map 253*5c90c05cSAndroid Build Coastguard Worker : is_set<T>::value ? range_format::set 254*5c90c05cSAndroid Build Coastguard Worker : range_format::sequence> {}; 255*5c90c05cSAndroid Build Coastguard Worker 256*5c90c05cSAndroid Build Coastguard Worker template <range_format K> 257*5c90c05cSAndroid Build Coastguard Worker using range_format_constant = std::integral_constant<range_format, K>; 258*5c90c05cSAndroid Build Coastguard Worker 259*5c90c05cSAndroid Build Coastguard Worker // These are not generic lambdas for compatibility with C++11. 260*5c90c05cSAndroid Build Coastguard Worker template <typename Char> struct parse_empty_specs { 261*5c90c05cSAndroid Build Coastguard Worker template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) { 262*5c90c05cSAndroid Build Coastguard Worker f.parse(ctx); 263*5c90c05cSAndroid Build Coastguard Worker detail::maybe_set_debug_format(f, true); 264*5c90c05cSAndroid Build Coastguard Worker } 265*5c90c05cSAndroid Build Coastguard Worker parse_context<Char>& ctx; 266*5c90c05cSAndroid Build Coastguard Worker }; 267*5c90c05cSAndroid Build Coastguard Worker template <typename FormatContext> struct format_tuple_element { 268*5c90c05cSAndroid Build Coastguard Worker using char_type = typename FormatContext::char_type; 269*5c90c05cSAndroid Build Coastguard Worker 270*5c90c05cSAndroid Build Coastguard Worker template <typename T> 271*5c90c05cSAndroid Build Coastguard Worker void operator()(const formatter<T, char_type>& f, const T& v) { 272*5c90c05cSAndroid Build Coastguard Worker if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out())); 273*5c90c05cSAndroid Build Coastguard Worker ctx.advance_to(f.format(v, ctx)); 274*5c90c05cSAndroid Build Coastguard Worker ++i; 275*5c90c05cSAndroid Build Coastguard Worker } 276*5c90c05cSAndroid Build Coastguard Worker 277*5c90c05cSAndroid Build Coastguard Worker int i; 278*5c90c05cSAndroid Build Coastguard Worker FormatContext& ctx; 279*5c90c05cSAndroid Build Coastguard Worker basic_string_view<char_type> separator; 280*5c90c05cSAndroid Build Coastguard Worker }; 281*5c90c05cSAndroid Build Coastguard Worker 282*5c90c05cSAndroid Build Coastguard Worker } // namespace detail 283*5c90c05cSAndroid Build Coastguard Worker 284*5c90c05cSAndroid Build Coastguard Worker template <typename T> struct is_tuple_like { 285*5c90c05cSAndroid Build Coastguard Worker static constexpr const bool value = 286*5c90c05cSAndroid Build Coastguard Worker detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value; 287*5c90c05cSAndroid Build Coastguard Worker }; 288*5c90c05cSAndroid Build Coastguard Worker 289*5c90c05cSAndroid Build Coastguard Worker template <typename T, typename C> struct is_tuple_formattable { 290*5c90c05cSAndroid Build Coastguard Worker static constexpr const bool value = 291*5c90c05cSAndroid Build Coastguard Worker detail::is_tuple_formattable_<T, C>::value; 292*5c90c05cSAndroid Build Coastguard Worker }; 293*5c90c05cSAndroid Build Coastguard Worker 294*5c90c05cSAndroid Build Coastguard Worker template <typename Tuple, typename Char> 295*5c90c05cSAndroid Build Coastguard Worker struct formatter<Tuple, Char, 296*5c90c05cSAndroid Build Coastguard Worker enable_if_t<fmt::is_tuple_like<Tuple>::value && 297*5c90c05cSAndroid Build Coastguard Worker fmt::is_tuple_formattable<Tuple, Char>::value>> { 298*5c90c05cSAndroid Build Coastguard Worker private: 299*5c90c05cSAndroid Build Coastguard Worker decltype(detail::tuple::get_formatters<Tuple, Char>( 300*5c90c05cSAndroid Build Coastguard Worker detail::tuple_index_sequence<Tuple>())) formatters_; 301*5c90c05cSAndroid Build Coastguard Worker 302*5c90c05cSAndroid Build Coastguard Worker basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; 303*5c90c05cSAndroid Build Coastguard Worker basic_string_view<Char> opening_bracket_ = 304*5c90c05cSAndroid Build Coastguard Worker detail::string_literal<Char, '('>{}; 305*5c90c05cSAndroid Build Coastguard Worker basic_string_view<Char> closing_bracket_ = 306*5c90c05cSAndroid Build Coastguard Worker detail::string_literal<Char, ')'>{}; 307*5c90c05cSAndroid Build Coastguard Worker 308*5c90c05cSAndroid Build Coastguard Worker public: 309*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR formatter() {} 310*5c90c05cSAndroid Build Coastguard Worker 311*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { 312*5c90c05cSAndroid Build Coastguard Worker separator_ = sep; 313*5c90c05cSAndroid Build Coastguard Worker } 314*5c90c05cSAndroid Build Coastguard Worker 315*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, 316*5c90c05cSAndroid Build Coastguard Worker basic_string_view<Char> close) { 317*5c90c05cSAndroid Build Coastguard Worker opening_bracket_ = open; 318*5c90c05cSAndroid Build Coastguard Worker closing_bracket_ = close; 319*5c90c05cSAndroid Build Coastguard Worker } 320*5c90c05cSAndroid Build Coastguard Worker 321*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { 322*5c90c05cSAndroid Build Coastguard Worker auto it = ctx.begin(); 323*5c90c05cSAndroid Build Coastguard Worker auto end = ctx.end(); 324*5c90c05cSAndroid Build Coastguard Worker if (it != end && detail::to_ascii(*it) == 'n') { 325*5c90c05cSAndroid Build Coastguard Worker ++it; 326*5c90c05cSAndroid Build Coastguard Worker set_brackets({}, {}); 327*5c90c05cSAndroid Build Coastguard Worker set_separator({}); 328*5c90c05cSAndroid Build Coastguard Worker } 329*5c90c05cSAndroid Build Coastguard Worker if (it != end && *it != '}') report_error("invalid format specifier"); 330*5c90c05cSAndroid Build Coastguard Worker ctx.advance_to(it); 331*5c90c05cSAndroid Build Coastguard Worker detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx}); 332*5c90c05cSAndroid Build Coastguard Worker return it; 333*5c90c05cSAndroid Build Coastguard Worker } 334*5c90c05cSAndroid Build Coastguard Worker 335*5c90c05cSAndroid Build Coastguard Worker template <typename FormatContext> 336*5c90c05cSAndroid Build Coastguard Worker auto format(const Tuple& value, FormatContext& ctx) const 337*5c90c05cSAndroid Build Coastguard Worker -> decltype(ctx.out()) { 338*5c90c05cSAndroid Build Coastguard Worker ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out())); 339*5c90c05cSAndroid Build Coastguard Worker detail::for_each2( 340*5c90c05cSAndroid Build Coastguard Worker formatters_, value, 341*5c90c05cSAndroid Build Coastguard Worker detail::format_tuple_element<FormatContext>{0, ctx, separator_}); 342*5c90c05cSAndroid Build Coastguard Worker return detail::copy<Char>(closing_bracket_, ctx.out()); 343*5c90c05cSAndroid Build Coastguard Worker } 344*5c90c05cSAndroid Build Coastguard Worker }; 345*5c90c05cSAndroid Build Coastguard Worker 346*5c90c05cSAndroid Build Coastguard Worker template <typename T, typename Char> struct is_range { 347*5c90c05cSAndroid Build Coastguard Worker static constexpr const bool value = 348*5c90c05cSAndroid Build Coastguard Worker detail::is_range_<T>::value && !detail::has_to_string_view<T>::value; 349*5c90c05cSAndroid Build Coastguard Worker }; 350*5c90c05cSAndroid Build Coastguard Worker 351*5c90c05cSAndroid Build Coastguard Worker namespace detail { 352*5c90c05cSAndroid Build Coastguard Worker 353*5c90c05cSAndroid Build Coastguard Worker template <typename Char, typename Element> 354*5c90c05cSAndroid Build Coastguard Worker using range_formatter_type = formatter<remove_cvref_t<Element>, Char>; 355*5c90c05cSAndroid Build Coastguard Worker 356*5c90c05cSAndroid Build Coastguard Worker template <typename R> 357*5c90c05cSAndroid Build Coastguard Worker using maybe_const_range = 358*5c90c05cSAndroid Build Coastguard Worker conditional_t<has_const_begin_end<R>::value, const R, R>; 359*5c90c05cSAndroid Build Coastguard Worker 360*5c90c05cSAndroid Build Coastguard Worker // Workaround a bug in MSVC 2015 and earlier. 361*5c90c05cSAndroid Build Coastguard Worker #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 362*5c90c05cSAndroid Build Coastguard Worker template <typename R, typename Char> 363*5c90c05cSAndroid Build Coastguard Worker struct is_formattable_delayed 364*5c90c05cSAndroid Build Coastguard Worker : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {}; 365*5c90c05cSAndroid Build Coastguard Worker #endif 366*5c90c05cSAndroid Build Coastguard Worker } // namespace detail 367*5c90c05cSAndroid Build Coastguard Worker 368*5c90c05cSAndroid Build Coastguard Worker template <typename...> struct conjunction : std::true_type {}; 369*5c90c05cSAndroid Build Coastguard Worker template <typename P> struct conjunction<P> : P {}; 370*5c90c05cSAndroid Build Coastguard Worker template <typename P1, typename... Pn> 371*5c90c05cSAndroid Build Coastguard Worker struct conjunction<P1, Pn...> 372*5c90c05cSAndroid Build Coastguard Worker : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {}; 373*5c90c05cSAndroid Build Coastguard Worker 374*5c90c05cSAndroid Build Coastguard Worker template <typename T, typename Char, typename Enable = void> 375*5c90c05cSAndroid Build Coastguard Worker struct range_formatter; 376*5c90c05cSAndroid Build Coastguard Worker 377*5c90c05cSAndroid Build Coastguard Worker template <typename T, typename Char> 378*5c90c05cSAndroid Build Coastguard Worker struct range_formatter< 379*5c90c05cSAndroid Build Coastguard Worker T, Char, 380*5c90c05cSAndroid Build Coastguard Worker enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>, 381*5c90c05cSAndroid Build Coastguard Worker is_formattable<T, Char>>::value>> { 382*5c90c05cSAndroid Build Coastguard Worker private: 383*5c90c05cSAndroid Build Coastguard Worker detail::range_formatter_type<Char, T> underlying_; 384*5c90c05cSAndroid Build Coastguard Worker basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; 385*5c90c05cSAndroid Build Coastguard Worker basic_string_view<Char> opening_bracket_ = 386*5c90c05cSAndroid Build Coastguard Worker detail::string_literal<Char, '['>{}; 387*5c90c05cSAndroid Build Coastguard Worker basic_string_view<Char> closing_bracket_ = 388*5c90c05cSAndroid Build Coastguard Worker detail::string_literal<Char, ']'>{}; 389*5c90c05cSAndroid Build Coastguard Worker bool is_debug = false; 390*5c90c05cSAndroid Build Coastguard Worker 391*5c90c05cSAndroid Build Coastguard Worker template <typename Output, typename It, typename Sentinel, typename U = T, 392*5c90c05cSAndroid Build Coastguard Worker FMT_ENABLE_IF(std::is_same<U, Char>::value)> 393*5c90c05cSAndroid Build Coastguard Worker auto write_debug_string(Output& out, It it, Sentinel end) const -> Output { 394*5c90c05cSAndroid Build Coastguard Worker auto buf = basic_memory_buffer<Char>(); 395*5c90c05cSAndroid Build Coastguard Worker for (; it != end; ++it) buf.push_back(*it); 396*5c90c05cSAndroid Build Coastguard Worker auto specs = format_specs(); 397*5c90c05cSAndroid Build Coastguard Worker specs.set_type(presentation_type::debug); 398*5c90c05cSAndroid Build Coastguard Worker return detail::write<Char>( 399*5c90c05cSAndroid Build Coastguard Worker out, basic_string_view<Char>(buf.data(), buf.size()), specs); 400*5c90c05cSAndroid Build Coastguard Worker } 401*5c90c05cSAndroid Build Coastguard Worker 402*5c90c05cSAndroid Build Coastguard Worker template <typename Output, typename It, typename Sentinel, typename U = T, 403*5c90c05cSAndroid Build Coastguard Worker FMT_ENABLE_IF(!std::is_same<U, Char>::value)> 404*5c90c05cSAndroid Build Coastguard Worker auto write_debug_string(Output& out, It, Sentinel) const -> Output { 405*5c90c05cSAndroid Build Coastguard Worker return out; 406*5c90c05cSAndroid Build Coastguard Worker } 407*5c90c05cSAndroid Build Coastguard Worker 408*5c90c05cSAndroid Build Coastguard Worker public: 409*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR range_formatter() {} 410*5c90c05cSAndroid Build Coastguard Worker 411*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& { 412*5c90c05cSAndroid Build Coastguard Worker return underlying_; 413*5c90c05cSAndroid Build Coastguard Worker } 414*5c90c05cSAndroid Build Coastguard Worker 415*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { 416*5c90c05cSAndroid Build Coastguard Worker separator_ = sep; 417*5c90c05cSAndroid Build Coastguard Worker } 418*5c90c05cSAndroid Build Coastguard Worker 419*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, 420*5c90c05cSAndroid Build Coastguard Worker basic_string_view<Char> close) { 421*5c90c05cSAndroid Build Coastguard Worker opening_bracket_ = open; 422*5c90c05cSAndroid Build Coastguard Worker closing_bracket_ = close; 423*5c90c05cSAndroid Build Coastguard Worker } 424*5c90c05cSAndroid Build Coastguard Worker 425*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { 426*5c90c05cSAndroid Build Coastguard Worker auto it = ctx.begin(); 427*5c90c05cSAndroid Build Coastguard Worker auto end = ctx.end(); 428*5c90c05cSAndroid Build Coastguard Worker detail::maybe_set_debug_format(underlying_, true); 429*5c90c05cSAndroid Build Coastguard Worker if (it == end) return underlying_.parse(ctx); 430*5c90c05cSAndroid Build Coastguard Worker 431*5c90c05cSAndroid Build Coastguard Worker switch (detail::to_ascii(*it)) { 432*5c90c05cSAndroid Build Coastguard Worker case 'n': 433*5c90c05cSAndroid Build Coastguard Worker set_brackets({}, {}); 434*5c90c05cSAndroid Build Coastguard Worker ++it; 435*5c90c05cSAndroid Build Coastguard Worker break; 436*5c90c05cSAndroid Build Coastguard Worker case '?': 437*5c90c05cSAndroid Build Coastguard Worker is_debug = true; 438*5c90c05cSAndroid Build Coastguard Worker set_brackets({}, {}); 439*5c90c05cSAndroid Build Coastguard Worker ++it; 440*5c90c05cSAndroid Build Coastguard Worker if (it == end || *it != 's') report_error("invalid format specifier"); 441*5c90c05cSAndroid Build Coastguard Worker FMT_FALLTHROUGH; 442*5c90c05cSAndroid Build Coastguard Worker case 's': 443*5c90c05cSAndroid Build Coastguard Worker if (!std::is_same<T, Char>::value) 444*5c90c05cSAndroid Build Coastguard Worker report_error("invalid format specifier"); 445*5c90c05cSAndroid Build Coastguard Worker if (!is_debug) { 446*5c90c05cSAndroid Build Coastguard Worker set_brackets(detail::string_literal<Char, '"'>{}, 447*5c90c05cSAndroid Build Coastguard Worker detail::string_literal<Char, '"'>{}); 448*5c90c05cSAndroid Build Coastguard Worker set_separator({}); 449*5c90c05cSAndroid Build Coastguard Worker detail::maybe_set_debug_format(underlying_, false); 450*5c90c05cSAndroid Build Coastguard Worker } 451*5c90c05cSAndroid Build Coastguard Worker ++it; 452*5c90c05cSAndroid Build Coastguard Worker return it; 453*5c90c05cSAndroid Build Coastguard Worker } 454*5c90c05cSAndroid Build Coastguard Worker 455*5c90c05cSAndroid Build Coastguard Worker if (it != end && *it != '}') { 456*5c90c05cSAndroid Build Coastguard Worker if (*it != ':') report_error("invalid format specifier"); 457*5c90c05cSAndroid Build Coastguard Worker detail::maybe_set_debug_format(underlying_, false); 458*5c90c05cSAndroid Build Coastguard Worker ++it; 459*5c90c05cSAndroid Build Coastguard Worker } 460*5c90c05cSAndroid Build Coastguard Worker 461*5c90c05cSAndroid Build Coastguard Worker ctx.advance_to(it); 462*5c90c05cSAndroid Build Coastguard Worker return underlying_.parse(ctx); 463*5c90c05cSAndroid Build Coastguard Worker } 464*5c90c05cSAndroid Build Coastguard Worker 465*5c90c05cSAndroid Build Coastguard Worker template <typename R, typename FormatContext> 466*5c90c05cSAndroid Build Coastguard Worker auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { 467*5c90c05cSAndroid Build Coastguard Worker auto out = ctx.out(); 468*5c90c05cSAndroid Build Coastguard Worker auto it = detail::range_begin(range); 469*5c90c05cSAndroid Build Coastguard Worker auto end = detail::range_end(range); 470*5c90c05cSAndroid Build Coastguard Worker if (is_debug) return write_debug_string(out, std::move(it), end); 471*5c90c05cSAndroid Build Coastguard Worker 472*5c90c05cSAndroid Build Coastguard Worker out = detail::copy<Char>(opening_bracket_, out); 473*5c90c05cSAndroid Build Coastguard Worker int i = 0; 474*5c90c05cSAndroid Build Coastguard Worker for (; it != end; ++it) { 475*5c90c05cSAndroid Build Coastguard Worker if (i > 0) out = detail::copy<Char>(separator_, out); 476*5c90c05cSAndroid Build Coastguard Worker ctx.advance_to(out); 477*5c90c05cSAndroid Build Coastguard Worker auto&& item = *it; // Need an lvalue 478*5c90c05cSAndroid Build Coastguard Worker out = underlying_.format(item, ctx); 479*5c90c05cSAndroid Build Coastguard Worker ++i; 480*5c90c05cSAndroid Build Coastguard Worker } 481*5c90c05cSAndroid Build Coastguard Worker out = detail::copy<Char>(closing_bracket_, out); 482*5c90c05cSAndroid Build Coastguard Worker return out; 483*5c90c05cSAndroid Build Coastguard Worker } 484*5c90c05cSAndroid Build Coastguard Worker }; 485*5c90c05cSAndroid Build Coastguard Worker 486*5c90c05cSAndroid Build Coastguard Worker FMT_EXPORT 487*5c90c05cSAndroid Build Coastguard Worker template <typename T, typename Char, typename Enable = void> 488*5c90c05cSAndroid Build Coastguard Worker struct range_format_kind 489*5c90c05cSAndroid Build Coastguard Worker : conditional_t< 490*5c90c05cSAndroid Build Coastguard Worker is_range<T, Char>::value, detail::range_format_kind_<T>, 491*5c90c05cSAndroid Build Coastguard Worker std::integral_constant<range_format, range_format::disabled>> {}; 492*5c90c05cSAndroid Build Coastguard Worker 493*5c90c05cSAndroid Build Coastguard Worker template <typename R, typename Char> 494*5c90c05cSAndroid Build Coastguard Worker struct formatter< 495*5c90c05cSAndroid Build Coastguard Worker R, Char, 496*5c90c05cSAndroid Build Coastguard Worker enable_if_t<conjunction< 497*5c90c05cSAndroid Build Coastguard Worker bool_constant< 498*5c90c05cSAndroid Build Coastguard Worker range_format_kind<R, Char>::value != range_format::disabled && 499*5c90c05cSAndroid Build Coastguard Worker range_format_kind<R, Char>::value != range_format::map && 500*5c90c05cSAndroid Build Coastguard Worker range_format_kind<R, Char>::value != range_format::string && 501*5c90c05cSAndroid Build Coastguard Worker range_format_kind<R, Char>::value != range_format::debug_string> 502*5c90c05cSAndroid Build Coastguard Worker // Workaround a bug in MSVC 2015 and earlier. 503*5c90c05cSAndroid Build Coastguard Worker #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 504*5c90c05cSAndroid Build Coastguard Worker , 505*5c90c05cSAndroid Build Coastguard Worker detail::is_formattable_delayed<R, Char> 506*5c90c05cSAndroid Build Coastguard Worker #endif 507*5c90c05cSAndroid Build Coastguard Worker >::value>> { 508*5c90c05cSAndroid Build Coastguard Worker private: 509*5c90c05cSAndroid Build Coastguard Worker using range_type = detail::maybe_const_range<R>; 510*5c90c05cSAndroid Build Coastguard Worker range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_; 511*5c90c05cSAndroid Build Coastguard Worker 512*5c90c05cSAndroid Build Coastguard Worker public: 513*5c90c05cSAndroid Build Coastguard Worker using nonlocking = void; 514*5c90c05cSAndroid Build Coastguard Worker 515*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR formatter() { 516*5c90c05cSAndroid Build Coastguard Worker if (detail::const_check(range_format_kind<R, Char>::value != 517*5c90c05cSAndroid Build Coastguard Worker range_format::set)) 518*5c90c05cSAndroid Build Coastguard Worker return; 519*5c90c05cSAndroid Build Coastguard Worker range_formatter_.set_brackets(detail::string_literal<Char, '{'>{}, 520*5c90c05cSAndroid Build Coastguard Worker detail::string_literal<Char, '}'>{}); 521*5c90c05cSAndroid Build Coastguard Worker } 522*5c90c05cSAndroid Build Coastguard Worker 523*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { 524*5c90c05cSAndroid Build Coastguard Worker return range_formatter_.parse(ctx); 525*5c90c05cSAndroid Build Coastguard Worker } 526*5c90c05cSAndroid Build Coastguard Worker 527*5c90c05cSAndroid Build Coastguard Worker template <typename FormatContext> 528*5c90c05cSAndroid Build Coastguard Worker auto format(range_type& range, FormatContext& ctx) const 529*5c90c05cSAndroid Build Coastguard Worker -> decltype(ctx.out()) { 530*5c90c05cSAndroid Build Coastguard Worker return range_formatter_.format(range, ctx); 531*5c90c05cSAndroid Build Coastguard Worker } 532*5c90c05cSAndroid Build Coastguard Worker }; 533*5c90c05cSAndroid Build Coastguard Worker 534*5c90c05cSAndroid Build Coastguard Worker // A map formatter. 535*5c90c05cSAndroid Build Coastguard Worker template <typename R, typename Char> 536*5c90c05cSAndroid Build Coastguard Worker struct formatter< 537*5c90c05cSAndroid Build Coastguard Worker R, Char, 538*5c90c05cSAndroid Build Coastguard Worker enable_if_t<range_format_kind<R, Char>::value == range_format::map>> { 539*5c90c05cSAndroid Build Coastguard Worker private: 540*5c90c05cSAndroid Build Coastguard Worker using map_type = detail::maybe_const_range<R>; 541*5c90c05cSAndroid Build Coastguard Worker using element_type = detail::uncvref_type<map_type>; 542*5c90c05cSAndroid Build Coastguard Worker 543*5c90c05cSAndroid Build Coastguard Worker decltype(detail::tuple::get_formatters<element_type, Char>( 544*5c90c05cSAndroid Build Coastguard Worker detail::tuple_index_sequence<element_type>())) formatters_; 545*5c90c05cSAndroid Build Coastguard Worker bool no_delimiters_ = false; 546*5c90c05cSAndroid Build Coastguard Worker 547*5c90c05cSAndroid Build Coastguard Worker public: 548*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR formatter() {} 549*5c90c05cSAndroid Build Coastguard Worker 550*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { 551*5c90c05cSAndroid Build Coastguard Worker auto it = ctx.begin(); 552*5c90c05cSAndroid Build Coastguard Worker auto end = ctx.end(); 553*5c90c05cSAndroid Build Coastguard Worker if (it != end) { 554*5c90c05cSAndroid Build Coastguard Worker if (detail::to_ascii(*it) == 'n') { 555*5c90c05cSAndroid Build Coastguard Worker no_delimiters_ = true; 556*5c90c05cSAndroid Build Coastguard Worker ++it; 557*5c90c05cSAndroid Build Coastguard Worker } 558*5c90c05cSAndroid Build Coastguard Worker if (it != end && *it != '}') { 559*5c90c05cSAndroid Build Coastguard Worker if (*it != ':') report_error("invalid format specifier"); 560*5c90c05cSAndroid Build Coastguard Worker ++it; 561*5c90c05cSAndroid Build Coastguard Worker } 562*5c90c05cSAndroid Build Coastguard Worker ctx.advance_to(it); 563*5c90c05cSAndroid Build Coastguard Worker } 564*5c90c05cSAndroid Build Coastguard Worker detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx}); 565*5c90c05cSAndroid Build Coastguard Worker return it; 566*5c90c05cSAndroid Build Coastguard Worker } 567*5c90c05cSAndroid Build Coastguard Worker 568*5c90c05cSAndroid Build Coastguard Worker template <typename FormatContext> 569*5c90c05cSAndroid Build Coastguard Worker auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) { 570*5c90c05cSAndroid Build Coastguard Worker auto out = ctx.out(); 571*5c90c05cSAndroid Build Coastguard Worker basic_string_view<Char> open = detail::string_literal<Char, '{'>{}; 572*5c90c05cSAndroid Build Coastguard Worker if (!no_delimiters_) out = detail::copy<Char>(open, out); 573*5c90c05cSAndroid Build Coastguard Worker int i = 0; 574*5c90c05cSAndroid Build Coastguard Worker basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{}; 575*5c90c05cSAndroid Build Coastguard Worker for (auto&& value : map) { 576*5c90c05cSAndroid Build Coastguard Worker if (i > 0) out = detail::copy<Char>(sep, out); 577*5c90c05cSAndroid Build Coastguard Worker ctx.advance_to(out); 578*5c90c05cSAndroid Build Coastguard Worker detail::for_each2(formatters_, value, 579*5c90c05cSAndroid Build Coastguard Worker detail::format_tuple_element<FormatContext>{ 580*5c90c05cSAndroid Build Coastguard Worker 0, ctx, detail::string_literal<Char, ':', ' '>{}}); 581*5c90c05cSAndroid Build Coastguard Worker ++i; 582*5c90c05cSAndroid Build Coastguard Worker } 583*5c90c05cSAndroid Build Coastguard Worker basic_string_view<Char> close = detail::string_literal<Char, '}'>{}; 584*5c90c05cSAndroid Build Coastguard Worker if (!no_delimiters_) out = detail::copy<Char>(close, out); 585*5c90c05cSAndroid Build Coastguard Worker return out; 586*5c90c05cSAndroid Build Coastguard Worker } 587*5c90c05cSAndroid Build Coastguard Worker }; 588*5c90c05cSAndroid Build Coastguard Worker 589*5c90c05cSAndroid Build Coastguard Worker // A (debug_)string formatter. 590*5c90c05cSAndroid Build Coastguard Worker template <typename R, typename Char> 591*5c90c05cSAndroid Build Coastguard Worker struct formatter< 592*5c90c05cSAndroid Build Coastguard Worker R, Char, 593*5c90c05cSAndroid Build Coastguard Worker enable_if_t<range_format_kind<R, Char>::value == range_format::string || 594*5c90c05cSAndroid Build Coastguard Worker range_format_kind<R, Char>::value == 595*5c90c05cSAndroid Build Coastguard Worker range_format::debug_string>> { 596*5c90c05cSAndroid Build Coastguard Worker private: 597*5c90c05cSAndroid Build Coastguard Worker using range_type = detail::maybe_const_range<R>; 598*5c90c05cSAndroid Build Coastguard Worker using string_type = 599*5c90c05cSAndroid Build Coastguard Worker conditional_t<std::is_constructible< 600*5c90c05cSAndroid Build Coastguard Worker detail::std_string_view<Char>, 601*5c90c05cSAndroid Build Coastguard Worker decltype(detail::range_begin(std::declval<R>())), 602*5c90c05cSAndroid Build Coastguard Worker decltype(detail::range_end(std::declval<R>()))>::value, 603*5c90c05cSAndroid Build Coastguard Worker detail::std_string_view<Char>, std::basic_string<Char>>; 604*5c90c05cSAndroid Build Coastguard Worker 605*5c90c05cSAndroid Build Coastguard Worker formatter<string_type, Char> underlying_; 606*5c90c05cSAndroid Build Coastguard Worker 607*5c90c05cSAndroid Build Coastguard Worker public: 608*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { 609*5c90c05cSAndroid Build Coastguard Worker return underlying_.parse(ctx); 610*5c90c05cSAndroid Build Coastguard Worker } 611*5c90c05cSAndroid Build Coastguard Worker 612*5c90c05cSAndroid Build Coastguard Worker template <typename FormatContext> 613*5c90c05cSAndroid Build Coastguard Worker auto format(range_type& range, FormatContext& ctx) const 614*5c90c05cSAndroid Build Coastguard Worker -> decltype(ctx.out()) { 615*5c90c05cSAndroid Build Coastguard Worker auto out = ctx.out(); 616*5c90c05cSAndroid Build Coastguard Worker if (detail::const_check(range_format_kind<R, Char>::value == 617*5c90c05cSAndroid Build Coastguard Worker range_format::debug_string)) 618*5c90c05cSAndroid Build Coastguard Worker *out++ = '"'; 619*5c90c05cSAndroid Build Coastguard Worker out = underlying_.format( 620*5c90c05cSAndroid Build Coastguard Worker string_type{detail::range_begin(range), detail::range_end(range)}, ctx); 621*5c90c05cSAndroid Build Coastguard Worker if (detail::const_check(range_format_kind<R, Char>::value == 622*5c90c05cSAndroid Build Coastguard Worker range_format::debug_string)) 623*5c90c05cSAndroid Build Coastguard Worker *out++ = '"'; 624*5c90c05cSAndroid Build Coastguard Worker return out; 625*5c90c05cSAndroid Build Coastguard Worker } 626*5c90c05cSAndroid Build Coastguard Worker }; 627*5c90c05cSAndroid Build Coastguard Worker 628*5c90c05cSAndroid Build Coastguard Worker template <typename It, typename Sentinel, typename Char = char> 629*5c90c05cSAndroid Build Coastguard Worker struct join_view : detail::view { 630*5c90c05cSAndroid Build Coastguard Worker It begin; 631*5c90c05cSAndroid Build Coastguard Worker Sentinel end; 632*5c90c05cSAndroid Build Coastguard Worker basic_string_view<Char> sep; 633*5c90c05cSAndroid Build Coastguard Worker 634*5c90c05cSAndroid Build Coastguard Worker join_view(It b, Sentinel e, basic_string_view<Char> s) 635*5c90c05cSAndroid Build Coastguard Worker : begin(std::move(b)), end(e), sep(s) {} 636*5c90c05cSAndroid Build Coastguard Worker }; 637*5c90c05cSAndroid Build Coastguard Worker 638*5c90c05cSAndroid Build Coastguard Worker template <typename It, typename Sentinel, typename Char> 639*5c90c05cSAndroid Build Coastguard Worker struct formatter<join_view<It, Sentinel, Char>, Char> { 640*5c90c05cSAndroid Build Coastguard Worker private: 641*5c90c05cSAndroid Build Coastguard Worker using value_type = 642*5c90c05cSAndroid Build Coastguard Worker #ifdef __cpp_lib_ranges 643*5c90c05cSAndroid Build Coastguard Worker std::iter_value_t<It>; 644*5c90c05cSAndroid Build Coastguard Worker #else 645*5c90c05cSAndroid Build Coastguard Worker typename std::iterator_traits<It>::value_type; 646*5c90c05cSAndroid Build Coastguard Worker #endif 647*5c90c05cSAndroid Build Coastguard Worker formatter<remove_cvref_t<value_type>, Char> value_formatter_; 648*5c90c05cSAndroid Build Coastguard Worker 649*5c90c05cSAndroid Build Coastguard Worker using view_ref = conditional_t<std::is_copy_constructible<It>::value, 650*5c90c05cSAndroid Build Coastguard Worker const join_view<It, Sentinel, Char>&, 651*5c90c05cSAndroid Build Coastguard Worker join_view<It, Sentinel, Char>&&>; 652*5c90c05cSAndroid Build Coastguard Worker 653*5c90c05cSAndroid Build Coastguard Worker public: 654*5c90c05cSAndroid Build Coastguard Worker using nonlocking = void; 655*5c90c05cSAndroid Build Coastguard Worker 656*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { 657*5c90c05cSAndroid Build Coastguard Worker return value_formatter_.parse(ctx); 658*5c90c05cSAndroid Build Coastguard Worker } 659*5c90c05cSAndroid Build Coastguard Worker 660*5c90c05cSAndroid Build Coastguard Worker template <typename FormatContext> 661*5c90c05cSAndroid Build Coastguard Worker auto format(view_ref& value, FormatContext& ctx) const 662*5c90c05cSAndroid Build Coastguard Worker -> decltype(ctx.out()) { 663*5c90c05cSAndroid Build Coastguard Worker auto it = std::forward<view_ref>(value).begin; 664*5c90c05cSAndroid Build Coastguard Worker auto out = ctx.out(); 665*5c90c05cSAndroid Build Coastguard Worker if (it == value.end) return out; 666*5c90c05cSAndroid Build Coastguard Worker out = value_formatter_.format(*it, ctx); 667*5c90c05cSAndroid Build Coastguard Worker ++it; 668*5c90c05cSAndroid Build Coastguard Worker while (it != value.end) { 669*5c90c05cSAndroid Build Coastguard Worker out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out); 670*5c90c05cSAndroid Build Coastguard Worker ctx.advance_to(out); 671*5c90c05cSAndroid Build Coastguard Worker out = value_formatter_.format(*it, ctx); 672*5c90c05cSAndroid Build Coastguard Worker ++it; 673*5c90c05cSAndroid Build Coastguard Worker } 674*5c90c05cSAndroid Build Coastguard Worker return out; 675*5c90c05cSAndroid Build Coastguard Worker } 676*5c90c05cSAndroid Build Coastguard Worker }; 677*5c90c05cSAndroid Build Coastguard Worker 678*5c90c05cSAndroid Build Coastguard Worker /// Returns a view that formats the iterator range `[begin, end)` with elements 679*5c90c05cSAndroid Build Coastguard Worker /// separated by `sep`. 680*5c90c05cSAndroid Build Coastguard Worker template <typename It, typename Sentinel> 681*5c90c05cSAndroid Build Coastguard Worker auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> { 682*5c90c05cSAndroid Build Coastguard Worker return {std::move(begin), end, sep}; 683*5c90c05cSAndroid Build Coastguard Worker } 684*5c90c05cSAndroid Build Coastguard Worker 685*5c90c05cSAndroid Build Coastguard Worker /** 686*5c90c05cSAndroid Build Coastguard Worker * Returns a view that formats `range` with elements separated by `sep`. 687*5c90c05cSAndroid Build Coastguard Worker * 688*5c90c05cSAndroid Build Coastguard Worker * **Example**: 689*5c90c05cSAndroid Build Coastguard Worker * 690*5c90c05cSAndroid Build Coastguard Worker * auto v = std::vector<int>{1, 2, 3}; 691*5c90c05cSAndroid Build Coastguard Worker * fmt::print("{}", fmt::join(v, ", ")); 692*5c90c05cSAndroid Build Coastguard Worker * // Output: 1, 2, 3 693*5c90c05cSAndroid Build Coastguard Worker * 694*5c90c05cSAndroid Build Coastguard Worker * `fmt::join` applies passed format specifiers to the range elements: 695*5c90c05cSAndroid Build Coastguard Worker * 696*5c90c05cSAndroid Build Coastguard Worker * fmt::print("{:02}", fmt::join(v, ", ")); 697*5c90c05cSAndroid Build Coastguard Worker * // Output: 01, 02, 03 698*5c90c05cSAndroid Build Coastguard Worker */ 699*5c90c05cSAndroid Build Coastguard Worker template <typename Range> 700*5c90c05cSAndroid Build Coastguard Worker auto join(Range&& r, string_view sep) 701*5c90c05cSAndroid Build Coastguard Worker -> join_view<decltype(detail::range_begin(r)), 702*5c90c05cSAndroid Build Coastguard Worker decltype(detail::range_end(r))> { 703*5c90c05cSAndroid Build Coastguard Worker return {detail::range_begin(r), detail::range_end(r), sep}; 704*5c90c05cSAndroid Build Coastguard Worker } 705*5c90c05cSAndroid Build Coastguard Worker 706*5c90c05cSAndroid Build Coastguard Worker template <typename Char, typename... T> struct tuple_join_view : detail::view { 707*5c90c05cSAndroid Build Coastguard Worker const std::tuple<T...>& tuple; 708*5c90c05cSAndroid Build Coastguard Worker basic_string_view<Char> sep; 709*5c90c05cSAndroid Build Coastguard Worker 710*5c90c05cSAndroid Build Coastguard Worker tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s) 711*5c90c05cSAndroid Build Coastguard Worker : tuple(t), sep{s} {} 712*5c90c05cSAndroid Build Coastguard Worker }; 713*5c90c05cSAndroid Build Coastguard Worker 714*5c90c05cSAndroid Build Coastguard Worker // Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers 715*5c90c05cSAndroid Build Coastguard Worker // support in tuple_join. It is disabled by default because of issues with 716*5c90c05cSAndroid Build Coastguard Worker // the dynamic width and precision. 717*5c90c05cSAndroid Build Coastguard Worker #ifndef FMT_TUPLE_JOIN_SPECIFIERS 718*5c90c05cSAndroid Build Coastguard Worker # define FMT_TUPLE_JOIN_SPECIFIERS 0 719*5c90c05cSAndroid Build Coastguard Worker #endif 720*5c90c05cSAndroid Build Coastguard Worker 721*5c90c05cSAndroid Build Coastguard Worker template <typename Char, typename... T> 722*5c90c05cSAndroid Build Coastguard Worker struct formatter<tuple_join_view<Char, T...>, Char> { 723*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { 724*5c90c05cSAndroid Build Coastguard Worker return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>()); 725*5c90c05cSAndroid Build Coastguard Worker } 726*5c90c05cSAndroid Build Coastguard Worker 727*5c90c05cSAndroid Build Coastguard Worker template <typename FormatContext> 728*5c90c05cSAndroid Build Coastguard Worker auto format(const tuple_join_view<Char, T...>& value, 729*5c90c05cSAndroid Build Coastguard Worker FormatContext& ctx) const -> typename FormatContext::iterator { 730*5c90c05cSAndroid Build Coastguard Worker return do_format(value, ctx, 731*5c90c05cSAndroid Build Coastguard Worker std::integral_constant<size_t, sizeof...(T)>()); 732*5c90c05cSAndroid Build Coastguard Worker } 733*5c90c05cSAndroid Build Coastguard Worker 734*5c90c05cSAndroid Build Coastguard Worker private: 735*5c90c05cSAndroid Build Coastguard Worker std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_; 736*5c90c05cSAndroid Build Coastguard Worker 737*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx, 738*5c90c05cSAndroid Build Coastguard Worker std::integral_constant<size_t, 0>) 739*5c90c05cSAndroid Build Coastguard Worker -> const Char* { 740*5c90c05cSAndroid Build Coastguard Worker return ctx.begin(); 741*5c90c05cSAndroid Build Coastguard Worker } 742*5c90c05cSAndroid Build Coastguard Worker 743*5c90c05cSAndroid Build Coastguard Worker template <size_t N> 744*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx, 745*5c90c05cSAndroid Build Coastguard Worker std::integral_constant<size_t, N>) 746*5c90c05cSAndroid Build Coastguard Worker -> const Char* { 747*5c90c05cSAndroid Build Coastguard Worker auto end = ctx.begin(); 748*5c90c05cSAndroid Build Coastguard Worker #if FMT_TUPLE_JOIN_SPECIFIERS 749*5c90c05cSAndroid Build Coastguard Worker end = std::get<sizeof...(T) - N>(formatters_).parse(ctx); 750*5c90c05cSAndroid Build Coastguard Worker if (N > 1) { 751*5c90c05cSAndroid Build Coastguard Worker auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>()); 752*5c90c05cSAndroid Build Coastguard Worker if (end != end1) 753*5c90c05cSAndroid Build Coastguard Worker report_error("incompatible format specs for tuple elements"); 754*5c90c05cSAndroid Build Coastguard Worker } 755*5c90c05cSAndroid Build Coastguard Worker #endif 756*5c90c05cSAndroid Build Coastguard Worker return end; 757*5c90c05cSAndroid Build Coastguard Worker } 758*5c90c05cSAndroid Build Coastguard Worker 759*5c90c05cSAndroid Build Coastguard Worker template <typename FormatContext> 760*5c90c05cSAndroid Build Coastguard Worker auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx, 761*5c90c05cSAndroid Build Coastguard Worker std::integral_constant<size_t, 0>) const -> 762*5c90c05cSAndroid Build Coastguard Worker typename FormatContext::iterator { 763*5c90c05cSAndroid Build Coastguard Worker return ctx.out(); 764*5c90c05cSAndroid Build Coastguard Worker } 765*5c90c05cSAndroid Build Coastguard Worker 766*5c90c05cSAndroid Build Coastguard Worker template <typename FormatContext, size_t N> 767*5c90c05cSAndroid Build Coastguard Worker auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx, 768*5c90c05cSAndroid Build Coastguard Worker std::integral_constant<size_t, N>) const -> 769*5c90c05cSAndroid Build Coastguard Worker typename FormatContext::iterator { 770*5c90c05cSAndroid Build Coastguard Worker auto out = std::get<sizeof...(T) - N>(formatters_) 771*5c90c05cSAndroid Build Coastguard Worker .format(std::get<sizeof...(T) - N>(value.tuple), ctx); 772*5c90c05cSAndroid Build Coastguard Worker if (N <= 1) return out; 773*5c90c05cSAndroid Build Coastguard Worker out = detail::copy<Char>(value.sep, out); 774*5c90c05cSAndroid Build Coastguard Worker ctx.advance_to(out); 775*5c90c05cSAndroid Build Coastguard Worker return do_format(value, ctx, std::integral_constant<size_t, N - 1>()); 776*5c90c05cSAndroid Build Coastguard Worker } 777*5c90c05cSAndroid Build Coastguard Worker }; 778*5c90c05cSAndroid Build Coastguard Worker 779*5c90c05cSAndroid Build Coastguard Worker namespace detail { 780*5c90c05cSAndroid Build Coastguard Worker // Check if T has an interface like a container adaptor (e.g. std::stack, 781*5c90c05cSAndroid Build Coastguard Worker // std::queue, std::priority_queue). 782*5c90c05cSAndroid Build Coastguard Worker template <typename T> class is_container_adaptor_like { 783*5c90c05cSAndroid Build Coastguard Worker template <typename U> static auto check(U* p) -> typename U::container_type; 784*5c90c05cSAndroid Build Coastguard Worker template <typename> static void check(...); 785*5c90c05cSAndroid Build Coastguard Worker 786*5c90c05cSAndroid Build Coastguard Worker public: 787*5c90c05cSAndroid Build Coastguard Worker static constexpr const bool value = 788*5c90c05cSAndroid Build Coastguard Worker !std::is_void<decltype(check<T>(nullptr))>::value; 789*5c90c05cSAndroid Build Coastguard Worker }; 790*5c90c05cSAndroid Build Coastguard Worker 791*5c90c05cSAndroid Build Coastguard Worker template <typename Container> struct all { 792*5c90c05cSAndroid Build Coastguard Worker const Container& c; 793*5c90c05cSAndroid Build Coastguard Worker auto begin() const -> typename Container::const_iterator { return c.begin(); } 794*5c90c05cSAndroid Build Coastguard Worker auto end() const -> typename Container::const_iterator { return c.end(); } 795*5c90c05cSAndroid Build Coastguard Worker }; 796*5c90c05cSAndroid Build Coastguard Worker } // namespace detail 797*5c90c05cSAndroid Build Coastguard Worker 798*5c90c05cSAndroid Build Coastguard Worker template <typename T, typename Char> 799*5c90c05cSAndroid Build Coastguard Worker struct formatter< 800*5c90c05cSAndroid Build Coastguard Worker T, Char, 801*5c90c05cSAndroid Build Coastguard Worker enable_if_t<conjunction<detail::is_container_adaptor_like<T>, 802*5c90c05cSAndroid Build Coastguard Worker bool_constant<range_format_kind<T, Char>::value == 803*5c90c05cSAndroid Build Coastguard Worker range_format::disabled>>::value>> 804*5c90c05cSAndroid Build Coastguard Worker : formatter<detail::all<typename T::container_type>, Char> { 805*5c90c05cSAndroid Build Coastguard Worker using all = detail::all<typename T::container_type>; 806*5c90c05cSAndroid Build Coastguard Worker template <typename FormatContext> 807*5c90c05cSAndroid Build Coastguard Worker auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) { 808*5c90c05cSAndroid Build Coastguard Worker struct getter : T { 809*5c90c05cSAndroid Build Coastguard Worker static auto get(const T& t) -> all { 810*5c90c05cSAndroid Build Coastguard Worker return {t.*(&getter::c)}; // Access c through the derived class. 811*5c90c05cSAndroid Build Coastguard Worker } 812*5c90c05cSAndroid Build Coastguard Worker }; 813*5c90c05cSAndroid Build Coastguard Worker return formatter<all>::format(getter::get(t), ctx); 814*5c90c05cSAndroid Build Coastguard Worker } 815*5c90c05cSAndroid Build Coastguard Worker }; 816*5c90c05cSAndroid Build Coastguard Worker 817*5c90c05cSAndroid Build Coastguard Worker FMT_BEGIN_EXPORT 818*5c90c05cSAndroid Build Coastguard Worker 819*5c90c05cSAndroid Build Coastguard Worker /** 820*5c90c05cSAndroid Build Coastguard Worker * Returns an object that formats `std::tuple` with elements separated by `sep`. 821*5c90c05cSAndroid Build Coastguard Worker * 822*5c90c05cSAndroid Build Coastguard Worker * **Example**: 823*5c90c05cSAndroid Build Coastguard Worker * 824*5c90c05cSAndroid Build Coastguard Worker * auto t = std::tuple<int, char>{1, 'a'}; 825*5c90c05cSAndroid Build Coastguard Worker * fmt::print("{}", fmt::join(t, ", ")); 826*5c90c05cSAndroid Build Coastguard Worker * // Output: 1, a 827*5c90c05cSAndroid Build Coastguard Worker */ 828*5c90c05cSAndroid Build Coastguard Worker template <typename... T> 829*5c90c05cSAndroid Build Coastguard Worker FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep) 830*5c90c05cSAndroid Build Coastguard Worker -> tuple_join_view<char, T...> { 831*5c90c05cSAndroid Build Coastguard Worker return {tuple, sep}; 832*5c90c05cSAndroid Build Coastguard Worker } 833*5c90c05cSAndroid Build Coastguard Worker 834*5c90c05cSAndroid Build Coastguard Worker /** 835*5c90c05cSAndroid Build Coastguard Worker * Returns an object that formats `std::initializer_list` with elements 836*5c90c05cSAndroid Build Coastguard Worker * separated by `sep`. 837*5c90c05cSAndroid Build Coastguard Worker * 838*5c90c05cSAndroid Build Coastguard Worker * **Example**: 839*5c90c05cSAndroid Build Coastguard Worker * 840*5c90c05cSAndroid Build Coastguard Worker * fmt::print("{}", fmt::join({1, 2, 3}, ", ")); 841*5c90c05cSAndroid Build Coastguard Worker * // Output: "1, 2, 3" 842*5c90c05cSAndroid Build Coastguard Worker */ 843*5c90c05cSAndroid Build Coastguard Worker template <typename T> 844*5c90c05cSAndroid Build Coastguard Worker auto join(std::initializer_list<T> list, string_view sep) 845*5c90c05cSAndroid Build Coastguard Worker -> join_view<const T*, const T*> { 846*5c90c05cSAndroid Build Coastguard Worker return join(std::begin(list), std::end(list), sep); 847*5c90c05cSAndroid Build Coastguard Worker } 848*5c90c05cSAndroid Build Coastguard Worker 849*5c90c05cSAndroid Build Coastguard Worker FMT_END_EXPORT 850*5c90c05cSAndroid Build Coastguard Worker FMT_END_NAMESPACE 851*5c90c05cSAndroid Build Coastguard Worker 852*5c90c05cSAndroid Build Coastguard Worker #endif // FMT_RANGES_H_ 853