xref: /aosp_15_r20/external/fmtlib/include/fmt/args.h (revision 5c90c05cd622c0a81b57953a4d343e0e489f2e08)
1 // Formatting library for C++ - dynamic argument lists
2 //
3 // Copyright (c) 2012 - present, Victor Zverovich
4 // All rights reserved.
5 //
6 // For the license information refer to format.h.
7 
8 #ifndef FMT_ARGS_H_
9 #define FMT_ARGS_H_
10 
11 #ifndef FMT_MODULE
12 #  include <functional>  // std::reference_wrapper
13 #  include <memory>      // std::unique_ptr
14 #  include <vector>
15 #endif
16 
17 #include "format.h"  // std_string_view
18 
19 FMT_BEGIN_NAMESPACE
20 namespace detail {
21 
22 template <typename T> struct is_reference_wrapper : std::false_type {};
23 template <typename T>
24 struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
25 
26 template <typename T> auto unwrap(const T& v) -> const T& { return v; }
27 template <typename T>
28 auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
29   return static_cast<const T&>(v);
30 }
31 
32 // node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC
33 // 2022 (v17.10.0).
34 //
35 // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
36 // templates it doesn't complain about inability to deduce single translation
37 // unit for placing vtable. So node is made a fake template.
38 template <typename = void> struct node {
39   virtual ~node() = default;
40   std::unique_ptr<node<>> next;
41 };
42 
43 class dynamic_arg_list {
44   template <typename T> struct typed_node : node<> {
45     T value;
46 
47     template <typename Arg>
48     FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
49 
50     template <typename Char>
51     FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
52         : value(arg.data(), arg.size()) {}
53   };
54 
55   std::unique_ptr<node<>> head_;
56 
57  public:
58   template <typename T, typename Arg> auto push(const Arg& arg) -> const T& {
59     auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
60     auto& value = new_node->value;
61     new_node->next = std::move(head_);
62     head_ = std::move(new_node);
63     return value;
64   }
65 };
66 }  // namespace detail
67 
68 /**
69  * A dynamic list of formatting arguments with storage.
70  *
71  * It can be implicitly converted into `fmt::basic_format_args` for passing
72  * into type-erased formatting functions such as `fmt::vformat`.
73  */
74 template <typename Context> class dynamic_format_arg_store {
75  private:
76   using char_type = typename Context::char_type;
77 
78   template <typename T> struct need_copy {
79     static constexpr detail::type mapped_type =
80         detail::mapped_type_constant<T, char_type>::value;
81 
82     enum {
83       value = !(detail::is_reference_wrapper<T>::value ||
84                 std::is_same<T, basic_string_view<char_type>>::value ||
85                 std::is_same<T, detail::std_string_view<char_type>>::value ||
86                 (mapped_type != detail::type::cstring_type &&
87                  mapped_type != detail::type::string_type &&
88                  mapped_type != detail::type::custom_type))
89     };
90   };
91 
92   template <typename T>
93   using stored_t = conditional_t<
94       std::is_convertible<T, std::basic_string<char_type>>::value &&
95           !detail::is_reference_wrapper<T>::value,
96       std::basic_string<char_type>, T>;
97 
98   // Storage of basic_format_arg must be contiguous.
99   std::vector<basic_format_arg<Context>> data_;
100   std::vector<detail::named_arg_info<char_type>> named_info_;
101 
102   // Storage of arguments not fitting into basic_format_arg must grow
103   // without relocation because items in data_ refer to it.
104   detail::dynamic_arg_list dynamic_args_;
105 
106   friend class basic_format_args<Context>;
107 
108   auto data() const -> const basic_format_arg<Context>* {
109     return named_info_.empty() ? data_.data() : data_.data() + 1;
110   }
111 
112   template <typename T> void emplace_arg(const T& arg) {
113     data_.emplace_back(arg);
114   }
115 
116   template <typename T>
117   void emplace_arg(const detail::named_arg<char_type, T>& arg) {
118     if (named_info_.empty())
119       data_.insert(data_.begin(), basic_format_arg<Context>(nullptr, 0));
120     data_.emplace_back(detail::unwrap(arg.value));
121     auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
122       data->pop_back();
123     };
124     std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
125         guard{&data_, pop_one};
126     named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
127     data_[0] = {named_info_.data(), named_info_.size()};
128     guard.release();
129   }
130 
131  public:
132   constexpr dynamic_format_arg_store() = default;
133 
134   operator basic_format_args<Context>() const {
135     return basic_format_args<Context>(data(), static_cast<int>(data_.size()),
136                                       !named_info_.empty());
137   }
138 
139   /**
140    * Adds an argument into the dynamic store for later passing to a formatting
141    * function.
142    *
143    * Note that custom types and string types (but not string views) are copied
144    * into the store dynamically allocating memory if necessary.
145    *
146    * **Example**:
147    *
148    *     fmt::dynamic_format_arg_store<fmt::format_context> store;
149    *     store.push_back(42);
150    *     store.push_back("abc");
151    *     store.push_back(1.5f);
152    *     std::string result = fmt::vformat("{} and {} and {}", store);
153    */
154   template <typename T> void push_back(const T& arg) {
155     if (detail::const_check(need_copy<T>::value))
156       emplace_arg(dynamic_args_.push<stored_t<T>>(arg));
157     else
158       emplace_arg(detail::unwrap(arg));
159   }
160 
161   /**
162    * Adds a reference to the argument into the dynamic store for later passing
163    * to a formatting function.
164    *
165    * **Example**:
166    *
167    *     fmt::dynamic_format_arg_store<fmt::format_context> store;
168    *     char band[] = "Rolling Stones";
169    *     store.push_back(std::cref(band));
170    *     band[9] = 'c'; // Changing str affects the output.
171    *     std::string result = fmt::vformat("{}", store);
172    *     // result == "Rolling Scones"
173    */
174   template <typename T> void push_back(std::reference_wrapper<T> arg) {
175     static_assert(
176         need_copy<T>::value,
177         "objects of built-in types and string views are always copied");
178     emplace_arg(arg.get());
179   }
180 
181   /**
182    * Adds named argument into the dynamic store for later passing to a
183    * formatting function. `std::reference_wrapper` is supported to avoid
184    * copying of the argument. The name is always copied into the store.
185    */
186   template <typename T>
187   void push_back(const detail::named_arg<char_type, T>& arg) {
188     const char_type* arg_name =
189         dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
190     if (detail::const_check(need_copy<T>::value)) {
191       emplace_arg(
192           fmt::arg(arg_name, dynamic_args_.push<stored_t<T>>(arg.value)));
193     } else {
194       emplace_arg(fmt::arg(arg_name, arg.value));
195     }
196   }
197 
198   /// Erase all elements from the store.
199   void clear() {
200     data_.clear();
201     named_info_.clear();
202     dynamic_args_ = {};
203   }
204 
205   /// Reserves space to store at least `new_cap` arguments including
206   /// `new_cap_named` named arguments.
207   void reserve(size_t new_cap, size_t new_cap_named) {
208     FMT_ASSERT(new_cap >= new_cap_named,
209                "set of arguments includes set of named arguments");
210     data_.reserve(new_cap);
211     named_info_.reserve(new_cap_named);
212   }
213 };
214 
215 FMT_END_NAMESPACE
216 
217 #endif  // FMT_ARGS_H_
218