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