• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //  Copyright (c) 2001-2011 Hartmut Kaiser
2 //
3 //  Distributed under the Boost Software License, Version 1.0. (See accompanying
4 //  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #if !defined(BOOST_SPIRIT_KARMA_OUTPUT_ITERATOR_MAY_26_2007_0506PM)
7 #define BOOST_SPIRIT_KARMA_OUTPUT_ITERATOR_MAY_26_2007_0506PM
8 
9 #if defined(_MSC_VER)
10 #pragma once
11 #endif
12 
13 #include <iterator>
14 #include <vector>
15 #include <algorithm>
16 
17 #include <boost/config.hpp>
18 #include <boost/noncopyable.hpp>
19 #include <boost/mpl/if.hpp>
20 
21 #include <boost/spirit/home/karma/generator.hpp>
22 #include <boost/spirit/home/support/iterators/ostream_iterator.hpp>
23 #include <boost/spirit/home/support/unused.hpp>
24 
25 #if defined(BOOST_MSVC) && defined(BOOST_SPIRIT_UNICODE)
26 #include <boost/spirit/home/support/char_encoding/unicode.hpp>
27 #endif
28 
29 namespace boost { namespace spirit { namespace karma { namespace detail
30 {
31     ///////////////////////////////////////////////////////////////////////////
32     //  This class is used to keep track of the current position in the output.
33     ///////////////////////////////////////////////////////////////////////////
34     class position_sink
35     {
36     public:
position_sink()37         position_sink() : count(0), line(1), column(1) {}
tidy()38         void tidy() { count = 0; line = 1; column = 1; }
39 
40         template <typename T>
output(T const & value)41         void output(T const& value)
42         {
43             ++count;
44             if (value == '\n') {
45                 ++line;
46                 column = 1;
47             }
48             else {
49                 ++column;
50             }
51         }
get_count() const52         std::size_t get_count() const { return count; }
get_line() const53         std::size_t get_line() const { return line; }
get_column() const54         std::size_t get_column() const { return column; }
55 
56     private:
57         std::size_t count;
58         std::size_t line;
59         std::size_t column;
60     };
61 
62     ///////////////////////////////////////////////////////////////////////////
63     struct position_policy
64     {
position_policyboost::spirit::karma::detail::position_policy65         position_policy() {}
position_policyboost::spirit::karma::detail::position_policy66         position_policy(position_policy const& rhs)
67           : track_position_data(rhs.track_position_data) {}
68 
69         template <typename T>
outputboost::spirit::karma::detail::position_policy70         void output(T const& value)
71         {
72             // track position in the output
73             track_position_data.output(value);
74         }
75 
76         // return the current count in the output
get_out_countboost::spirit::karma::detail::position_policy77         std::size_t get_out_count() const
78         {
79             return track_position_data.get_count();
80         }
81 
82 	// return the current line in the output
get_lineboost::spirit::karma::detail::position_policy83 	std::size_t get_line() const
84 	{
85 	    return track_position_data.get_line();
86 	}
87 
88 	// return the current column in the output
get_columnboost::spirit::karma::detail::position_policy89 	std::size_t get_column() const
90 	{
91 	    return track_position_data.get_column();
92 	}
93 
94     private:
95         position_sink track_position_data;            // for position tracking
96     };
97 
98     struct no_position_policy
99     {
no_position_policyboost::spirit::karma::detail::no_position_policy100         no_position_policy() {}
no_position_policyboost::spirit::karma::detail::no_position_policy101         no_position_policy(no_position_policy const&) {}
102 
103         template <typename T>
outputboost::spirit::karma::detail::no_position_policy104         void output(T const& /*value*/) {}
105     };
106 
107     ///////////////////////////////////////////////////////////////////////////
108     //  This class is used to count the number of characters streamed into the
109     //  output.
110     ///////////////////////////////////////////////////////////////////////////
111     template <typename OutputIterator>
112     class counting_sink : boost::noncopyable
113     {
114     public:
counting_sink(OutputIterator & sink_,std::size_t count_=0,bool enabled=true)115         counting_sink(OutputIterator& sink_, std::size_t count_ = 0
116               , bool enabled = true)
117           : count(count_), initial_count(count), prev_count(0), sink(sink_)
118         {
119             prev_count = sink.chain_counting(enabled ? this : NULL);
120         }
~counting_sink()121         ~counting_sink()
122         {
123             if (prev_count)           // propagate count
124                 prev_count->update_count(count-initial_count);
125             sink.chain_counting(prev_count);
126         }
127 
output()128         void output()
129         {
130             ++count;
131         }
get_count() const132         std::size_t get_count() const { return count; }
133 
134         // propagate count from embedded counters
update_count(std::size_t c)135         void update_count(std::size_t c)
136         {
137             count += c;
138         }
139 
140     private:
141         std::size_t count;
142         std::size_t initial_count;
143         counting_sink* prev_count;                // previous counter in chain
144         OutputIterator& sink;
145     };
146 
147     ///////////////////////////////////////////////////////////////////////////
148     template <typename OutputIterator>
149     struct counting_policy
150     {
151     public:
counting_policyboost::spirit::karma::detail::counting_policy152         counting_policy() : count(NULL) {}
counting_policyboost::spirit::karma::detail::counting_policy153         counting_policy(counting_policy const& rhs) : count(rhs.count) {}
154 
155         // functions related to counting
chain_countingboost::spirit::karma::detail::counting_policy156         counting_sink<OutputIterator>* chain_counting(
157             counting_sink<OutputIterator>* count_data)
158         {
159             counting_sink<OutputIterator>* prev_count = count;
160             count = count_data;
161             return prev_count;
162         }
163 
164         template <typename T>
outputboost::spirit::karma::detail::counting_policy165         void output(T const&)
166         {
167             // count characters, if appropriate
168             if (NULL != count)
169                 count->output();
170         }
171 
172     private:
173         counting_sink<OutputIterator>* count;      // for counting
174     };
175 
176     struct no_counting_policy
177     {
no_counting_policyboost::spirit::karma::detail::no_counting_policy178         no_counting_policy() {}
no_counting_policyboost::spirit::karma::detail::no_counting_policy179         no_counting_policy(no_counting_policy const&) {}
180 
181         template <typename T>
outputboost::spirit::karma::detail::no_counting_policy182         void output(T const& /*value*/) {}
183     };
184 
185     ///////////////////////////////////////////////////////////////////////////
186     //  The following classes are used to intercept the output into a buffer
187     //  allowing to do things like alignment, character escaping etc.
188     ///////////////////////////////////////////////////////////////////////////
189     class buffer_sink : boost::noncopyable
190     {
191        // wchar_t is only 16-bits on Windows. If BOOST_SPIRIT_UNICODE is
192        // defined, the character type is 32-bits wide so we need to make
193        // sure the buffer is at least that wide.
194 #if (defined(_MSC_VER) || defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2) && defined(BOOST_SPIRIT_UNICODE)
195        typedef spirit::char_encoding::unicode::char_type buffer_char_type;
196 #else
197        typedef wchar_t buffer_char_type;
198 #endif
199 
200     public:
buffer_sink()201         buffer_sink()
202           : width(0) {}
203 
~buffer_sink()204         ~buffer_sink()
205         {
206             tidy();
207         }
208 
enable(std::size_t width_)209         void enable(std::size_t width_)
210         {
211             tidy();             // release existing buffer
212             width = (width_ == std::size_t(-1)) ? 0 : width_;
213             buffer.reserve(width);
214         }
215 
tidy()216         void tidy()
217         {
218             buffer.clear();
219             width = 0;
220         }
221 
222         template <typename T>
output(T const & value)223         void output(T const& value)
224         {
225             BOOST_STATIC_ASSERT(sizeof(T) <= sizeof(buffer_char_type));
226             buffer.push_back(value);
227         }
228 
229         template <typename OutputIterator_>
copy(OutputIterator_ & sink,std::size_t maxwidth) const230         bool copy(OutputIterator_& sink, std::size_t maxwidth) const
231         {
232 #if defined(BOOST_MSVC)
233 #pragma warning(push)
234 #pragma warning(disable: 4267)
235 #endif
236             typename std::basic_string<buffer_char_type>::const_iterator end =
237                 buffer.begin() + (std::min)(buffer.size(), maxwidth);
238 
239 #if defined(BOOST_MSVC)
240 #pragma warning(disable: 4244) // conversion from 'x' to 'y', possible loss of data
241 #endif
242             std::copy(buffer.begin(), end, sink);
243 #if defined(BOOST_MSVC)
244 #pragma warning(pop)
245 #endif
246             return true;
247         }
248         template <typename RestIterator>
copy_rest(RestIterator & sink,std::size_t start_at) const249         bool copy_rest(RestIterator& sink, std::size_t start_at) const
250         {
251 #if defined(BOOST_MSVC)
252 #pragma warning(push)
253 #pragma warning(disable: 4267)
254 #endif
255             typename std::basic_string<buffer_char_type>::const_iterator begin =
256                 buffer.begin() + (std::min)(buffer.size(), start_at);
257 
258 #if defined(BOOST_MSVC)
259 #pragma warning(disable: 4244) // conversion from 'x' to 'y', possible loss of data
260 #endif
261             std::copy(begin, buffer.end(), sink);
262 #if defined(BOOST_MSVC)
263 #pragma warning(pop)
264 #endif
265             return true;
266         }
267 
buffer_size() const268         std::size_t buffer_size() const
269         {
270             return buffer.size();
271         }
272 
273     private:
274         std::size_t width;
275         std::basic_string<buffer_char_type> buffer;
276     };
277 
278     ///////////////////////////////////////////////////////////////////////////
279     struct buffering_policy
280     {
281     public:
buffering_policyboost::spirit::karma::detail::buffering_policy282         buffering_policy() : buffer(NULL) {}
buffering_policyboost::spirit::karma::detail::buffering_policy283         buffering_policy(buffering_policy const& rhs) : buffer(rhs.buffer) {}
284 
285         // functions related to buffering
chain_bufferingboost::spirit::karma::detail::buffering_policy286         buffer_sink* chain_buffering(buffer_sink* buffer_data)
287         {
288             buffer_sink* prev_buffer = buffer;
289             buffer = buffer_data;
290             return prev_buffer;
291         }
292 
293         template <typename T>
outputboost::spirit::karma::detail::buffering_policy294         bool output(T const& value)
295         {
296             // buffer characters, if appropriate
297             if (NULL != buffer) {
298                 buffer->output(value);
299                 return false;
300             }
301             return true;
302         }
303 
has_bufferboost::spirit::karma::detail::buffering_policy304         bool has_buffer() const { return NULL != buffer; }
305 
306     private:
307         buffer_sink* buffer;
308     };
309 
310     struct no_buffering_policy
311     {
no_buffering_policyboost::spirit::karma::detail::no_buffering_policy312         no_buffering_policy() {}
no_buffering_policyboost::spirit::karma::detail::no_buffering_policy313         no_buffering_policy(no_buffering_policy const&) {}
314 
315         template <typename T>
outputboost::spirit::karma::detail::no_buffering_policy316         bool output(T const& /*value*/)
317         {
318             return true;
319         }
320 
has_bufferboost::spirit::karma::detail::no_buffering_policy321         bool has_buffer() const { return false; }
322     };
323 
324     ///////////////////////////////////////////////////////////////////////////
325     //  forward declaration only
326     template <typename OutputIterator>
327     struct enable_buffering;
328 
329     template <typename OutputIterator, typename Properties
330       , typename Derived = unused_type>
331     class output_iterator;
332 
333     ///////////////////////////////////////////////////////////////////////////
334     template <typename Buffering, typename Counting, typename Tracking>
335     struct output_iterator_base : Buffering, Counting, Tracking
336     {
337         typedef Buffering buffering_policy;
338         typedef Counting counting_policy;
339         typedef Tracking tracking_policy;
340 
output_iterator_baseboost::spirit::karma::detail::output_iterator_base341         output_iterator_base() {}
output_iterator_baseboost::spirit::karma::detail::output_iterator_base342         output_iterator_base(output_iterator_base const& rhs)
343           : buffering_policy(rhs), counting_policy(rhs), tracking_policy(rhs)
344         {}
345 
346         template <typename T>
outputboost::spirit::karma::detail::output_iterator_base347         bool output(T const& value)
348         {
349             this->counting_policy::output(value);
350             this->tracking_policy::output(value);
351             return this->buffering_policy::output(value);
352         }
353     };
354 
355     template <typename Buffering, typename Counting, typename Tracking>
356     struct disabling_output_iterator : Buffering, Counting, Tracking
357     {
358         typedef Buffering buffering_policy;
359         typedef Counting counting_policy;
360         typedef Tracking tracking_policy;
361 
disabling_output_iteratorboost::spirit::karma::detail::disabling_output_iterator362         disabling_output_iterator() : do_output(true) {}
disabling_output_iteratorboost::spirit::karma::detail::disabling_output_iterator363         disabling_output_iterator(disabling_output_iterator const& rhs)
364           : buffering_policy(rhs), counting_policy(rhs), tracking_policy(rhs)
365           , do_output(rhs.do_output)
366         {}
367 
368         template <typename T>
outputboost::spirit::karma::detail::disabling_output_iterator369         bool output(T const& value)
370         {
371             if (!do_output)
372                 return false;
373 
374             this->counting_policy::output(value);
375             this->tracking_policy::output(value);
376             return this->buffering_policy::output(value);
377         }
378 
379         bool do_output;
380     };
381 
382     ///////////////////////////////////////////////////////////////////////////
383     template <typename OutputIterator, typename Properties, typename Derived>
384     struct make_output_iterator
385     {
386         // get the most derived type of this class
387         typedef typename mpl::if_<
388             traits::not_is_unused<Derived>, Derived
389           , output_iterator<OutputIterator, Properties, Derived>
390         >::type most_derived_type;
391 
392         static const generator_properties::enum_type properties = static_cast<generator_properties::enum_type>(Properties::value);
393 
394         typedef typename mpl::if_c<
395             (properties & generator_properties::tracking) ? true : false
396           , position_policy, no_position_policy
397         >::type tracking_type;
398 
399         typedef typename mpl::if_c<
400             (properties & generator_properties::buffering) ? true : false
401           , buffering_policy, no_buffering_policy
402         >::type buffering_type;
403 
404         typedef typename mpl::if_c<
405             (properties & generator_properties::counting) ? true : false
406           , counting_policy<most_derived_type>, no_counting_policy
407         >::type counting_type;
408 
409         typedef typename mpl::if_c<
410             (properties & generator_properties::disabling) ? true : false
411           , disabling_output_iterator<buffering_type, counting_type, tracking_type>
412           , output_iterator_base<buffering_type, counting_type, tracking_type>
413         >::type type;
414     };
415 
416     ///////////////////////////////////////////////////////////////////////////
417     //  Karma uses an output iterator wrapper for all output operations. This
418     //  is necessary to avoid the dreaded 'scanner business' problem, i.e. the
419     //  dependency of rules and grammars on the used output iterator.
420     //
421     //  By default the user supplied output iterator is wrapped inside an
422     //  instance of this internal output_iterator class.
423     //
424     //  This output_iterator class normally just forwards to the embedded user
425     //  supplied iterator. But it is possible to enable additional functionality
426     //  on demand, such as counting, buffering, and position tracking.
427     ///////////////////////////////////////////////////////////////////////////
428     template <typename OutputIterator, typename Properties, typename Derived>
429     class output_iterator
430       : public make_output_iterator<OutputIterator, Properties, Derived>::type
431     {
432     private:
433         // base iterator type
434         typedef typename make_output_iterator<
435             OutputIterator, Properties, Derived>::type base_iterator;
436 
437     public:
438         typedef std::output_iterator_tag iterator_category;
439         typedef void value_type;
440         typedef void difference_type;
441         typedef void pointer;
442         typedef void reference;
443 
output_iterator(OutputIterator & sink_)444         explicit output_iterator(OutputIterator& sink_)
445           : sink(&sink_)
446         {}
output_iterator(output_iterator const & rhs)447         output_iterator(output_iterator const& rhs)
448           : base_iterator(rhs), sink(rhs.sink)
449         {}
450 
operator *()451         output_iterator& operator*() { return *this; }
operator ++()452         output_iterator& operator++()
453         {
454             if (!this->base_iterator::has_buffer())
455                 ++(*sink);           // increment only if not buffering
456             return *this;
457         }
operator ++(int)458         output_iterator operator++(int)
459         {
460             if (!this->base_iterator::has_buffer()) {
461                 output_iterator t(*this);
462                 ++(*sink);
463                 return t;
464             }
465             return *this;
466         }
467 
468 #if defined(BOOST_MSVC)
469 // 'argument' : conversion from '...' to '...', possible loss of data
470 #pragma warning (push)
471 #pragma warning (disable: 4244)
472 #endif
473         template <typename T>
operator =(T const & value)474         void operator=(T const& value)
475         {
476             if (this->base_iterator::output(value))
477                 *(*sink) = value;
478         }
479 #if defined(BOOST_MSVC)
480 #pragma warning (pop)
481 #endif
482 
483         // plain output iterators are considered to be good all the time
good() const484         bool good() const { return true; }
485 
486         // allow to access underlying output iterator
base()487         OutputIterator& base() { return *sink; }
488 
489     protected:
490         // this is the wrapped user supplied output iterator
491         OutputIterator* sink;
492     };
493 
494     ///////////////////////////////////////////////////////////////////////////
495     template <typename T, typename Elem, typename Traits, typename Properties>
496     class output_iterator<karma::ostream_iterator<T, Elem, Traits>, Properties>
497       : public output_iterator<karma::ostream_iterator<T, Elem, Traits>, Properties
498           , output_iterator<karma::ostream_iterator<T, Elem, Traits>, Properties> >
499     {
500     private:
501         typedef output_iterator<karma::ostream_iterator<T, Elem, Traits>, Properties
502           , output_iterator<karma::ostream_iterator<T, Elem, Traits>, Properties>
503         > base_type;
504         typedef karma::ostream_iterator<T, Elem, Traits> base_iterator_type;
505         typedef std::basic_ostream<Elem, Traits> ostream_type;
506 
507     public:
output_iterator(base_iterator_type & sink)508         output_iterator(base_iterator_type& sink)
509           : base_type(sink) {}
510 
get_ostream()511         ostream_type& get_ostream() { return (*this->sink).get_ostream(); }
get_ostream() const512         ostream_type const& get_ostream() const { return (*this->sink).get_ostream(); }
513 
514         // expose good bit of underlying stream object
good() const515         bool good() const { return (*this->sink).get_ostream().good(); }
516     };
517 
518     ///////////////////////////////////////////////////////////////////////////
519     //  Helper class for exception safe enabling of character counting in the
520     //  output iterator
521     ///////////////////////////////////////////////////////////////////////////
522     template <typename OutputIterator>
523     struct enable_counting
524     {
enable_countingboost::spirit::karma::detail::enable_counting525         enable_counting(OutputIterator& sink_, std::size_t count = 0)
526           : count_data(sink_, count) {}
527 
528         // get number of characters counted since last enable
countboost::spirit::karma::detail::enable_counting529         std::size_t count() const
530         {
531             return count_data.get_count();
532         }
533 
534     private:
535         counting_sink<OutputIterator> count_data;              // for counting
536     };
537 
538     template <typename OutputIterator>
539     struct disable_counting
540     {
disable_countingboost::spirit::karma::detail::disable_counting541         disable_counting(OutputIterator& sink_)
542           : count_data(sink_, 0, false) {}
543 
544     private:
545         counting_sink<OutputIterator> count_data;
546     };
547 
548     ///////////////////////////////////////////////////////////////////////////
549     //  Helper class for exception safe enabling of character buffering in the
550     //  output iterator
551     ///////////////////////////////////////////////////////////////////////////
552     template <typename OutputIterator>
553     struct enable_buffering
554     {
enable_bufferingboost::spirit::karma::detail::enable_buffering555         enable_buffering(OutputIterator& sink_
556               , std::size_t width = std::size_t(-1))
557           : sink(sink_), prev_buffer(NULL), enabled(false)
558         {
559             buffer_data.enable(width);
560             prev_buffer = sink.chain_buffering(&buffer_data);
561             enabled = true;
562         }
~enable_bufferingboost::spirit::karma::detail::enable_buffering563         ~enable_buffering()
564         {
565             disable();
566         }
567 
568         // reset buffer chain to initial state
disableboost::spirit::karma::detail::enable_buffering569         void disable()
570         {
571             if (enabled) {
572                 BOOST_VERIFY(&buffer_data == sink.chain_buffering(prev_buffer));
573                 enabled = false;
574             }
575         }
576 
577         // copy to the underlying sink whatever is in the local buffer
buffer_copyboost::spirit::karma::detail::enable_buffering578         bool buffer_copy(std::size_t maxwidth = std::size_t(-1)
579           , bool disable_ = true)
580         {
581             if (disable_)
582                 disable();
583             return buffer_data.copy(sink, maxwidth) && sink.good();
584         }
585 
586         // return number of characters stored in the buffer
buffer_sizeboost::spirit::karma::detail::enable_buffering587         std::size_t buffer_size() const
588         {
589             return buffer_data.buffer_size();
590         }
591 
592         // copy to the remaining characters to the specified sink
593         template <typename RestIterator>
buffer_copy_restboost::spirit::karma::detail::enable_buffering594         bool buffer_copy_rest(RestIterator& sink, std::size_t start_at = 0) const
595         {
596             return buffer_data.copy_rest(sink, start_at);
597         }
598 
599         // copy the contents to the given output iterator
600         template <typename OutputIterator_>
buffer_copy_toboost::spirit::karma::detail::enable_buffering601         bool buffer_copy_to(OutputIterator_& sink
602           , std::size_t maxwidth = std::size_t(-1)) const
603         {
604             return buffer_data.copy(sink, maxwidth);
605         }
606 
607     private:
608         OutputIterator& sink;
609         buffer_sink buffer_data;    // for buffering
610         buffer_sink* prev_buffer;   // previous buffer in chain
611         bool enabled;
612     };
613 
614     ///////////////////////////////////////////////////////////////////////////
615     //  Helper class for exception safe disabling of output
616     ///////////////////////////////////////////////////////////////////////////
617     template <typename OutputIterator>
618     struct disable_output
619     {
disable_outputboost::spirit::karma::detail::disable_output620         disable_output(OutputIterator& sink_)
621           : sink(sink_), prev_do_output(sink.do_output)
622         {
623             sink.do_output = false;
624         }
~disable_outputboost::spirit::karma::detail::disable_output625         ~disable_output()
626         {
627             sink.do_output = prev_do_output;
628         }
629 
630         OutputIterator& sink;
631         bool prev_do_output;
632     };
633 
634     ///////////////////////////////////////////////////////////////////////////
635     template <typename Sink>
sink_is_good(Sink const &)636     bool sink_is_good(Sink const&)
637     {
638         return true;      // the general case is always good
639     }
640 
641     template <typename OutputIterator, typename Derived>
sink_is_good(output_iterator<OutputIterator,Derived> const & sink)642     bool sink_is_good(output_iterator<OutputIterator, Derived> const& sink)
643     {
644         return sink.good(); // our own output iterators are handled separately
645     }
646 
647 }}}}
648 
649 #endif
650 
651