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