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