blob: b7b4b9a0527e194bcc1c2fdd888371ad527e7a93 [file] [log] [blame]
// 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