1 // 2 // Copyright (c) 2015 Artyom Beilis (Tonkikh) 3 // 4 // Distributed under the Boost Software License, Version 1.0. (See 5 // accompanying file LICENSE_1_0.txt or copy at 6 // http://www.boost.org/LICENSE_1_0.txt) 7 // 8 #ifndef BOOST_LOCALE_GENERIC_CODECVT_HPP 9 #define BOOST_LOCALE_GENERIC_CODECVT_HPP 10 11 #include <boost/locale/utf.hpp> 12 #include <boost/cstdint.hpp> 13 #include <boost/static_assert.hpp> 14 #include <locale> 15 16 namespace boost { 17 namespace locale { 18 19 #ifndef BOOST_LOCALE_DOXYGEN 20 // 21 // Make sure that mbstate can keep 16 bit of UTF-16 sequence 22 // 23 BOOST_STATIC_ASSERT(sizeof(std::mbstate_t)>=2); 24 #endif 25 26 #if defined(_MSC_VER) && _MSC_VER < 1700 27 // up to MSVC 11 (2012) do_length is non-standard it counts wide characters instead of narrow and does not change mbstate 28 #define BOOST_LOCALE_DO_LENGTH_MBSTATE_CONST 29 #endif 30 31 /// 32 /// \brief A base class that used to define constants for generic_codecvt 33 /// 34 class generic_codecvt_base { 35 public: 36 /// 37 /// Initail state for converting to or from unicode code points, used by initial_state in derived classes 38 /// 39 enum initial_convertion_state { 40 to_unicode_state, ///< The state would be used by to_unicode functions 41 from_unicode_state ///< The state would be used by from_unicode functions 42 }; 43 }; 44 45 /// 46 /// \brief Geneneric generic codecvt facet, various stateless encodings to UTF-16 and UTF-32 using wchar_t, char32_t and char16_t 47 /// 48 /// Implementations should dervide from this class defining itself as CodecvtImpl and provide following members 49 /// 50 /// - `state_type` - a type of special object that allows to store intermediate cached data, for example `iconv_t` descriptor 51 /// - `state_type initial_state(generic_codecvt_base::initial_convertion_state direction) const` - member function that creates initial state 52 /// - `int max_encoding_length() const` - a maximal length that one Unicode code point is represented, for UTF-8 for example it is 4 from ISO-8859-1 it is 1 53 /// - `utf::code_point to_unicode(state_type &state,char const *&begin,char const *end)` - extract first code point from the text in range [begin,end), in case of success begin would point to the next character sequence to be encoded to next code point, in case of incomplete sequence - utf::incomplete shell be returned, and in case of invalid input sequence utf::illegal shell be returned and begin would remain unmodified 54 /// - `utf::code_point from_unicode(state_type &state,utf::code_point u,char *begin,char const *end)` - convert a unicode code point `u` into a character seqnece at [begin,end). Return the length of the sequence in case of success, utf::incomplete in case of not enough room to encode the code point of utf::illegal in case conversion can not be performed 55 /// 56 /// 57 /// For example implementaion of codecvt for latin1/ISO-8859-1 character set 58 /// 59 /// \code 60 /// 61 /// template<typename CharType> 62 /// class latin1_codecvt :boost::locale::generic_codecvt<CharType,latin1_codecvt<CharType> > 63 /// { 64 /// public: 65 /// 66 /// /* Standard codecvt constructor */ 67 /// latin1_codecvt(size_t refs = 0) : boost::locale::generic_codecvt<CharType,latin1_codecvt<CharType> >(refs) 68 /// { 69 /// } 70 /// 71 /// /* State is unused but required by generic_codecvt */ 72 /// struct state_type {}; 73 /// 74 /// state_type initial_state(generic_codecvt_base::initial_convertion_state /*unused*/) const 75 /// { 76 /// return state_type(); 77 /// } 78 /// 79 /// int max_encoding_length() const 80 /// { 81 /// return 1; 82 /// } 83 /// 84 /// boost::locale::utf::code_point to_unicode(state_type &,char const *&begin,char const *end) const 85 /// { 86 /// if(begin == end) 87 /// return boost::locale::utf::incomplete; 88 /// return *begin++; 89 /// } 90 /// 91 /// boost::locale::utf::code_point from_unicode(state_type &,boost::locale::utf::code_point u,char *begin,char const *end) const 92 /// { 93 /// if(u >= 256) 94 /// return boost::locale::utf::illegal; 95 /// if(begin == end) 96 /// return boost::locale::utf::incomplete; 97 /// *begin = u; 98 /// return 1; 99 /// } 100 /// }; 101 /// 102 /// \endcode 103 /// 104 /// When external tools used for encoding conversion, the `state_type` is useful to save objects used for conversions. For example, 105 /// icu::UConverter can be saved in such a state for an efficient use: 106 /// 107 /// \code 108 /// template<typename CharType> 109 /// class icu_codecvt :boost::locale::generic_codecvt<CharType,icu_codecvt<CharType> > 110 /// { 111 /// public: 112 /// 113 /// /* Standard codecvt constructor */ 114 /// icu_codecvt(std::string const &name,refs = 0) : 115 /// boost::locale::generic_codecvt<CharType,latin1_codecvt<CharType> >(refs) 116 /// { ... } 117 /// 118 /// /* State is unused but required by generic_codecvt */ 119 /// struct std::unique_ptr<UConverter,void (*)(UConverter*)> state_type; 120 /// 121 /// state_type &&initial_state(generic_codecvt_base::initial_convertion_state /*unused*/) const 122 /// { 123 /// UErrorCode err = U_ZERO_ERROR; 124 /// state_type ptr(ucnv_safeClone(converter_,0,0,&err,ucnv_close); 125 /// return std::move(ptr); 126 /// } 127 /// 128 /// boost::locale::utf::code_point to_unicode(state_type &ptr,char const *&begin,char const *end) const 129 /// { 130 /// UErrorCode err = U_ZERO_ERROR; 131 /// boost::locale::utf::code_point cp = ucnv_getNextUChar(ptr.get(),&begin,end,&err); 132 /// ... 133 /// } 134 /// ... 135 /// }; 136 /// \endcode 137 /// 138 /// 139 template<typename CharType,typename CodecvtImpl,int CharSize=sizeof(CharType)> 140 class generic_codecvt; 141 142 /// 143 /// \brief UTF-16 to/from UTF-8 codecvt facet to use with char16_t or wchar_t on Windows 144 /// 145 /// Note in order to fit the requirements of usability by std::wfstream it uses mbstate_t 146 /// to handle intermediate states in handling of variable length UTF-16 sequences 147 /// 148 /// Its member functions implement standard virtual functions of basic codecvt 149 /// 150 template<typename CharType,typename CodecvtImpl> 151 class generic_codecvt<CharType,CodecvtImpl,2> : public std::codecvt<CharType,char,std::mbstate_t>, public generic_codecvt_base 152 { 153 public: 154 155 typedef CharType uchar; 156 generic_codecvt(size_t refs=0)157 generic_codecvt(size_t refs = 0) : 158 std::codecvt<CharType,char,std::mbstate_t>(refs) 159 { 160 } implementation() const161 CodecvtImpl const &implementation() const 162 { 163 return *static_cast<CodecvtImpl const *>(this); 164 } 165 166 protected: 167 168 do_unshift(std::mbstate_t & s,char * from,char *,char * & next) const169 virtual std::codecvt_base::result do_unshift(std::mbstate_t &s,char *from,char * /*to*/,char *&next) const 170 { 171 boost::uint16_t &state = *reinterpret_cast<boost::uint16_t *>(&s); 172 #ifdef DEBUG_CODECVT 173 std::cout << "Entering unshift " << std::hex << state << std::dec << std::endl; 174 #endif 175 if(state != 0) 176 return std::codecvt_base::error; 177 next=from; 178 return std::codecvt_base::ok; 179 } do_encoding() const180 virtual int do_encoding() const throw() 181 { 182 return 0; 183 } do_max_length() const184 virtual int do_max_length() const throw() 185 { 186 return implementation().max_encoding_length(); 187 } do_always_noconv() const188 virtual bool do_always_noconv() const throw() 189 { 190 return false; 191 } 192 193 virtual int do_length(std::mbstate_t const & std_state,char const * from,char const * from_end,size_t max) const194 do_length( std::mbstate_t 195 #ifdef BOOST_LOCALE_DO_LENGTH_MBSTATE_CONST 196 const 197 #endif 198 &std_state, 199 char const *from, 200 char const *from_end, 201 size_t max) const 202 { 203 #ifndef BOOST_LOCALE_DO_LENGTH_MBSTATE_CONST 204 char const *save_from = from; 205 boost::uint16_t &state = *reinterpret_cast<boost::uint16_t *>(&std_state); 206 #else 207 size_t save_max = max; 208 boost::uint16_t state = *reinterpret_cast<boost::uint16_t const *>(&std_state); 209 #endif 210 211 typedef typename CodecvtImpl::state_type state_type; 212 state_type cvt_state = implementation().initial_state(generic_codecvt_base::to_unicode_state); 213 while(max > 0 && from < from_end){ 214 char const *prev_from = from; 215 boost::uint32_t ch=implementation().to_unicode(cvt_state,from,from_end); 216 if(ch==boost::locale::utf::incomplete || ch==boost::locale::utf::illegal) { 217 from = prev_from; 218 break; 219 } 220 max --; 221 if(ch > 0xFFFF) { 222 if(state == 0) { 223 from = prev_from; 224 state = 1; 225 } 226 else { 227 state = 0; 228 } 229 } 230 } 231 #ifndef BOOST_LOCALE_DO_LENGTH_MBSTATE_CONST 232 return from - save_from; 233 #else 234 return save_max - max; 235 #endif 236 } 237 238 239 virtual std::codecvt_base::result do_in(std::mbstate_t & std_state,char const * from,char const * from_end,char const * & from_next,uchar * to,uchar * to_end,uchar * & to_next) const240 do_in( std::mbstate_t &std_state, 241 char const *from, 242 char const *from_end, 243 char const *&from_next, 244 uchar *to, 245 uchar *to_end, 246 uchar *&to_next) const 247 { 248 std::codecvt_base::result r=std::codecvt_base::ok; 249 250 // mbstate_t is POD type and should be initialized to 0 (i.a. state = stateT()) 251 // according to standard. We use it to keep a flag 0/1 for surrogate pair writing 252 // 253 // if 0 no code above >0xFFFF observed, of 1 a code above 0xFFFF observerd 254 // and first pair is written, but no input consumed 255 boost::uint16_t &state = *reinterpret_cast<boost::uint16_t *>(&std_state); 256 typedef typename CodecvtImpl::state_type state_type; 257 state_type cvt_state = implementation().initial_state(generic_codecvt_base::to_unicode_state); 258 while(to < to_end && from < from_end) 259 { 260 #ifdef DEBUG_CODECVT 261 std::cout << "Entering IN--------------" << std::endl; 262 std::cout << "State " << std::hex << state <<std::endl; 263 std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl; 264 #endif 265 char const *from_saved = from; 266 267 uint32_t ch=implementation().to_unicode(cvt_state,from,from_end); 268 269 if(ch==boost::locale::utf::illegal) { 270 from = from_saved; 271 r=std::codecvt_base::error; 272 break; 273 } 274 if(ch==boost::locale::utf::incomplete) { 275 from = from_saved; 276 r=std::codecvt_base::partial; 277 break; 278 } 279 // Normal codepoints go direcly to stream 280 if(ch <= 0xFFFF) { 281 *to++=ch; 282 } 283 else { 284 // for other codepoints we do following 285 // 286 // 1. We can't consume our input as we may find ourselfs 287 // in state where all input consumed but not all output written,i.e. only 288 // 1st pair is written 289 // 2. We only write first pair and mark this in the state, we also revert back 290 // the from pointer in order to make sure this codepoint would be read 291 // once again and then we would consume our input together with writing 292 // second surrogate pair 293 ch-=0x10000; 294 boost::uint16_t vh = ch >> 10; 295 boost::uint16_t vl = ch & 0x3FF; 296 boost::uint16_t w1 = vh + 0xD800; 297 boost::uint16_t w2 = vl + 0xDC00; 298 if(state == 0) { 299 from = from_saved; 300 *to++ = w1; 301 state = 1; 302 } 303 else { 304 *to++ = w2; 305 state = 0; 306 } 307 } 308 } 309 from_next=from; 310 to_next=to; 311 if(r == std::codecvt_base::ok && (from!=from_end || state!=0)) 312 r = std::codecvt_base::partial; 313 #ifdef DEBUG_CODECVT 314 std::cout << "Returning "; 315 switch(r) { 316 case std::codecvt_base::ok: 317 std::cout << "ok" << std::endl; 318 break; 319 case std::codecvt_base::partial: 320 std::cout << "partial" << std::endl; 321 break; 322 case std::codecvt_base::error: 323 std::cout << "error" << std::endl; 324 break; 325 default: 326 std::cout << "other" << std::endl; 327 break; 328 } 329 std::cout << "State " << std::hex << state <<std::endl; 330 std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl; 331 #endif 332 return r; 333 } 334 335 virtual std::codecvt_base::result do_out(std::mbstate_t & std_state,uchar const * from,uchar const * from_end,uchar const * & from_next,char * to,char * to_end,char * & to_next) const336 do_out( std::mbstate_t &std_state, 337 uchar const *from, 338 uchar const *from_end, 339 uchar const *&from_next, 340 char *to, 341 char *to_end, 342 char *&to_next) const 343 { 344 std::codecvt_base::result r=std::codecvt_base::ok; 345 // mbstate_t is POD type and should be initialized to 0 (i.a. state = stateT()) 346 // according to standard. We assume that sizeof(mbstate_t) >=2 in order 347 // to be able to store first observerd surrogate pair 348 // 349 // State: state!=0 - a first surrogate pair was observerd (state = first pair), 350 // we expect the second one to come and then zero the state 351 /// 352 boost::uint16_t &state = *reinterpret_cast<boost::uint16_t *>(&std_state); 353 typedef typename CodecvtImpl::state_type state_type; 354 state_type cvt_state = implementation().initial_state(generic_codecvt_base::from_unicode_state); 355 while(to < to_end && from < from_end) 356 { 357 #ifdef DEBUG_CODECVT 358 std::cout << "Entering OUT --------------" << std::endl; 359 std::cout << "State " << std::hex << state <<std::endl; 360 std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl; 361 #endif 362 boost::uint32_t ch=0; 363 if(state != 0) { 364 // if the state idecates that 1st surrogate pair was written 365 // we should make sure that the second one that comes is actually 366 // second surrogate 367 boost::uint16_t w1 = state; 368 boost::uint16_t w2 = *from; 369 // we don't forward from as writing may fail to incomplete or 370 // partial conversion 371 if(0xDC00 <= w2 && w2<=0xDFFF) { 372 boost::uint16_t vh = w1 - 0xD800; 373 boost::uint16_t vl = w2 - 0xDC00; 374 ch=((uint32_t(vh) << 10) | vl) + 0x10000; 375 } 376 else { 377 // Invalid surrogate 378 r=std::codecvt_base::error; 379 break; 380 } 381 } 382 else { 383 ch = *from; 384 if(0xD800 <= ch && ch<=0xDBFF) { 385 // if this is a first surrogate pair we put 386 // it into the state and consume it, note we don't 387 // go forward as it should be illegal so we increase 388 // the from pointer manually 389 state = ch; 390 from++; 391 continue; 392 } 393 else if(0xDC00 <= ch && ch<=0xDFFF) { 394 // if we observe second surrogate pair and 395 // first only may be expected we should break from the loop with error 396 // as it is illegal input 397 r=std::codecvt_base::error; 398 break; 399 } 400 } 401 if(!boost::locale::utf::is_valid_codepoint(ch)) { 402 r=std::codecvt_base::error; 403 break; 404 } 405 boost::uint32_t len = implementation().from_unicode(cvt_state,ch,to,to_end); 406 if(len == boost::locale::utf::incomplete) { 407 r=std::codecvt_base::partial; 408 break; 409 } 410 else if(len == boost::locale::utf::illegal) { 411 r=std::codecvt_base::error; 412 break; 413 } 414 else 415 to+= len; 416 state = 0; 417 from++; 418 } 419 from_next=from; 420 to_next=to; 421 if(r==std::codecvt_base::ok && from!=from_end) 422 r = std::codecvt_base::partial; 423 #ifdef DEBUG_CODECVT 424 std::cout << "Returning "; 425 switch(r) { 426 case std::codecvt_base::ok: 427 std::cout << "ok" << std::endl; 428 break; 429 case std::codecvt_base::partial: 430 std::cout << "partial" << std::endl; 431 break; 432 case std::codecvt_base::error: 433 std::cout << "error" << std::endl; 434 break; 435 default: 436 std::cout << "other" << std::endl; 437 break; 438 } 439 std::cout << "State " << std::hex << state <<std::endl; 440 std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl; 441 #endif 442 return r; 443 } 444 445 }; 446 447 /// 448 /// \brief UTF-32 to/from UTF-8 codecvt facet to use with char32_t or wchar_t on POSIX platforms 449 /// 450 /// Its member functions implement standard virtual functions of basic codecvt. 451 /// mbstate_t is not used for UTF-32 handling due to fixed length encoding 452 /// 453 template<typename CharType,typename CodecvtImpl> 454 class generic_codecvt<CharType,CodecvtImpl,4> : public std::codecvt<CharType,char,std::mbstate_t>, public generic_codecvt_base 455 { 456 public: 457 typedef CharType uchar; 458 generic_codecvt(size_t refs=0)459 generic_codecvt(size_t refs = 0) : 460 std::codecvt<CharType,char,std::mbstate_t>(refs) 461 { 462 } 463 implementation() const464 CodecvtImpl const &implementation() const 465 { 466 return *static_cast<CodecvtImpl const *>(this); 467 } 468 469 protected: 470 do_unshift(std::mbstate_t &,char * from,char *,char * & next) const471 virtual std::codecvt_base::result do_unshift(std::mbstate_t &/*s*/,char *from,char * /*to*/,char *&next) const 472 { 473 next=from; 474 return std::codecvt_base::ok; 475 } do_encoding() const476 virtual int do_encoding() const throw() 477 { 478 return 0; 479 } do_max_length() const480 virtual int do_max_length() const throw() 481 { 482 return implementation().max_encoding_length(); 483 } do_always_noconv() const484 virtual bool do_always_noconv() const throw() 485 { 486 return false; 487 } 488 489 virtual int do_length(std::mbstate_t const &,char const * from,char const * from_end,size_t max) const490 do_length( std::mbstate_t 491 #ifdef BOOST_LOCALE_DO_LENGTH_MBSTATE_CONST 492 const 493 #endif 494 &/*state*/, 495 char const *from, 496 char const *from_end, 497 size_t max) const 498 { 499 #ifndef BOOST_LOCALE_DO_LENGTH_MBSTATE_CONST 500 char const *start_from = from; 501 #else 502 size_t save_max = max; 503 #endif 504 typedef typename CodecvtImpl::state_type state_type; 505 state_type cvt_state = implementation().initial_state(generic_codecvt_base::to_unicode_state); 506 while(max > 0 && from < from_end){ 507 char const *save_from = from; 508 boost::uint32_t ch=implementation().to_unicode(cvt_state,from,from_end); 509 if(ch==boost::locale::utf::incomplete || ch==boost::locale::utf::illegal) { 510 from = save_from; 511 break; 512 } 513 max--; 514 } 515 #ifndef BOOST_LOCALE_DO_LENGTH_MBSTATE_CONST 516 return from - start_from; 517 #else 518 return save_max - max; 519 #endif 520 } 521 522 523 virtual std::codecvt_base::result do_in(std::mbstate_t &,char const * from,char const * from_end,char const * & from_next,uchar * to,uchar * to_end,uchar * & to_next) const524 do_in( std::mbstate_t &/*state*/, 525 char const *from, 526 char const *from_end, 527 char const *&from_next, 528 uchar *to, 529 uchar *to_end, 530 uchar *&to_next) const 531 { 532 std::codecvt_base::result r=std::codecvt_base::ok; 533 534 // mbstate_t is POD type and should be initialized to 0 (i.a. state = stateT()) 535 // according to standard. We use it to keep a flag 0/1 for surrogate pair writing 536 // 537 // if 0 no code above >0xFFFF observed, of 1 a code above 0xFFFF observerd 538 // and first pair is written, but no input consumed 539 typedef typename CodecvtImpl::state_type state_type; 540 state_type cvt_state = implementation().initial_state(generic_codecvt_base::to_unicode_state); 541 while(to < to_end && from < from_end) 542 { 543 #ifdef DEBUG_CODECVT 544 std::cout << "Entering IN--------------" << std::endl; 545 std::cout << "State " << std::hex << state <<std::endl; 546 std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl; 547 #endif 548 char const *from_saved = from; 549 550 uint32_t ch=implementation().to_unicode(cvt_state,from,from_end); 551 552 if(ch==boost::locale::utf::illegal) { 553 r=std::codecvt_base::error; 554 from = from_saved; 555 break; 556 } 557 if(ch==boost::locale::utf::incomplete) { 558 r=std::codecvt_base::partial; 559 from=from_saved; 560 break; 561 } 562 *to++=ch; 563 } 564 from_next=from; 565 to_next=to; 566 if(r == std::codecvt_base::ok && from!=from_end) 567 r = std::codecvt_base::partial; 568 #ifdef DEBUG_CODECVT 569 std::cout << "Returning "; 570 switch(r) { 571 case std::codecvt_base::ok: 572 std::cout << "ok" << std::endl; 573 break; 574 case std::codecvt_base::partial: 575 std::cout << "partial" << std::endl; 576 break; 577 case std::codecvt_base::error: 578 std::cout << "error" << std::endl; 579 break; 580 default: 581 std::cout << "other" << std::endl; 582 break; 583 } 584 std::cout << "State " << std::hex << state <<std::endl; 585 std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl; 586 #endif 587 return r; 588 } 589 590 virtual std::codecvt_base::result do_out(std::mbstate_t &,uchar const * from,uchar const * from_end,uchar const * & from_next,char * to,char * to_end,char * & to_next) const591 do_out( std::mbstate_t &/*std_state*/, 592 uchar const *from, 593 uchar const *from_end, 594 uchar const *&from_next, 595 char *to, 596 char *to_end, 597 char *&to_next) const 598 { 599 std::codecvt_base::result r=std::codecvt_base::ok; 600 typedef typename CodecvtImpl::state_type state_type; 601 state_type cvt_state = implementation().initial_state(generic_codecvt_base::from_unicode_state); 602 while(to < to_end && from < from_end) 603 { 604 #ifdef DEBUG_CODECVT 605 std::cout << "Entering OUT --------------" << std::endl; 606 std::cout << "State " << std::hex << state <<std::endl; 607 std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl; 608 #endif 609 boost::uint32_t ch=0; 610 ch = *from; 611 if(!boost::locale::utf::is_valid_codepoint(ch)) { 612 r=std::codecvt_base::error; 613 break; 614 } 615 boost::uint32_t len = implementation().from_unicode(cvt_state,ch,to,to_end); 616 if(len == boost::locale::utf::incomplete) { 617 r=std::codecvt_base::partial; 618 break; 619 } 620 else if(len == boost::locale::utf::illegal) { 621 r=std::codecvt_base::error; 622 break; 623 } 624 to+=len; 625 from++; 626 } 627 from_next=from; 628 to_next=to; 629 if(r==std::codecvt_base::ok && from!=from_end) 630 r = std::codecvt_base::partial; 631 #ifdef DEBUG_CODECVT 632 std::cout << "Returning "; 633 switch(r) { 634 case std::codecvt_base::ok: 635 std::cout << "ok" << std::endl; 636 break; 637 case std::codecvt_base::partial: 638 std::cout << "partial" << std::endl; 639 break; 640 case std::codecvt_base::error: 641 std::cout << "error" << std::endl; 642 break; 643 default: 644 std::cout << "other" << std::endl; 645 break; 646 } 647 std::cout << "State " << std::hex << state <<std::endl; 648 std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl; 649 #endif 650 return r; 651 } 652 }; 653 654 655 template<typename CharType,typename CodecvtImpl> 656 class generic_codecvt<CharType,CodecvtImpl,1> : public std::codecvt<CharType,char,std::mbstate_t>, public generic_codecvt_base 657 { 658 public: 659 typedef CharType uchar; 660 implementation() const661 CodecvtImpl const &implementation() const 662 { 663 return *static_cast<CodecvtImpl const *>(this); 664 } 665 generic_codecvt(size_t refs=0)666 generic_codecvt(size_t refs = 0) : std::codecvt<char,char,std::mbstate_t>(refs) 667 { 668 } 669 }; 670 671 } // locale 672 } // namespace boost 673 674 #endif 675 /// 676 // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 677