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