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_NUMERIC_UTILS_FEB_23_2007_0841PM) 7 #define BOOST_SPIRIT_KARMA_NUMERIC_UTILS_FEB_23_2007_0841PM 8 9 #if defined(_MSC_VER) 10 #pragma once 11 #endif 12 13 #include <boost/config.hpp> 14 #include <boost/config/no_tr1/cmath.hpp> 15 #include <boost/limits.hpp> 16 17 #include <boost/math/special_functions/fpclassify.hpp> 18 #include <boost/type_traits/is_integral.hpp> 19 #include <boost/spirit/home/support/char_class.hpp> 20 #include <boost/spirit/home/support/unused.hpp> 21 #include <boost/spirit/home/support/numeric_traits.hpp> 22 #include <boost/spirit/home/support/detail/pow10.hpp> 23 #include <boost/spirit/home/support/detail/sign.hpp> 24 #include <boost/spirit/home/karma/detail/generate_to.hpp> 25 #include <boost/spirit/home/karma/detail/string_generate.hpp> 26 27 /////////////////////////////////////////////////////////////////////////////// 28 // 29 // The value BOOST_KARMA_NUMERICS_LOOP_UNROLL specifies, how to unroll the 30 // integer string generation loop (see below). 31 // 32 // Set the value to some integer in between 0 (no unrolling) and the 33 // largest expected generated integer string length (complete unrolling). 34 // If not specified, this value defaults to 6. 35 // 36 /////////////////////////////////////////////////////////////////////////////// 37 #if !defined(BOOST_KARMA_NUMERICS_LOOP_UNROLL) 38 #define BOOST_KARMA_NUMERICS_LOOP_UNROLL 6 39 #endif 40 41 #if BOOST_KARMA_NUMERICS_LOOP_UNROLL < 0 42 #error "Please set the BOOST_KARMA_NUMERICS_LOOP_UNROLL to a non-negative value!" 43 #endif 44 45 namespace boost { namespace spirit { namespace traits 46 { 47 /////////////////////////////////////////////////////////////////////// 48 // 49 // return the absolute value from a given number, avoiding over- and 50 // underflow 51 // 52 /////////////////////////////////////////////////////////////////////// 53 template <typename T, typename Enable/* = void*/> 54 struct absolute_value 55 { 56 typedef T type; callboost::spirit::traits::absolute_value57 static T call (T n) 58 { 59 // allow for ADL to find the correct overloads for fabs 60 using namespace std; 61 return fabs(n); 62 } 63 }; 64 65 #define BOOST_SPIRIT_ABSOLUTE_VALUE(signedtype, unsignedtype) \ 66 template <> \ 67 struct absolute_value<signedtype> \ 68 { \ 69 typedef unsignedtype type; \ 70 static type call(signedtype n) \ 71 { \ 72 /* implementation is well-defined for one's complement, */ \ 73 /* two's complement, and signed magnitude architectures */ \ 74 /* by the C++ Standard. [conv.integral] [expr.unary.op] */ \ 75 return (n >= 0) ? static_cast<type>(n) \ 76 : -static_cast<type>(n); \ 77 } \ 78 } \ 79 /**/ 80 #define BOOST_SPIRIT_ABSOLUTE_VALUE_UNSIGNED(unsignedtype) \ 81 template <> \ 82 struct absolute_value<unsignedtype> \ 83 { \ 84 typedef unsignedtype type; \ 85 static type call(unsignedtype n) \ 86 { \ 87 return n; \ 88 } \ 89 } \ 90 /**/ 91 92 #if defined(BOOST_MSVC) 93 # pragma warning(push) 94 // unary minus operator applied to unsigned type, result still unsigned 95 # pragma warning(disable: 4146) 96 #endif 97 BOOST_SPIRIT_ABSOLUTE_VALUE(signed char, unsigned char); 98 BOOST_SPIRIT_ABSOLUTE_VALUE(char, unsigned char); 99 BOOST_SPIRIT_ABSOLUTE_VALUE(short, unsigned short); 100 BOOST_SPIRIT_ABSOLUTE_VALUE(int, unsigned int); 101 BOOST_SPIRIT_ABSOLUTE_VALUE(long, unsigned long); 102 BOOST_SPIRIT_ABSOLUTE_VALUE_UNSIGNED(unsigned char); 103 BOOST_SPIRIT_ABSOLUTE_VALUE_UNSIGNED(unsigned short); 104 BOOST_SPIRIT_ABSOLUTE_VALUE_UNSIGNED(unsigned int); 105 BOOST_SPIRIT_ABSOLUTE_VALUE_UNSIGNED(unsigned long); 106 #ifdef BOOST_HAS_LONG_LONG 107 BOOST_SPIRIT_ABSOLUTE_VALUE(boost::long_long_type, boost::ulong_long_type); 108 BOOST_SPIRIT_ABSOLUTE_VALUE_UNSIGNED(boost::ulong_long_type); 109 #endif 110 #if defined(BOOST_MSVC) 111 # pragma warning(pop) 112 #endif 113 114 #undef BOOST_SPIRIT_ABSOLUTE_VALUE 115 #undef BOOST_SPIRIT_ABSOLUTE_VALUE_UNSIGNED 116 117 template <> 118 struct absolute_value<float> 119 { 120 typedef float type; callboost::spirit::traits::absolute_value121 static type call(float n) 122 { 123 return (spirit::detail::signbit)(n) ? -n : n; 124 } 125 }; 126 127 template <> 128 struct absolute_value<double> 129 { 130 typedef double type; callboost::spirit::traits::absolute_value131 static type call(double n) 132 { 133 return (spirit::detail::signbit)(n) ? -n : n; 134 } 135 }; 136 137 template <> 138 struct absolute_value<long double> 139 { 140 typedef long double type; callboost::spirit::traits::absolute_value141 static type call(long double n) 142 { 143 return (spirit::detail::signbit)(n) ? -n : n; 144 } 145 }; 146 147 // specialization for pointers 148 template <typename T> 149 struct absolute_value<T*> 150 { 151 typedef std::size_t type; callboost::spirit::traits::absolute_value152 static type call (T* p) 153 { 154 return std::size_t(p); 155 } 156 }; 157 158 template <typename T> 159 inline typename absolute_value<T>::type get_absolute_value(T n)160 get_absolute_value(T n) 161 { 162 return absolute_value<T>::call(n); 163 } 164 165 /////////////////////////////////////////////////////////////////////// 166 template <typename T, typename Enable/* = void*/> 167 struct is_negative 168 { callboost::spirit::traits::is_negative169 static bool call(T n) 170 { 171 return (n < 0) ? true : false; 172 } 173 }; 174 175 template <> 176 struct is_negative<float> 177 { callboost::spirit::traits::is_negative178 static bool call(float n) 179 { 180 return (spirit::detail::signbit)(n) ? true : false; 181 } 182 }; 183 184 template <> 185 struct is_negative<double> 186 { callboost::spirit::traits::is_negative187 static bool call(double n) 188 { 189 return (spirit::detail::signbit)(n) ? true : false; 190 } 191 }; 192 193 template <> 194 struct is_negative<long double> 195 { callboost::spirit::traits::is_negative196 static bool call(long double n) 197 { 198 return (spirit::detail::signbit)(n) ? true : false; 199 } 200 }; 201 202 template <typename T> test_negative(T n)203 inline bool test_negative(T n) 204 { 205 return is_negative<T>::call(n); 206 } 207 208 /////////////////////////////////////////////////////////////////////// 209 template <typename T, typename Enable/* = void*/> 210 struct is_zero 211 { callboost::spirit::traits::is_zero212 static bool call(T n) 213 { 214 return (n == 0) ? true : false; 215 } 216 }; 217 218 template <> 219 struct is_zero<float> 220 { callboost::spirit::traits::is_zero221 static bool call(float n) 222 { 223 return (math::fpclassify)(n) == FP_ZERO; 224 } 225 }; 226 227 template <> 228 struct is_zero<double> 229 { callboost::spirit::traits::is_zero230 static bool call(double n) 231 { 232 return (math::fpclassify)(n) == FP_ZERO; 233 } 234 }; 235 236 template <> 237 struct is_zero<long double> 238 { callboost::spirit::traits::is_zero239 static bool call(long double n) 240 { 241 return (math::fpclassify)(n) == FP_ZERO; 242 } 243 }; 244 245 template <typename T> test_zero(T n)246 inline bool test_zero(T n) 247 { 248 return is_zero<T>::call(n); 249 } 250 251 /////////////////////////////////////////////////////////////////////// 252 template <typename T, typename Enable/* = void*/> 253 struct is_nan 254 { callboost::spirit::traits::is_nan255 static bool call(T n) 256 { 257 // NaN numbers are not equal to anything 258 return (n != n) ? true : false; 259 } 260 }; 261 262 template <> 263 struct is_nan<float> 264 { callboost::spirit::traits::is_nan265 static bool call(float n) 266 { 267 return (math::fpclassify)(n) == FP_NAN; 268 } 269 }; 270 271 template <> 272 struct is_nan<double> 273 { callboost::spirit::traits::is_nan274 static bool call(double n) 275 { 276 return (math::fpclassify)(n) == FP_NAN; 277 } 278 }; 279 280 template <> 281 struct is_nan<long double> 282 { callboost::spirit::traits::is_nan283 static bool call(long double n) 284 { 285 return (math::fpclassify)(n) == FP_NAN; 286 } 287 }; 288 289 template <typename T> test_nan(T n)290 inline bool test_nan(T n) 291 { 292 return is_nan<T>::call(n); 293 } 294 295 /////////////////////////////////////////////////////////////////////// 296 template <typename T, typename Enable/* = void*/> 297 struct is_infinite 298 { callboost::spirit::traits::is_infinite299 static bool call(T n) 300 { 301 return std::numeric_limits<T>::has_infinity 302 && n == std::numeric_limits<T>::infinity(); 303 } 304 }; 305 306 template <> 307 struct is_infinite<float> 308 { callboost::spirit::traits::is_infinite309 static bool call(float n) 310 { 311 return (math::fpclassify)(n) == FP_INFINITE; 312 } 313 }; 314 315 template <> 316 struct is_infinite<double> 317 { callboost::spirit::traits::is_infinite318 static bool call(double n) 319 { 320 return (math::fpclassify)(n) == FP_INFINITE; 321 } 322 }; 323 324 template <> 325 struct is_infinite<long double> 326 { callboost::spirit::traits::is_infinite327 static bool call(long double n) 328 { 329 return (math::fpclassify)(n) == FP_INFINITE; 330 } 331 }; 332 333 template <typename T> test_infinite(T n)334 inline bool test_infinite(T n) 335 { 336 return is_infinite<T>::call(n); 337 } 338 339 /////////////////////////////////////////////////////////////////////// 340 struct cast_to_long 341 { callboost::spirit::traits::cast_to_long342 static long call(float n, mpl::false_) 343 { 344 return static_cast<long>(std::floor(n)); 345 } 346 callboost::spirit::traits::cast_to_long347 static long call(double n, mpl::false_) 348 { 349 return static_cast<long>(std::floor(n)); 350 } 351 callboost::spirit::traits::cast_to_long352 static long call(long double n, mpl::false_) 353 { 354 return static_cast<long>(std::floor(n)); 355 } 356 357 template <typename T> callboost::spirit::traits::cast_to_long358 static long call(T n, mpl::false_) 359 { 360 // allow for ADL to find the correct overload for floor and 361 // lround 362 using namespace std; 363 return lround(floor(n)); 364 } 365 366 template <typename T> callboost::spirit::traits::cast_to_long367 static long call(T n, mpl::true_) 368 { 369 return static_cast<long>(n); 370 } 371 372 template <typename T> callboost::spirit::traits::cast_to_long373 static long call(T n) 374 { 375 return call(n, mpl::bool_<is_integral<T>::value>()); 376 } 377 }; 378 379 /////////////////////////////////////////////////////////////////////// 380 struct truncate_to_long 381 { callboost::spirit::traits::truncate_to_long382 static long call(float n, mpl::false_) 383 { 384 return test_negative(n) ? static_cast<long>(std::ceil(n)) : 385 static_cast<long>(std::floor(n)); 386 } 387 callboost::spirit::traits::truncate_to_long388 static long call(double n, mpl::false_) 389 { 390 return test_negative(n) ? static_cast<long>(std::ceil(n)) : 391 static_cast<long>(std::floor(n)); 392 } 393 callboost::spirit::traits::truncate_to_long394 static long call(long double n, mpl::false_) 395 { 396 return test_negative(n) ? static_cast<long>(std::ceil(n)) : 397 static_cast<long>(std::floor(n)); 398 } 399 400 template <typename T> callboost::spirit::traits::truncate_to_long401 static long call(T n, mpl::false_) 402 { 403 // allow for ADL to find the correct overloads for ltrunc 404 using namespace std; 405 return ltrunc(n); 406 } 407 408 template <typename T> callboost::spirit::traits::truncate_to_long409 static long call(T n, mpl::true_) 410 { 411 return static_cast<long>(n); 412 } 413 414 template <typename T> callboost::spirit::traits::truncate_to_long415 static long call(T n) 416 { 417 return call(n, mpl::bool_<is_integral<T>::value>()); 418 } 419 }; 420 421 /////////////////////////////////////////////////////////////////////// 422 // 423 // Traits class for radix specific number conversion 424 // 425 // Convert a digit from binary representation to character 426 // representation: 427 // 428 // static int call(unsigned n); 429 // 430 /////////////////////////////////////////////////////////////////////// 431 namespace detail 432 { 433 template <typename CharEncoding, typename Tag, bool radix_less_than_10> 434 struct convert_digit 435 { callboost::spirit::traits::detail::convert_digit436 static int call(unsigned n) 437 { 438 if (n <= 9) 439 return n + '0'; 440 441 using spirit::char_class::convert; 442 return convert<CharEncoding>::to(Tag(), n - 10 + 'a'); 443 } 444 }; 445 446 template <> 447 struct convert_digit<unused_type, unused_type, false> 448 { callboost::spirit::traits::detail::convert_digit449 static int call(unsigned n) 450 { 451 if (n <= 9) 452 return n + '0'; 453 return n - 10 + 'a'; 454 } 455 }; 456 457 template <typename CharEncoding, typename Tag> 458 struct convert_digit<CharEncoding, Tag, true> 459 { callboost::spirit::traits::detail::convert_digit460 static int call(unsigned n) 461 { 462 return n + '0'; 463 } 464 }; 465 } 466 467 template <unsigned Radix, typename CharEncoding, typename Tag> 468 struct convert_digit 469 : detail::convert_digit<CharEncoding, Tag, (Radix <= 10) ? true : false> 470 {}; 471 472 /////////////////////////////////////////////////////////////////////// 473 template <unsigned Radix> 474 struct divide 475 { 476 template <typename T> callboost::spirit::traits::divide477 static T call(T& n, mpl::true_) 478 { 479 return n / Radix; 480 } 481 482 template <typename T> callboost::spirit::traits::divide483 static T call(T& n, mpl::false_) 484 { 485 // Allow ADL to find the correct overload for floor 486 using namespace std; 487 return floor(n / Radix); 488 } 489 490 template <typename T> callboost::spirit::traits::divide491 static T call(T& n, T const&, int) 492 { 493 return call(n, mpl::bool_<is_integral<T>::value>()); 494 } 495 496 template <typename T> callboost::spirit::traits::divide497 static T call(T& n) 498 { 499 return call(n, mpl::bool_<is_integral<T>::value>()); 500 } 501 }; 502 503 // specialization for division by 10 504 template <> 505 struct divide<10> 506 { 507 template <typename T> callboost::spirit::traits::divide508 static T call(T& n, T, int, mpl::true_) 509 { 510 return n / 10; 511 } 512 513 template <typename T> callboost::spirit::traits::divide514 static T call(T, T& num, int exp, mpl::false_) 515 { 516 // Allow ADL to find the correct overload for floor 517 using namespace std; 518 return floor(num / spirit::traits::pow10<T>(exp)); 519 } 520 521 template <typename T> callboost::spirit::traits::divide522 static T call(T& n, T& num, int exp) 523 { 524 return call(n, num, exp, mpl::bool_<is_integral<T>::value>()); 525 } 526 527 template <typename T> callboost::spirit::traits::divide528 static T call(T& n) 529 { 530 return call(n, n, 1, mpl::bool_<is_integral<T>::value>()); 531 } 532 }; 533 534 /////////////////////////////////////////////////////////////////////// 535 template <unsigned Radix> 536 struct remainder 537 { 538 template <typename T> callboost::spirit::traits::remainder539 static long call(T n, mpl::true_) 540 { 541 // this cast is safe since we know the result is not larger 542 // than Radix 543 return static_cast<long>(n % Radix); 544 } 545 546 template <typename T> callboost::spirit::traits::remainder547 static long call(T n, mpl::false_) 548 { 549 // Allow ADL to find the correct overload for fmod 550 using namespace std; 551 return cast_to_long::call(fmod(n, T(Radix))); 552 } 553 554 template <typename T> callboost::spirit::traits::remainder555 static long call(T n) 556 { 557 return call(n, mpl::bool_<is_integral<T>::value>()); 558 } 559 }; 560 }}} 561 562 namespace boost { namespace spirit { namespace karma 563 { 564 /////////////////////////////////////////////////////////////////////////// 565 // 566 // The int_inserter template takes care of the integer to string 567 // conversion. If specified, the loop is unrolled for better performance. 568 // 569 // Set the value BOOST_KARMA_NUMERICS_LOOP_UNROLL to some integer in 570 // between 0 (no unrolling) and the largest expected generated integer 571 // string length (complete unrolling). 572 // If not specified, this value defaults to 6. 573 // 574 /////////////////////////////////////////////////////////////////////////// 575 #define BOOST_KARMA_NUMERICS_INNER_LOOP_PREFIX(z, x, data) \ 576 if (!traits::test_zero(n)) { \ 577 int ch = radix_type::call(remainder_type::call(n)); \ 578 n = divide_type::call(n, num, ++exp); \ 579 /**/ 580 581 #define BOOST_KARMA_NUMERICS_INNER_LOOP_SUFFIX(z, x, data) \ 582 *sink = char(ch); \ 583 ++sink; \ 584 } \ 585 /**/ 586 587 template < 588 unsigned Radix, typename CharEncoding = unused_type 589 , typename Tag = unused_type> 590 struct int_inserter 591 { 592 typedef traits::convert_digit<Radix, CharEncoding, Tag> radix_type; 593 typedef traits::divide<Radix> divide_type; 594 typedef traits::remainder<Radix> remainder_type; 595 596 template <typename OutputIterator, typename T> 597 static bool callboost::spirit::karma::int_inserter598 call(OutputIterator& sink, T n, T& num, int exp) 599 { 600 // remainder_type::call returns n % Radix 601 int ch = radix_type::call(remainder_type::call(n)); 602 n = divide_type::call(n, num, ++exp); 603 604 BOOST_PP_REPEAT( 605 BOOST_KARMA_NUMERICS_LOOP_UNROLL, 606 BOOST_KARMA_NUMERICS_INNER_LOOP_PREFIX, _); 607 608 if (!traits::test_zero(n)) 609 call(sink, n, num, exp); 610 611 BOOST_PP_REPEAT( 612 BOOST_KARMA_NUMERICS_LOOP_UNROLL, 613 BOOST_KARMA_NUMERICS_INNER_LOOP_SUFFIX, _); 614 615 *sink = char(ch); 616 ++sink; 617 return true; 618 } 619 620 // Common code for integer string representations 621 template <typename OutputIterator, typename T> 622 static bool callboost::spirit::karma::int_inserter623 call(OutputIterator& sink, T n) 624 { 625 return call(sink, n, n, 0); 626 } 627 628 private: 629 // helper function returning the biggest number representable either in 630 // a boost::long_long_type (if this does exist) or in a plain long 631 // otherwise 632 #if defined(BOOST_HAS_LONG_LONG) 633 typedef boost::long_long_type biggest_long_type; 634 #else 635 typedef long biggest_long_type; 636 #endif 637 max_longboost::spirit::karma::int_inserter638 static biggest_long_type max_long() 639 { 640 return (std::numeric_limits<biggest_long_type>::max)(); 641 } 642 643 public: 644 // Specialization for doubles and floats, falling back to long integers 645 // for representable values. These specializations speed up formatting 646 // of floating point numbers considerably as all the required 647 // arithmetics will be executed using integral data types. 648 template <typename OutputIterator> 649 static bool callboost::spirit::karma::int_inserter650 call(OutputIterator& sink, long double n) 651 { 652 if (std::fabs(n) < max_long()) 653 { 654 biggest_long_type l((biggest_long_type)n); 655 return call(sink, l, l, 0); 656 } 657 return call(sink, n, n, 0); 658 } 659 template <typename OutputIterator> 660 static bool callboost::spirit::karma::int_inserter661 call(OutputIterator& sink, double n) 662 { 663 if (std::fabs(n) < max_long()) 664 { 665 biggest_long_type l((biggest_long_type)n); 666 return call(sink, l, l, 0); 667 } 668 return call(sink, n, n, 0); 669 } 670 template <typename OutputIterator> 671 static bool callboost::spirit::karma::int_inserter672 call(OutputIterator& sink, float n) 673 { 674 if (std::fabs(n) < max_long()) 675 { 676 biggest_long_type l((biggest_long_type)n); 677 return call(sink, l, l, 0); 678 } 679 return call(sink, n, n, 0); 680 } 681 }; 682 683 #undef BOOST_KARMA_NUMERICS_INNER_LOOP_PREFIX 684 #undef BOOST_KARMA_NUMERICS_INNER_LOOP_SUFFIX 685 686 /////////////////////////////////////////////////////////////////////////// 687 // 688 // The uint_inserter template takes care of the conversion of any integer 689 // to a string, while interpreting the number as an unsigned type. 690 // 691 /////////////////////////////////////////////////////////////////////////// 692 template < 693 unsigned Radix, typename CharEncoding = unused_type 694 , typename Tag = unused_type> 695 struct uint_inserter : int_inserter<Radix, CharEncoding, Tag> 696 { 697 typedef int_inserter<Radix, CharEncoding, Tag> base_type; 698 699 // Common code for integer string representations 700 template <typename OutputIterator, typename T> 701 static bool callboost::spirit::karma::uint_inserter702 call(OutputIterator& sink, T const& n) 703 { 704 typedef typename traits::absolute_value<T>::type type; 705 type un = type(n); 706 return base_type::call(sink, un, un, 0); 707 } 708 }; 709 710 /////////////////////////////////////////////////////////////////////////// 711 // 712 // The sign_inserter template generates a sign for a given numeric value. 713 // 714 // The parameter forcesign allows to generate a sign even for positive 715 // numbers. 716 // 717 /////////////////////////////////////////////////////////////////////////// 718 struct sign_inserter 719 { 720 template <typename OutputIterator> 721 static bool call_noforceboost::spirit::karma::sign_inserter722 call_noforce(OutputIterator& sink, bool is_zero, bool is_negative, 723 bool sign_if_zero) 724 { 725 // generate a sign for negative numbers only 726 if (is_negative || (is_zero && sign_if_zero)) { 727 *sink = '-'; 728 ++sink; 729 } 730 return true; 731 } 732 733 template <typename OutputIterator> 734 static bool call_forceboost::spirit::karma::sign_inserter735 call_force(OutputIterator& sink, bool is_zero, bool is_negative, 736 bool sign_if_zero) 737 { 738 // generate a sign for all numbers except zero 739 if (!is_zero || sign_if_zero) 740 *sink = is_negative ? '-' : '+'; 741 else 742 *sink = ' '; 743 744 ++sink; 745 return true; 746 } 747 748 template <typename OutputIterator> 749 static bool callboost::spirit::karma::sign_inserter750 call(OutputIterator& sink, bool is_zero, bool is_negative 751 , bool forcesign, bool sign_if_zero = false) 752 { 753 return forcesign ? 754 call_force(sink, is_zero, is_negative, sign_if_zero) : 755 call_noforce(sink, is_zero, is_negative, sign_if_zero); 756 } 757 }; 758 759 /////////////////////////////////////////////////////////////////////////// 760 // These are helper functions for the real policies allowing to generate 761 // a single character and a string 762 /////////////////////////////////////////////////////////////////////////// 763 template <typename CharEncoding = unused_type, typename Tag = unused_type> 764 struct char_inserter 765 { 766 template <typename OutputIterator, typename Char> callboost::spirit::karma::char_inserter767 static bool call(OutputIterator& sink, Char c) 768 { 769 return detail::generate_to(sink, c, CharEncoding(), Tag()); 770 } 771 }; 772 773 template <typename CharEncoding = unused_type, typename Tag = unused_type> 774 struct string_inserter 775 { 776 template <typename OutputIterator, typename String> callboost::spirit::karma::string_inserter777 static bool call(OutputIterator& sink, String str) 778 { 779 return detail::string_generate(sink, str, CharEncoding(), Tag()); 780 } 781 }; 782 783 }}} 784 785 #endif 786