| // Copyright (c) 2001-2009 Hartmut Kaiser |
| // |
| // Distributed under the Boost Software License, Version 1.0. (See accompanying |
| // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| |
| #if !defined(BOOST_SPIRIT_KARMA_OUTPUT_ITERATOR_MAY_26_2007_0506PM) |
| #define BOOST_SPIRIT_KARMA_OUTPUT_ITERATOR_MAY_26_2007_0506PM |
| |
| #if defined(_MSC_VER) && (_MSC_VER >= 1020) |
| #pragma once // MS compatible compilers support #pragma once |
| #endif |
| |
| #include <iterator> |
| #include <vector> |
| #include <algorithm> |
| |
| #include <boost/noncopyable.hpp> |
| #include <boost/spirit/home/karma/detail/ostream_iterator.hpp> |
| |
| namespace boost { namespace spirit { namespace karma { namespace detail |
| { |
| /////////////////////////////////////////////////////////////////////////// |
| // This class is used to keep track of the current position in the output. |
| /////////////////////////////////////////////////////////////////////////// |
| class position_sink |
| { |
| public: |
| position_sink() : count(0), line(1), column(0) {} |
| void tidy() { count = 0; line = 1; column = 0; } |
| |
| template <typename T> |
| void output(T const& value) |
| { |
| ++count; |
| if (value == '\n') { |
| ++line; |
| column = 1; |
| } |
| else { |
| ++column; |
| } |
| } |
| std::size_t get_count() const { return count; } |
| std::size_t get_line() const { return line; } |
| std::size_t get_column() const { return column; } |
| |
| private: |
| std::size_t count; |
| std::size_t line; |
| std::size_t column; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // This class is used to count the umber of characters streamed into the |
| // output. |
| /////////////////////////////////////////////////////////////////////////// |
| class counting_sink |
| { |
| public: |
| counting_sink() : count(0) {} |
| |
| void init(std::size_t count_) { count = count_; } |
| void tidy() { count = 0; } |
| |
| void output() { ++count; } |
| std::size_t get_count() const { return count; } |
| |
| private: |
| std::size_t count; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // The following classes are used to intercept the output into a buffer |
| // allowing to do things like alignment, character escaping etc. |
| // |
| // We need to use virtual functions because output_iterators do not have |
| // an associated value_type. The type of the buffer elements is available |
| // at insertion time only (and not at buffer creation time). |
| /////////////////////////////////////////////////////////////////////////// |
| template <typename OutputIterator> |
| struct abstract_container |
| { |
| virtual ~abstract_container() {} |
| virtual void output(void const *item) = 0; |
| virtual void copy(OutputIterator& sink) = 0; |
| virtual std::size_t buffer_size() = 0; |
| }; |
| |
| template <typename OutputIterator, typename T> |
| class concrete_container : public abstract_container<OutputIterator> |
| { |
| public: |
| concrete_container(std::size_t size) |
| { |
| buffer.reserve(size); |
| } |
| ~concrete_container() {} |
| |
| void output(void const *item) |
| { |
| buffer.push_back(*static_cast<T const*>(item)); |
| } |
| void copy(OutputIterator& sink) |
| { |
| std::copy(buffer.begin(), buffer.end(), sink); |
| } |
| std::size_t buffer_size() |
| { |
| return buffer.size(); |
| } |
| |
| private: |
| std::vector<T> buffer; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////// |
| template <typename OutputIterator> |
| class buffer_sink : boost::noncopyable |
| { |
| public: |
| buffer_sink() |
| : width(0), buffer(0) |
| {} |
| |
| ~buffer_sink() |
| { |
| delete buffer; |
| } |
| |
| void init(std::size_t width_) { width = width_; } |
| void tidy() { delete buffer; buffer = 0; width = 0; } |
| |
| template <typename T> |
| void output(T const& value) |
| { |
| if (0 == buffer) |
| { |
| typedef concrete_container<OutputIterator, T> container; |
| buffer = new container(width); |
| } |
| buffer->output(&value); |
| } |
| |
| void copy(OutputIterator& sink) const |
| { |
| if (buffer) |
| buffer->copy(sink); |
| } |
| |
| std::size_t buffer_size() const |
| { |
| return buffer ? buffer->buffer_size() : 0; |
| } |
| |
| private: |
| std::size_t width; |
| abstract_container<OutputIterator> *buffer; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // forward declaration only |
| /////////////////////////////////////////////////////////////////////////// |
| template <typename OutputIterator> struct enable_counting; |
| template <typename OutputIterator> struct enable_buffering; |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Karma uses a output iterator wrapper for all output operations. This |
| // is necessary to avoid the dreaded 'scanner business' problem, i.e. the |
| // dependency of rules and grammars on the used output iterator. |
| // |
| // By default the user supplied output iterator is wrapped inside an |
| // instance of this internal output_iterator class. |
| // |
| // This output_iterator class normally just forwards to the embedded user |
| // supplied iterator. But it is possible to enable additional functionality |
| // on demand, such as counting, buffering, and position tracking. |
| /////////////////////////////////////////////////////////////////////////// |
| template <typename OutputIterator, typename Enable = void> |
| class output_iterator : boost::noncopyable |
| { |
| private: |
| enum output_mode |
| { |
| output_characters = 0, // just hand through character |
| count_characters = 1, // additionally count characters |
| buffer_characters = 2 // buffer all characters, no output |
| }; |
| |
| struct output_proxy |
| { |
| output_proxy(output_iterator& parent) |
| : parent(parent) |
| {} |
| |
| template <typename T> |
| output_proxy& operator=(T const& value) |
| { |
| parent.output(value); |
| return *this; |
| } |
| |
| private: |
| output_iterator& parent; |
| |
| // suppress warning about assignment operator not being generated |
| output_proxy& operator=(output_proxy const&); |
| }; |
| |
| #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) |
| private: |
| friend struct enable_counting<output_iterator<OutputIterator> >; |
| friend struct enable_buffering<output_iterator<OutputIterator> >; |
| friend struct enable_counting<output_iterator<OutputIterator, int> >; |
| friend struct enable_buffering<output_iterator<OutputIterator, int> >; |
| #else |
| public: |
| #endif |
| // functions related to counting |
| void enable_counting(std::size_t count = 0) |
| { |
| count_data.init(count); |
| mode = output_mode(mode | count_characters); |
| } |
| void disable_counting() |
| { |
| mode = output_mode(mode & ~count_characters); |
| } |
| void reset_counting() |
| { |
| count_data.tidy(); |
| } |
| |
| // functions related to buffering |
| void enable_buffering(std::size_t width = 0) |
| { |
| buffer_data.init(width); |
| mode = output_mode(mode | buffer_characters); |
| } |
| void disable_buffering() |
| { |
| mode = output_mode(mode & ~buffer_characters); |
| } |
| void reset_buffering() |
| { |
| buffer_data.tidy(); |
| } |
| |
| public: |
| typedef std::output_iterator_tag iterator_category; |
| typedef void value_type; |
| typedef void difference_type; |
| typedef void pointer; |
| typedef void reference; |
| |
| output_iterator(OutputIterator& sink_) |
| : sink(sink_), mode(output_characters) |
| {} |
| |
| output_proxy operator*() { return output_proxy(*this); } |
| output_iterator& operator++() { ++sink; return *this; } |
| output_iterator& operator++(int) { sink++; return *this; } |
| |
| template <typename T> |
| void output(T const& value) |
| { |
| if (mode & count_characters) // count characters, if appropriate |
| count_data.output(); |
| |
| // always track position in the output (this is needed by different |
| // generators, such as indent, pad, etc.) |
| track_position_data.output(value); |
| |
| if (mode & buffer_characters) // buffer output, if appropriate |
| buffer_data.output(value); |
| else |
| *sink = value; |
| } |
| |
| // functions related to counting |
| std::size_t count() const |
| { |
| return count_data.get_count(); |
| } |
| |
| // functions related to buffering |
| std::size_t buffer_size() const |
| { |
| return buffer_data.buffer_size(); |
| } |
| void buffer_copy() |
| { |
| buffer_data.copy(sink); |
| } |
| |
| // return the current count in the output |
| std::size_t get_out_count() const |
| { |
| return track_position_data.get_count(); |
| } |
| |
| protected: |
| // this is the wrapped user supplied output iterator |
| OutputIterator& sink; |
| |
| private: |
| // these are the hooks providing optional functionality |
| counting_sink count_data; // for counting |
| buffer_sink<OutputIterator> buffer_data; // for buffering |
| position_sink track_position_data; // for position tracking |
| int mode; |
| |
| // suppress warning about assignment operator not being generated |
| output_iterator& operator=(output_iterator const&); |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////// |
| template <typename T, typename Elem, typename Traits> |
| class output_iterator<ostream_iterator<T, Elem, Traits> > |
| : public output_iterator<ostream_iterator<T, Elem, Traits>, int> |
| { |
| private: |
| typedef |
| output_iterator<ostream_iterator<T, Elem, Traits>, int> |
| base_type; |
| typedef ostream_iterator<T, Elem, Traits> base_iterator_type; |
| typedef std::basic_ostream<Elem, Traits> ostream_type; |
| |
| public: |
| output_iterator(base_iterator_type& sink) |
| : base_type(sink) |
| {} |
| |
| ostream_type& get_ostream() { return this->sink.get_ostream(); } |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Helper class for exception safe enabling of character counting in the |
| // output iterator |
| /////////////////////////////////////////////////////////////////////////// |
| template <typename OutputIterator> |
| struct enable_counting |
| { |
| enable_counting(OutputIterator& sink_, std::size_t count = 0) |
| : sink(sink_) |
| { |
| sink.enable_counting(count); |
| } |
| ~enable_counting() |
| { |
| sink.disable_counting(); |
| sink.reset_counting(); |
| } |
| |
| void disable() |
| { |
| sink.disable_counting(); |
| } |
| |
| OutputIterator& sink; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Helper class for exception safe enabling of character buffering in the |
| // output iterator |
| /////////////////////////////////////////////////////////////////////////// |
| template <typename OutputIterator> |
| struct enable_buffering |
| { |
| enable_buffering(OutputIterator& sink_, std::size_t width = 0) |
| : sink(sink_) |
| { |
| sink.enable_buffering(width); |
| } |
| ~enable_buffering() |
| { |
| sink.disable_buffering(); |
| sink.reset_buffering(); |
| } |
| |
| void disable() |
| { |
| sink.disable_buffering(); |
| } |
| |
| OutputIterator& sink; |
| }; |
| |
| }}}} |
| |
| #endif |
| |