// Copyright (c) 2001-2011 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) | |
#pragma once | |
#endif | |
#include <iterator> | |
#include <vector> | |
#include <algorithm> | |
#include <boost/config.hpp> | |
#include <boost/noncopyable.hpp> | |
#include <boost/mpl/if.hpp> | |
#include <boost/spirit/home/karma/generator.hpp> | |
#include <boost/spirit/home/support/iterators/ostream_iterator.hpp> | |
#include <boost/spirit/home/support/unused.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(1) {} | |
void tidy() { count = 0; line = 1; column = 1; } | |
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; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
struct position_policy | |
{ | |
position_policy() {} | |
position_policy(position_policy const& rhs) | |
: track_position_data(rhs.track_position_data) {} | |
template <typename T> | |
void output(T const& value) | |
{ | |
// track position in the output | |
track_position_data.output(value); | |
} | |
// return the current count in the output | |
std::size_t get_out_count() const | |
{ | |
return track_position_data.get_count(); | |
} | |
private: | |
position_sink track_position_data; // for position tracking | |
}; | |
struct no_position_policy | |
{ | |
no_position_policy() {} | |
no_position_policy(no_position_policy const&) {} | |
template <typename T> | |
void output(T const& /*value*/) {} | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// This class is used to count the number of characters streamed into the | |
// output. | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename OutputIterator> | |
class counting_sink : boost::noncopyable | |
{ | |
public: | |
counting_sink(OutputIterator& sink_, std::size_t count_ = 0 | |
, bool enabled = true) | |
: count(count_), initial_count(count), prev_count(0), sink(sink_) | |
{ | |
prev_count = sink.chain_counting(enabled ? this : NULL); | |
} | |
~counting_sink() | |
{ | |
if (prev_count) // propagate count | |
prev_count->update_count(count-initial_count); | |
sink.chain_counting(prev_count); | |
} | |
void output() | |
{ | |
++count; | |
} | |
std::size_t get_count() const { return count; } | |
// propagate count from embedded counters | |
void update_count(std::size_t c) | |
{ | |
count += c; | |
} | |
private: | |
std::size_t count; | |
std::size_t initial_count; | |
counting_sink* prev_count; // previous counter in chain | |
OutputIterator& sink; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename OutputIterator> | |
struct counting_policy | |
{ | |
public: | |
counting_policy() : count(NULL) {} | |
counting_policy(counting_policy const& rhs) : count(rhs.count) {} | |
// functions related to counting | |
counting_sink<OutputIterator>* chain_counting( | |
counting_sink<OutputIterator>* count_data) | |
{ | |
counting_sink<OutputIterator>* prev_count = count; | |
count = count_data; | |
return prev_count; | |
} | |
template <typename T> | |
void output(T const&) | |
{ | |
// count characters, if appropriate | |
if (NULL != count) | |
count->output(); | |
} | |
private: | |
counting_sink<OutputIterator>* count; // for counting | |
}; | |
struct no_counting_policy | |
{ | |
no_counting_policy() {} | |
no_counting_policy(no_counting_policy const&) {} | |
template <typename T> | |
void output(T const& /*value*/) {} | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// The following classes are used to intercept the output into a buffer | |
// allowing to do things like alignment, character escaping etc. | |
/////////////////////////////////////////////////////////////////////////// | |
class buffer_sink : boost::noncopyable | |
{ | |
public: | |
buffer_sink() | |
: width(0) {} | |
~buffer_sink() | |
{ | |
tidy(); | |
} | |
void enable(std::size_t width_) | |
{ | |
tidy(); // release existing buffer | |
width = (width_ == std::size_t(-1)) ? 0 : width_; | |
buffer.reserve(width); | |
} | |
void tidy() | |
{ | |
buffer.clear(); | |
width = 0; | |
} | |
template <typename T> | |
void output(T const& value) | |
{ | |
BOOST_STATIC_ASSERT(sizeof(T) <= sizeof(wchar_t)); | |
buffer.push_back(value); | |
} | |
template <typename OutputIterator_> | |
bool copy(OutputIterator_& sink, std::size_t maxwidth) const | |
{ | |
#if defined(BOOST_MSVC) | |
#pragma warning(push) | |
#pragma warning(disable: 4267) | |
#endif | |
typename std::basic_string<wchar_t>::const_iterator end = | |
buffer.begin() + (std::min)(buffer.size(), maxwidth); | |
#if defined(BOOST_MSVC) | |
#pragma warning(pop) | |
#endif | |
std::copy(buffer.begin(), end, sink); | |
return true; | |
} | |
template <typename RestIterator> | |
bool copy_rest(RestIterator& sink, std::size_t start_at) const | |
{ | |
#if defined(BOOST_MSVC) | |
#pragma warning(push) | |
#pragma warning(disable: 4267) | |
#endif | |
typename std::basic_string<wchar_t>::const_iterator begin = | |
buffer.begin() + (std::min)(buffer.size(), start_at); | |
#if defined(BOOST_MSVC) | |
#pragma warning(pop) | |
#endif | |
std::copy(begin, buffer.end(), sink); | |
return true; | |
} | |
std::size_t buffer_size() const | |
{ | |
return buffer.size(); | |
} | |
private: | |
std::size_t width; | |
std::basic_string<wchar_t> buffer; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
struct buffering_policy | |
{ | |
public: | |
buffering_policy() : buffer(NULL) {} | |
buffering_policy(buffering_policy const& rhs) : buffer(rhs.buffer) {} | |
// functions related to buffering | |
buffer_sink* chain_buffering(buffer_sink* buffer_data) | |
{ | |
buffer_sink* prev_buffer = buffer; | |
buffer = buffer_data; | |
return prev_buffer; | |
} | |
template <typename T> | |
bool output(T const& value) | |
{ | |
// buffer characters, if appropriate | |
if (NULL != buffer) { | |
buffer->output(value); | |
return false; | |
} | |
return true; | |
} | |
bool has_buffer() const { return NULL != buffer; } | |
private: | |
buffer_sink* buffer; | |
}; | |
struct no_buffering_policy | |
{ | |
no_buffering_policy() {} | |
no_buffering_policy(no_counting_policy const&) {} | |
template <typename T> | |
bool output(T const& /*value*/) | |
{ | |
return true; | |
} | |
bool has_buffer() const { return false; } | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// forward declaration only | |
template <typename OutputIterator> | |
struct enable_buffering; | |
template <typename OutputIterator, typename Properties | |
, typename Derived = unused_type> | |
class output_iterator; | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename Buffering, typename Counting, typename Tracking> | |
struct output_iterator_base : Buffering, Counting, Tracking | |
{ | |
typedef Buffering buffering_policy; | |
typedef Counting counting_policy; | |
typedef Tracking tracking_policy; | |
output_iterator_base() {} | |
output_iterator_base(output_iterator_base const& rhs) | |
: buffering_policy(rhs), counting_policy(rhs), tracking_policy(rhs) | |
{} | |
template <typename T> | |
bool output(T const& value) | |
{ | |
this->counting_policy::output(value); | |
this->tracking_policy::output(value); | |
return this->buffering_policy::output(value); | |
} | |
}; | |
template <typename Buffering, typename Counting, typename Tracking> | |
struct disabling_output_iterator : Buffering, Counting, Tracking | |
{ | |
typedef Buffering buffering_policy; | |
typedef Counting counting_policy; | |
typedef Tracking tracking_policy; | |
disabling_output_iterator() : do_output(true) {} | |
disabling_output_iterator(disabling_output_iterator const& rhs) | |
: buffering_policy(rhs), counting_policy(rhs), tracking_policy(rhs) | |
, do_output(rhs.do_output) | |
{} | |
template <typename T> | |
bool output(T const& value) | |
{ | |
if (!do_output) | |
return false; | |
this->counting_policy::output(value); | |
this->tracking_policy::output(value); | |
return this->buffering_policy::output(value); | |
} | |
bool do_output; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename OutputIterator, typename Properties, typename Derived> | |
struct make_output_iterator | |
{ | |
// get the most derived type of this class | |
typedef typename mpl::if_< | |
traits::not_is_unused<Derived>, Derived | |
, output_iterator<OutputIterator, Properties, Derived> | |
>::type most_derived_type; | |
enum { properties = Properties::value }; | |
typedef typename mpl::if_c< | |
(properties & generator_properties::tracking) ? true : false | |
, position_policy, no_position_policy | |
>::type tracking_type; | |
typedef typename mpl::if_c< | |
(properties & generator_properties::buffering) ? true : false | |
, buffering_policy, no_buffering_policy | |
>::type buffering_type; | |
typedef typename mpl::if_c< | |
(properties & generator_properties::counting) ? true : false | |
, counting_policy<most_derived_type>, no_counting_policy | |
>::type counting_type; | |
typedef typename mpl::if_c< | |
(properties & generator_properties::disabling) ? true : false | |
, disabling_output_iterator<buffering_type, counting_type, tracking_type> | |
, output_iterator_base<buffering_type, counting_type, tracking_type> | |
>::type type; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// Karma uses an 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 Properties, typename Derived> | |
class output_iterator | |
: public make_output_iterator<OutputIterator, Properties, Derived>::type | |
{ | |
private: | |
// base iterator type | |
typedef typename make_output_iterator< | |
OutputIterator, Properties, Derived>::type base_iterator; | |
public: | |
typedef std::output_iterator_tag iterator_category; | |
typedef void value_type; | |
typedef void difference_type; | |
typedef void pointer; | |
typedef void reference; | |
explicit output_iterator(OutputIterator& sink_) | |
: sink(&sink_) | |
{} | |
output_iterator(output_iterator const& rhs) | |
: base_iterator(rhs), sink(rhs.sink) | |
{} | |
output_iterator& operator*() { return *this; } | |
output_iterator& operator++() | |
{ | |
if (!this->base_iterator::has_buffer()) | |
++(*sink); // increment only if not buffering | |
return *this; | |
} | |
output_iterator operator++(int) | |
{ | |
if (!this->base_iterator::has_buffer()) { | |
output_iterator t(*this); | |
++(*sink); | |
return t; | |
} | |
return *this; | |
} | |
#if defined(BOOST_MSVC) | |
// 'argument' : conversion from '...' to '...', possible loss of data | |
#pragma warning (push) | |
#pragma warning (disable: 4244) | |
#endif | |
template <typename T> | |
void operator=(T const& value) | |
{ | |
if (this->base_iterator::output(value)) | |
*(*sink) = value; | |
} | |
#if defined(BOOST_MSVC) | |
#pragma warning (pop) | |
#endif | |
// plain output iterators are considered to be good all the time | |
bool good() const { return true; } | |
protected: | |
// this is the wrapped user supplied output iterator | |
OutputIterator* sink; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename T, typename Elem, typename Traits, typename Properties> | |
class output_iterator<karma::ostream_iterator<T, Elem, Traits>, Properties> | |
: public output_iterator<karma::ostream_iterator<T, Elem, Traits>, Properties | |
, output_iterator<karma::ostream_iterator<T, Elem, Traits>, Properties> > | |
{ | |
private: | |
typedef output_iterator<karma::ostream_iterator<T, Elem, Traits>, Properties | |
, output_iterator<karma::ostream_iterator<T, Elem, Traits>, Properties> | |
> base_type; | |
typedef karma::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(); } | |
ostream_type const& get_ostream() const { return (*this->sink).get_ostream(); } | |
// expose good bit of underlying stream object | |
bool good() const { return (*this->sink).get_ostream().good(); } | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// 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) | |
: count_data(sink_, count) {} | |
// get number of characters counted since last enable | |
std::size_t count() const | |
{ | |
return count_data.get_count(); | |
} | |
private: | |
counting_sink<OutputIterator> count_data; // for counting | |
}; | |
template <typename OutputIterator> | |
struct disable_counting | |
{ | |
disable_counting(OutputIterator& sink_) | |
: count_data(sink_, 0, false) {} | |
private: | |
counting_sink<OutputIterator> count_data; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// 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 = std::size_t(-1)) | |
: sink(sink_), prev_buffer(NULL), enabled(false) | |
{ | |
buffer_data.enable(width); | |
prev_buffer = sink.chain_buffering(&buffer_data); | |
enabled = true; | |
} | |
~enable_buffering() | |
{ | |
disable(); | |
} | |
// reset buffer chain to initial state | |
void disable() | |
{ | |
if (enabled) { | |
BOOST_VERIFY(&buffer_data == sink.chain_buffering(prev_buffer)); | |
enabled = false; | |
} | |
} | |
// copy to the underlying sink whatever is in the local buffer | |
bool buffer_copy(std::size_t maxwidth = std::size_t(-1) | |
, bool disable_ = true) | |
{ | |
if (disable_) | |
disable(); | |
return buffer_data.copy(sink, maxwidth) && sink.good(); | |
} | |
// return number of characters stored in the buffer | |
std::size_t buffer_size() const | |
{ | |
return buffer_data.buffer_size(); | |
} | |
// copy to the remaining characters to the specified sink | |
template <typename RestIterator> | |
bool buffer_copy_rest(RestIterator& sink, std::size_t start_at = 0) const | |
{ | |
return buffer_data.copy_rest(sink, start_at); | |
} | |
// copy the contents to the given output iterator | |
template <typename OutputIterator_> | |
bool buffer_copy_to(OutputIterator_& sink | |
, std::size_t maxwidth = std::size_t(-1)) const | |
{ | |
return buffer_data.copy(sink, maxwidth); | |
} | |
private: | |
OutputIterator& sink; | |
buffer_sink buffer_data; // for buffering | |
buffer_sink* prev_buffer; // previous buffer in chain | |
bool enabled; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// Helper class for exception safe disabling of output | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename OutputIterator> | |
struct disable_output | |
{ | |
disable_output(OutputIterator& sink_) | |
: sink(sink_), prev_do_output(sink.do_output) | |
{ | |
sink.do_output = false; | |
} | |
~disable_output() | |
{ | |
sink.do_output = prev_do_output; | |
} | |
OutputIterator& sink; | |
bool prev_do_output; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename Sink> | |
bool sink_is_good(Sink const& sink) | |
{ | |
return true; // the general case is always good | |
} | |
template <typename OutputIterator, typename Derived> | |
bool sink_is_good(output_iterator<OutputIterator, Derived> const& sink) | |
{ | |
return sink.good(); // our own output iterators are handled separately | |
} | |
}}}} | |
#endif | |