]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * Copyright Andrey Semashev 2007 - 2015. | |
3 | * Distributed under the Boost Software License, Version 1.0. | |
4 | * (See accompanying file LICENSE_1_0.txt or copy at | |
5 | * http://www.boost.org/LICENSE_1_0.txt) | |
6 | */ | |
7 | /*! | |
8 | * \file format.hpp | |
9 | * \author Andrey Semashev | |
10 | * \date 15.11.2012 | |
11 | * | |
12 | * \brief This header is the Boost.Log library implementation, see the library documentation | |
13 | * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. | |
14 | */ | |
15 | ||
16 | #ifndef BOOST_LOG_DETAIL_FORMAT_HPP_INCLUDED_ | |
17 | #define BOOST_LOG_DETAIL_FORMAT_HPP_INCLUDED_ | |
18 | ||
19 | #include <string> | |
20 | #include <vector> | |
21 | #include <iosfwd> | |
22 | #include <boost/assert.hpp> | |
23 | #include <boost/move/core.hpp> | |
24 | #include <boost/move/utility_core.hpp> | |
25 | #include <boost/log/detail/config.hpp> | |
26 | #include <boost/log/detail/unhandled_exception_count.hpp> | |
27 | #include <boost/log/detail/cleanup_scope_guard.hpp> | |
28 | #include <boost/log/utility/formatting_ostream.hpp> | |
29 | #include <boost/log/detail/header.hpp> | |
30 | ||
31 | #ifdef BOOST_HAS_PRAGMA_ONCE | |
32 | #pragma once | |
33 | #endif | |
34 | ||
35 | namespace boost { | |
36 | ||
37 | BOOST_LOG_OPEN_NAMESPACE | |
38 | ||
39 | namespace aux { | |
40 | ||
41 | //! An element (either literal or placeholder) of the format string | |
42 | struct format_element | |
43 | { | |
44 | //! Argument placeholder number or -1 if it's not a placeholder (i.e. a literal) | |
45 | int arg_number; | |
46 | //! If the element describes a constant literal, the starting character and length of the literal | |
47 | unsigned int literal_start_pos, literal_len; | |
48 | ||
49 | format_element() : arg_number(0), literal_start_pos(0), literal_len(0) | |
50 | { | |
51 | } | |
52 | ||
53 | static format_element literal(unsigned int start_pos, unsigned int len) | |
54 | { | |
55 | format_element el; | |
56 | el.arg_number = -1; | |
57 | el.literal_start_pos = start_pos; | |
58 | el.literal_len = len; | |
59 | return el; | |
60 | } | |
61 | ||
62 | static format_element positional_argument(unsigned int arg_n) | |
63 | { | |
64 | format_element el; | |
65 | el.arg_number = arg_n; | |
66 | return el; | |
67 | } | |
68 | }; | |
69 | ||
70 | //! Parsed format string description | |
71 | template< typename CharT > | |
72 | struct format_description | |
73 | { | |
74 | BOOST_COPYABLE_AND_MOVABLE_ALT(format_description) | |
75 | ||
76 | public: | |
77 | //! Character type | |
78 | typedef CharT char_type; | |
79 | //! String type | |
80 | typedef std::basic_string< char_type > string_type; | |
81 | ||
82 | //! Array of format element descriptors | |
83 | typedef std::vector< format_element > format_element_list; | |
84 | ||
85 | //! Characters of all literal parts of the format string | |
86 | string_type literal_chars; | |
87 | //! Format element descriptors | |
88 | format_element_list format_elements; | |
89 | ||
90 | BOOST_DEFAULTED_FUNCTION(format_description(), {}) | |
91 | ||
92 | format_description(format_description const& that) : literal_chars(that.literal_chars), format_elements(that.format_elements) | |
93 | { | |
94 | } | |
95 | ||
96 | format_description(BOOST_RV_REF(format_description) that) | |
97 | { | |
98 | literal_chars.swap(that.literal_chars); | |
99 | format_elements.swap(that.format_elements); | |
100 | } | |
101 | ||
102 | format_description& operator= (format_description that) | |
103 | { | |
104 | literal_chars.swap(that.literal_chars); | |
105 | format_elements.swap(that.format_elements); | |
106 | return *this; | |
107 | } | |
108 | }; | |
109 | ||
110 | //! Parses format string | |
111 | template< typename CharT > | |
112 | BOOST_LOG_API format_description< CharT > parse_format(const CharT* begin, const CharT* end); | |
113 | ||
114 | //! Parses format string | |
115 | template< typename CharT > | |
116 | BOOST_FORCEINLINE format_description< CharT > parse_format(const CharT* begin) | |
117 | { | |
118 | return parse_format(begin, begin + std::char_traits< CharT >::length(begin)); | |
119 | } | |
120 | ||
121 | //! Parses format string | |
122 | template< typename CharT, typename TraitsT, typename AllocatorT > | |
123 | BOOST_FORCEINLINE format_description< CharT > parse_format(std::basic_string< CharT, TraitsT, AllocatorT > const& fmt) | |
124 | { | |
125 | const CharT* begin = fmt.c_str(); | |
126 | return parse_format(begin, begin + fmt.size()); | |
127 | } | |
128 | ||
129 | //! Formatter object | |
130 | template< typename CharT > | |
131 | class basic_format | |
132 | { | |
133 | public: | |
134 | //! Character type | |
135 | typedef CharT char_type; | |
136 | //! String type | |
137 | typedef std::basic_string< char_type > string_type; | |
138 | //! Stream type | |
139 | typedef basic_formatting_ostream< char_type > stream_type; | |
140 | //! Format description type | |
141 | typedef format_description< char_type > format_description_type; | |
142 | ||
143 | //! The pump receives arguments and formats them into strings. At destruction the pump composes the final string in the attached stream. | |
144 | class pump; | |
145 | friend class pump; | |
146 | ||
147 | private: | |
148 | //! Formatting params for a single placeholder in the format string | |
149 | struct formatting_params | |
150 | { | |
151 | //! Formatting element index in the format description | |
152 | unsigned int element_idx; | |
153 | //! Formatting result | |
154 | string_type target; | |
155 | ||
156 | formatting_params() : element_idx(~0u) {} | |
157 | }; | |
158 | typedef std::vector< formatting_params > formatting_params_list; | |
159 | ||
160 | private: | |
161 | //! Format string description | |
162 | format_description_type m_format; | |
163 | //! Formatting parameters for all placeholders | |
164 | formatting_params_list m_formatting_params; | |
165 | //! Current formatting position | |
166 | unsigned int m_current_idx; | |
167 | ||
168 | public: | |
169 | //! Initializing constructor | |
170 | explicit basic_format(string_type const& fmt) : m_format(aux::parse_format(fmt)), m_current_idx(0) | |
171 | { | |
172 | init_params(); | |
173 | } | |
174 | //! Initializing constructor | |
175 | explicit basic_format(const char_type* fmt) : m_format(aux::parse_format(fmt)), m_current_idx(0) | |
176 | { | |
177 | init_params(); | |
178 | } | |
179 | ||
180 | //! Clears all formatted strings and resets the current formatting position | |
181 | void clear() BOOST_NOEXCEPT | |
182 | { | |
183 | for (typename formatting_params_list::iterator it = m_formatting_params.begin(), end = m_formatting_params.end(); it != end; ++it) | |
184 | { | |
185 | it->target.clear(); | |
186 | } | |
187 | m_current_idx = 0; | |
188 | } | |
189 | ||
190 | //! Creates a pump that will receive all format arguments and put the formatted string into the stream | |
191 | pump make_pump(stream_type& strm) | |
192 | { | |
193 | // Flush the stream beforehand so that the pump can safely switch the stream storage string | |
194 | strm.flush(); | |
195 | return pump(*this, strm); | |
196 | } | |
197 | ||
198 | //! Composes the final string from the formatted pieces | |
199 | string_type str() const | |
200 | { | |
201 | string_type result; | |
202 | compose(result); | |
203 | return BOOST_LOG_NRVO_RESULT(result); | |
204 | } | |
205 | ||
206 | private: | |
207 | //! Initializes the formatting params | |
208 | void init_params() | |
209 | { | |
210 | typename format_description_type::format_element_list::const_iterator it = m_format.format_elements.begin(), end = m_format.format_elements.end(); | |
211 | for (; it != end; ++it) | |
212 | { | |
213 | if (it->arg_number >= 0) | |
214 | { | |
215 | if (static_cast< unsigned int >(it->arg_number) >= m_formatting_params.size()) | |
216 | m_formatting_params.resize(it->arg_number + 1); | |
217 | m_formatting_params[it->arg_number].element_idx = static_cast< unsigned int >(it - m_format.format_elements.begin()); | |
218 | } | |
219 | } | |
220 | } | |
221 | ||
222 | //! Composes the final string from the formatted pieces | |
223 | template< typename T > | |
224 | void compose(T& str) const | |
225 | { | |
226 | typename format_description_type::format_element_list::const_iterator it = m_format.format_elements.begin(), end = m_format.format_elements.end(); | |
227 | for (; it != end; ++it) | |
228 | { | |
229 | if (it->arg_number >= 0) | |
230 | { | |
231 | // This is a placeholder | |
232 | string_type const& target = m_formatting_params[it->arg_number].target; | |
233 | str.append(target.data(), target.size()); | |
234 | } | |
235 | else | |
236 | { | |
237 | // This is a literal | |
238 | const char_type* p = m_format.literal_chars.c_str() + it->literal_start_pos; | |
239 | str.append(p, it->literal_len); | |
240 | } | |
241 | } | |
242 | } | |
243 | }; | |
244 | ||
245 | //! The pump receives arguments and formats them into strings. At destruction the pump composes the final string in the attached stream. | |
246 | template< typename CharT > | |
247 | class basic_format< CharT >::pump | |
248 | { | |
249 | BOOST_MOVABLE_BUT_NOT_COPYABLE(pump) | |
250 | ||
251 | private: | |
252 | //! The guard temporarily replaces storage string in the specified stream | |
253 | struct scoped_storage | |
254 | { | |
255 | scoped_storage(stream_type& strm, string_type& storage) : m_stream(strm), m_storage_state_backup(strm.rdbuf()->get_storage_state()) | |
256 | { | |
257 | strm.attach(storage); | |
258 | } | |
259 | ~scoped_storage() | |
260 | { | |
261 | m_stream.rdbuf()->set_storage_state(m_storage_state_backup); | |
262 | } | |
263 | ||
264 | private: | |
265 | stream_type& m_stream; | |
266 | typename stream_type::streambuf_type::storage_state m_storage_state_backup; | |
267 | }; | |
268 | ||
269 | private: | |
270 | //! Reference to the owner | |
271 | basic_format* m_owner; | |
272 | //! Reference to the stream | |
273 | stream_type* m_stream; | |
274 | //! Unhandled exception count | |
275 | const unsigned int m_exception_count; | |
276 | ||
277 | public: | |
278 | //! Initializing constructor | |
279 | pump(basic_format& owner, stream_type& strm) BOOST_NOEXCEPT : m_owner(&owner), m_stream(&strm), m_exception_count(unhandled_exception_count()) | |
280 | { | |
281 | } | |
282 | ||
283 | //! Move constructor | |
284 | pump(BOOST_RV_REF(pump) that) BOOST_NOEXCEPT : m_owner(that.m_owner), m_stream(that.m_stream), m_exception_count(that.m_exception_count) | |
285 | { | |
286 | that.m_owner = NULL; | |
287 | that.m_stream = NULL; | |
288 | } | |
289 | ||
290 | //! Destructor | |
291 | ~pump() BOOST_NOEXCEPT_IF(false) | |
292 | { | |
293 | if (m_owner) | |
294 | { | |
295 | // Whether or not the destructor is called because of an exception, the format object has to be cleared | |
296 | boost::log::aux::cleanup_guard< basic_format< char_type > > cleanup1(*m_owner); | |
297 | ||
298 | BOOST_ASSERT(m_stream != NULL); | |
299 | if (m_exception_count >= unhandled_exception_count()) | |
300 | { | |
301 | // Compose the final string in the stream buffer | |
302 | m_stream->flush(); | |
303 | m_owner->compose(*m_stream->rdbuf()); | |
304 | } | |
305 | } | |
306 | } | |
307 | ||
308 | /*! | |
309 | * Puts an argument to the formatter. Note the pump has to be returned by value and not by reference in order this to | |
310 | * work with Boost.Phoenix expressions. Otherwise the pump that is returned from \c basic_format::make_pump is | |
311 | * destroyed after the first call to \c operator%, and the returned reference becomes dangling. | |
312 | */ | |
313 | template< typename T > | |
314 | pump operator% (T const& val) | |
315 | { | |
316 | BOOST_ASSERT_MSG(m_owner != NULL && m_stream != NULL, "Boost.Log: This basic_format::pump has already been moved from"); | |
317 | ||
318 | if (m_owner->m_current_idx < m_owner->m_formatting_params.size()) | |
319 | { | |
320 | scoped_storage storage_guard(*m_stream, m_owner->m_formatting_params[m_owner->m_current_idx].target); | |
321 | ||
322 | *m_stream << val; | |
323 | m_stream->flush(); | |
324 | ||
325 | ++m_owner->m_current_idx; | |
326 | } | |
327 | ||
328 | return boost::move(*this); | |
329 | } | |
330 | }; | |
331 | ||
332 | } // namespace aux | |
333 | ||
334 | BOOST_LOG_CLOSE_NAMESPACE // namespace log | |
335 | ||
336 | } // namespace boost | |
337 | ||
338 | #include <boost/log/detail/footer.hpp> | |
339 | ||
340 | #endif // BOOST_LOG_DETAIL_FORMAT_HPP_INCLUDED_ |