Chris@16: /* Chris@101: * Copyright Andrey Semashev 2007 - 2015. Chris@16: * Distributed under the Boost Software License, Version 1.0. Chris@16: * (See accompanying file LICENSE_1_0.txt or copy at Chris@16: * http://www.boost.org/LICENSE_1_0.txt) Chris@16: */ Chris@16: /*! Chris@16: * \file format.hpp Chris@16: * \author Andrey Semashev Chris@16: * \date 15.11.2012 Chris@16: * Chris@16: * \brief This header is the Boost.Log library implementation, see the library documentation Chris@16: * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. Chris@16: */ Chris@16: Chris@16: #ifndef BOOST_LOG_DETAIL_FORMAT_HPP_INCLUDED_ Chris@16: #define BOOST_LOG_DETAIL_FORMAT_HPP_INCLUDED_ Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #ifdef BOOST_HAS_PRAGMA_ONCE Chris@16: #pragma once Chris@16: #endif Chris@16: Chris@16: namespace boost { Chris@16: Chris@16: BOOST_LOG_OPEN_NAMESPACE Chris@16: Chris@16: namespace aux { Chris@16: Chris@16: //! An element (either literal or placeholder) of the format string Chris@16: struct format_element Chris@16: { Chris@16: //! Argument placeholder number or -1 if it's not a placeholder (i.e. a literal) Chris@16: int arg_number; Chris@16: //! If the element describes a constant literal, the starting character and length of the literal Chris@16: unsigned int literal_start_pos, literal_len; Chris@16: Chris@16: format_element() : arg_number(0), literal_start_pos(0), literal_len(0) Chris@16: { Chris@16: } Chris@16: Chris@16: static format_element literal(unsigned int start_pos, unsigned int len) Chris@16: { Chris@16: format_element el; Chris@16: el.arg_number = -1; Chris@16: el.literal_start_pos = start_pos; Chris@16: el.literal_len = len; Chris@16: return el; Chris@16: } Chris@16: Chris@16: static format_element positional_argument(unsigned int arg_n) Chris@16: { Chris@16: format_element el; Chris@16: el.arg_number = arg_n; Chris@16: return el; Chris@16: } Chris@16: }; Chris@16: Chris@16: //! Parsed format string description Chris@16: template< typename CharT > Chris@16: struct format_description Chris@16: { Chris@16: BOOST_COPYABLE_AND_MOVABLE_ALT(format_description) Chris@16: Chris@16: public: Chris@16: //! Character type Chris@16: typedef CharT char_type; Chris@16: //! String type Chris@16: typedef std::basic_string< char_type > string_type; Chris@16: Chris@16: //! Array of format element descriptors Chris@16: typedef std::vector< format_element > format_element_list; Chris@16: Chris@16: //! Characters of all literal parts of the format string Chris@16: string_type literal_chars; Chris@16: //! Format element descriptors Chris@16: format_element_list format_elements; Chris@16: Chris@16: BOOST_DEFAULTED_FUNCTION(format_description(), {}) Chris@16: Chris@16: format_description(format_description const& that) : literal_chars(that.literal_chars), format_elements(that.format_elements) Chris@16: { Chris@16: } Chris@16: Chris@16: format_description(BOOST_RV_REF(format_description) that) Chris@16: { Chris@16: literal_chars.swap(that.literal_chars); Chris@16: format_elements.swap(that.format_elements); Chris@16: } Chris@16: Chris@16: format_description& operator= (format_description that) Chris@16: { Chris@16: literal_chars.swap(that.literal_chars); Chris@16: format_elements.swap(that.format_elements); Chris@16: return *this; Chris@16: } Chris@16: }; Chris@16: Chris@16: //! Parses format string Chris@16: template< typename CharT > Chris@16: BOOST_LOG_API format_description< CharT > parse_format(const CharT* begin, const CharT* end); Chris@16: Chris@16: //! Parses format string Chris@16: template< typename CharT > Chris@16: BOOST_FORCEINLINE format_description< CharT > parse_format(const CharT* begin) Chris@16: { Chris@16: return parse_format(begin, begin + std::char_traits< CharT >::length(begin)); Chris@16: } Chris@16: Chris@16: //! Parses format string Chris@16: template< typename CharT, typename TraitsT, typename AllocatorT > Chris@16: BOOST_FORCEINLINE format_description< CharT > parse_format(std::basic_string< CharT, TraitsT, AllocatorT > const& fmt) Chris@16: { Chris@16: const CharT* begin = fmt.c_str(); Chris@16: return parse_format(begin, begin + fmt.size()); Chris@16: } Chris@16: Chris@16: //! Formatter object Chris@16: template< typename CharT > Chris@16: class basic_format Chris@16: { Chris@16: public: Chris@16: //! Character type Chris@16: typedef CharT char_type; Chris@16: //! String type Chris@16: typedef std::basic_string< char_type > string_type; Chris@16: //! Stream type Chris@16: typedef basic_formatting_ostream< char_type > stream_type; Chris@16: //! Format description type Chris@16: typedef format_description< char_type > format_description_type; Chris@16: Chris@16: //! The pump receives arguments and formats them into strings. At destruction the pump composes the final string in the attached stream. Chris@16: class pump; Chris@16: friend class pump; Chris@16: Chris@16: private: Chris@16: //! Formatting params for a single placeholder in the format string Chris@16: struct formatting_params Chris@16: { Chris@16: //! Formatting element index in the format description Chris@16: unsigned int element_idx; Chris@16: //! Formatting result Chris@16: string_type target; Chris@16: Chris@16: formatting_params() : element_idx(~0u) {} Chris@16: }; Chris@16: typedef std::vector< formatting_params > formatting_params_list; Chris@16: Chris@16: private: Chris@16: //! Format string description Chris@16: format_description_type m_format; Chris@16: //! Formatting parameters for all placeholders Chris@16: formatting_params_list m_formatting_params; Chris@16: //! Current formatting position Chris@16: unsigned int m_current_idx; Chris@16: Chris@16: public: Chris@16: //! Initializing constructor Chris@16: explicit basic_format(string_type const& fmt) : m_format(aux::parse_format(fmt)), m_current_idx(0) Chris@16: { Chris@16: init_params(); Chris@16: } Chris@16: //! Initializing constructor Chris@16: explicit basic_format(const char_type* fmt) : m_format(aux::parse_format(fmt)), m_current_idx(0) Chris@16: { Chris@16: init_params(); Chris@16: } Chris@16: Chris@16: //! Clears all formatted strings and resets the current formatting position Chris@16: void clear() BOOST_NOEXCEPT Chris@16: { Chris@16: for (typename formatting_params_list::iterator it = m_formatting_params.begin(), end = m_formatting_params.end(); it != end; ++it) Chris@16: { Chris@16: it->target.clear(); Chris@16: } Chris@16: m_current_idx = 0; Chris@16: } Chris@16: Chris@16: //! Creates a pump that will receive all format arguments and put the formatted string into the stream Chris@16: pump make_pump(stream_type& strm) BOOST_NOEXCEPT Chris@16: { Chris@16: return pump(*this, strm); Chris@16: } Chris@16: Chris@16: //! Composes the final string from the formatted pieces Chris@16: void compose(string_type& str) const Chris@16: { Chris@16: typename format_description_type::format_element_list::const_iterator it = m_format.format_elements.begin(), end = m_format.format_elements.end(); Chris@16: for (; it != end; ++it) Chris@16: { Chris@16: if (it->arg_number >= 0) Chris@16: { Chris@16: // This is a placeholder Chris@16: str.append(m_formatting_params[it->arg_number].target); Chris@16: } Chris@16: else Chris@16: { Chris@16: // This is a literal Chris@16: const char_type* p = m_format.literal_chars.c_str() + it->literal_start_pos; Chris@16: str.append(p, it->literal_len); Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: //! Composes the final string from the formatted pieces Chris@16: string_type str() const Chris@16: { Chris@16: string_type result; Chris@16: compose(result); Chris@16: return boost::move(result); Chris@16: } Chris@16: Chris@16: private: Chris@16: //! Initializes the formatting params Chris@16: void init_params() Chris@16: { Chris@16: typename format_description_type::format_element_list::const_iterator it = m_format.format_elements.begin(), end = m_format.format_elements.end(); Chris@16: for (; it != end; ++it) Chris@16: { Chris@16: if (it->arg_number >= 0) Chris@16: { Chris@16: if (static_cast< unsigned int >(it->arg_number) >= m_formatting_params.size()) Chris@16: m_formatting_params.resize(it->arg_number + 1); Chris@16: m_formatting_params[it->arg_number].element_idx = static_cast< unsigned int >(it - m_format.format_elements.begin()); Chris@16: } Chris@16: } Chris@16: } Chris@16: }; Chris@16: Chris@16: //! The pump receives arguments and formats them into strings. At destruction the pump composes the final string in the attached stream. Chris@16: template< typename CharT > Chris@16: class basic_format< CharT >::pump Chris@16: { Chris@16: BOOST_MOVABLE_BUT_NOT_COPYABLE(pump) Chris@16: Chris@16: private: Chris@16: //! The guard temporarily replaces storage string in the specified stream Chris@16: struct scoped_storage Chris@16: { Chris@16: scoped_storage(stream_type& strm, string_type& storage) : m_stream(strm), m_storage_backup(*strm.rdbuf()->storage()) Chris@16: { Chris@16: strm.attach(storage); Chris@16: } Chris@16: ~scoped_storage() Chris@16: { Chris@16: m_stream.attach(m_storage_backup); Chris@16: } Chris@16: Chris@16: private: Chris@16: stream_type& m_stream; Chris@16: string_type& m_storage_backup; Chris@16: }; Chris@16: Chris@16: private: Chris@16: //! Reference to the owner Chris@16: basic_format* m_owner; Chris@16: //! Reference to the stream Chris@16: stream_type* m_stream; Chris@16: //! Unhandled exception count Chris@16: const unsigned int m_exception_count; Chris@16: Chris@16: public: Chris@16: //! Initializing constructor Chris@16: pump(basic_format& owner, stream_type& strm) BOOST_NOEXCEPT : m_owner(&owner), m_stream(&strm), m_exception_count(unhandled_exception_count()) Chris@16: { Chris@16: } Chris@16: Chris@16: //! Move constructor Chris@16: 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: { Chris@16: that.m_owner = NULL; Chris@16: that.m_stream = NULL; Chris@16: } Chris@16: Chris@16: //! Destructor Chris@16: ~pump() BOOST_NOEXCEPT_IF(false) Chris@16: { Chris@16: if (m_owner) Chris@16: { Chris@16: // Whether or not the destructor is called because of an exception, the format object has to be cleared Chris@16: boost::log::aux::cleanup_guard< basic_format< char_type > > cleanup1(*m_owner); Chris@16: Chris@16: BOOST_ASSERT(m_stream != NULL); Chris@16: if (m_exception_count >= unhandled_exception_count()) Chris@16: { Chris@16: // Compose the final string in the stream buffer Chris@16: m_stream->flush(); Chris@16: m_owner->compose(*m_stream->rdbuf()->storage()); Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: /*! Chris@16: * 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: * work with Boost.Phoenix expressions. Otherwise the pump that is returned from \c basic_format::make_pump is Chris@16: * destroyed after the first call to \c operator%, and the returned reference becomes dangling. Chris@16: */ Chris@16: template< typename T > Chris@16: pump operator% (T const& val) Chris@16: { Chris@16: BOOST_ASSERT_MSG(m_owner != NULL && m_stream != NULL, "Boost.Log: This basic_format::pump has already been moved from"); Chris@16: Chris@16: if (m_owner->m_current_idx < m_owner->m_formatting_params.size()) Chris@16: { Chris@16: scoped_storage storage_guard(*m_stream, m_owner->m_formatting_params[m_owner->m_current_idx].target); Chris@16: Chris@16: *m_stream << val; Chris@16: m_stream->flush(); Chris@16: Chris@16: ++m_owner->m_current_idx; Chris@16: } Chris@16: Chris@16: return boost::move(*this); Chris@16: } Chris@16: }; Chris@16: Chris@16: } // namespace aux Chris@16: Chris@16: BOOST_LOG_CLOSE_NAMESPACE // namespace log Chris@16: Chris@16: } // namespace boost Chris@16: Chris@16: #include Chris@16: Chris@101: #endif // BOOST_LOG_DETAIL_FORMAT_HPP_INCLUDED_