1 // 2 // Copyright (c) 2009-2011 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_FORMAT_HPP_INCLUDED 9 #define BOOST_LOCALE_FORMAT_HPP_INCLUDED 10 11 #include <boost/locale/config.hpp> 12 #ifdef BOOST_MSVC 13 # pragma warning(push) 14 # pragma warning(disable : 4275 4251 4231 4660) 15 #endif 16 #include <boost/locale/message.hpp> 17 #include <boost/locale/formatting.hpp> 18 #include <boost/locale/hold_ptr.hpp> 19 20 #include <sstream> 21 22 23 namespace boost { 24 namespace locale { 25 26 /// 27 /// \defgroup format Format 28 /// 29 /// This module provides printf like functionality integrated into iostreams and suitable for localization 30 /// 31 /// @{ 32 /// 33 34 /// \cond INTERNAL 35 namespace details { 36 37 template<typename CharType> 38 struct formattible { 39 typedef std::basic_ostream<CharType> stream_type; 40 typedef void (*writer_type)(stream_type &output,void const *ptr); 41 formattibleboost::locale::details::formattible42 formattible() : 43 pointer_(0), 44 writer_(&formattible::void_write) 45 { 46 } 47 formattibleboost::locale::details::formattible48 formattible(formattible const &other) : 49 pointer_(other.pointer_), 50 writer_(other.writer_) 51 { 52 } 53 operator =boost::locale::details::formattible54 formattible const &operator=(formattible const &other) 55 { 56 if(this != &other) { 57 pointer_=other.pointer_; 58 writer_=other.writer_; 59 } 60 return *this; 61 } 62 63 template<typename Type> formattibleboost::locale::details::formattible64 formattible(Type const &value) 65 { 66 pointer_ = static_cast<void const *>(&value); 67 writer_ = &write<Type>; 68 } 69 70 template<typename Type> operator =boost::locale::details::formattible71 formattible const &operator=(Type const &other) 72 { 73 *this = formattible(other); 74 return *this; 75 } 76 operator <<(stream_type & out,formattible const & fmt)77 friend stream_type &operator<<(stream_type &out,formattible const &fmt) 78 { 79 fmt.writer_(out,fmt.pointer_); 80 return out; 81 } 82 83 private: void_writeboost::locale::details::formattible84 static void void_write(stream_type &output,void const * /*ptr*/) 85 { 86 CharType empty_string[1]={0}; 87 output<<empty_string; 88 } 89 90 template<typename Type> writeboost::locale::details::formattible91 static void write(stream_type &output,void const *ptr) 92 { 93 output << *static_cast<Type const *>(ptr); 94 } 95 96 void const *pointer_; 97 writer_type writer_; 98 }; // formattible 99 100 class BOOST_LOCALE_DECL format_parser { 101 public: 102 format_parser(std::ios_base &ios,void *,void (*imbuer)(void *,std::locale const &)); 103 ~format_parser(); 104 105 unsigned get_position(); 106 107 void set_one_flag(std::string const &key,std::string const &value); 108 109 template<typename CharType> set_flag_with_str(std::string const & key,std::basic_string<CharType> const & value)110 void set_flag_with_str(std::string const &key,std::basic_string<CharType> const &value) 111 { 112 if(key=="ftime" || key=="strftime") { 113 as::strftime(ios_); 114 ios_info::get(ios_).date_time_pattern(value); 115 } 116 } 117 void restore(); 118 private: 119 void imbue(std::locale const &); 120 format_parser(format_parser const &); 121 void operator=(format_parser const &); 122 123 std::ios_base &ios_; 124 struct data; 125 hold_ptr<data> d; 126 }; 127 128 } 129 130 /// \endcond 131 132 /// 133 /// \brief a printf like class that allows type-safe and locale aware message formatting 134 /// 135 /// This class creates a formatted message similar to printf or boost::format and receives 136 /// formatted entries via operator %. 137 /// 138 /// For example 139 /// \code 140 /// cout << format("Hello {1}, you are {2} years old") % name % age << endl; 141 /// \endcode 142 /// 143 /// Formatting is enclosed between curly brackets \c { \c } and defined by a comma separated list of flags in the format key[=value] 144 /// value may also be text included between single quotes \c ' that is used for special purposes where inclusion of non-ASCII 145 /// text is allowed 146 /// 147 /// Including of literal \c { and \c } is possible by specifying double brackets \c {{ and \c }} accordingly. 148 /// 149 /// 150 /// For example: 151 /// 152 /// \code 153 /// cout << format("The height of water at {1,time} is {2,num=fixed,precision=3}") % time % height; 154 /// \endcode 155 /// 156 /// The special key -- a number without a value defines the position of an input parameter. 157 /// List of keys: 158 /// - \c [0-9]+ -- digits, the index of a formatted parameter -- mandatory key. 159 /// - \c num or \c number -- format a number. Optional values are: 160 /// - \c hex -- display hexadecimal number 161 /// - \c oct -- display in octal format 162 /// - \c sci or \c scientific -- display in scientific format 163 /// - \c fix or \c fixed -- display in fixed format 164 /// . 165 /// For example \c number=sci 166 /// - \c cur or \c currency -- format currency. Optional values are: 167 /// 168 /// - \c iso -- display using ISO currency symbol. 169 /// - \c nat or \c national -- display using national currency symbol. 170 /// . 171 /// - \c per or \c percent -- format percent value. 172 /// - \c date, \c time , \c datetime or \c dt -- format date, time or date and time. Optional values are: 173 /// - \c s or \c short -- display in short format 174 /// - \c m or \c medium -- display in medium format. 175 /// - \c l or \c long -- display in long format. 176 /// - \c f or \c full -- display in full format. 177 /// . 178 /// - \c ftime with string (quoted) parameter -- display as with \c strftime see, \c as::ftime manipulator 179 /// - \c spell or \c spellout -- spell the number. 180 /// - \c ord or \c ordinal -- format ordinal number (1st, 2nd... etc) 181 /// - \c left or \c < -- align to left. 182 /// - \c right or \c > -- align to right. 183 /// - \c width or \c w -- set field width (requires parameter). 184 /// - \c precision or \c p -- set precision (requires parameter). 185 /// - \c locale -- with parameter -- switch locale for current operation. This command generates locale 186 /// with formatting facets giving more fine grained control of formatting. For example: 187 /// \code 188 /// cout << format("Today {1,date} ({1,date,locale=he_IL.UTF-8@calendar=hebrew,date} Hebrew Date)") % date; 189 /// \endcode 190 /// - \c timezone or \c tz -- the name of the timezone to display the time in. For example:\n 191 /// \code 192 /// cout << format("Time is: Local {1,time}, ({1,time,tz=EET} Eastern European Time)") % date; 193 /// \endcode 194 /// - \c local - display the time in local time 195 /// - \c gmt - display the time in UTC time scale 196 /// \code 197 /// cout << format("Local time is: {1,time,local}, universal time is {1,time,gmt}") % time; 198 /// \endcode 199 /// 200 /// 201 /// Invalid formatting strings are slightly ignored. This would prevent from translator 202 /// to crash the program in unexpected location. 203 /// 204 template<typename CharType> 205 class basic_format { 206 public: 207 typedef CharType char_type; ///< Underlying character type 208 typedef basic_message<char_type> message_type; ///< The translation message type 209 /// \cond INTERNAL 210 typedef details::formattible<CharType> formattible_type; 211 /// \endcond 212 213 typedef std::basic_string<CharType> string_type; ///< string type for this type of character 214 typedef std::basic_ostream<CharType> stream_type; ///< output stream type for this type of character 215 216 217 /// 218 /// Create a format class for \a format_string 219 /// basic_format(string_type format_string)220 basic_format(string_type format_string) : 221 format_(format_string), 222 translate_(false), 223 parameters_count_(0) 224 { 225 } 226 /// 227 /// Create a format class using message \a trans. The message if translated first according 228 /// to the rules of target locale and then interpreted as format string 229 /// basic_format(message_type const & trans)230 basic_format(message_type const &trans) : 231 message_(trans), 232 translate_(true), 233 parameters_count_(0) 234 { 235 } 236 237 /// 238 /// Add new parameter to format list. The object should be a type 239 /// with defined expression out << object where \c out is \c std::basic_ostream. 240 /// 241 template<typename Formattible> operator %(Formattible const & object)242 basic_format &operator % (Formattible const &object) 243 { 244 add(formattible_type(object)); 245 return *this; 246 } 247 248 /// 249 /// Format a string using a locale \a loc 250 /// str(std::locale const & loc=std::locale ()) const251 string_type str(std::locale const &loc = std::locale()) const 252 { 253 std::basic_ostringstream<CharType> buffer; 254 buffer.imbue(loc); 255 write(buffer); 256 return buffer.str(); 257 } 258 259 /// 260 /// write a formatted string to output stream \a out using out's locale 261 /// write(stream_type & out) const262 void write(stream_type &out) const 263 { 264 string_type format; 265 if(translate_) 266 format = message_.str(out.getloc(),ios_info::get(out).domain_id()); 267 else 268 format = format_; 269 270 format_output(out,format); 271 272 } 273 274 275 private: 276 277 class format_guard { 278 public: format_guard(details::format_parser & fmt)279 format_guard(details::format_parser &fmt) : 280 fmt_(&fmt), 281 restored_(false) 282 { 283 } restore()284 void restore() 285 { 286 if(restored_) 287 return; 288 fmt_->restore(); 289 restored_ = true; 290 } ~format_guard()291 ~format_guard() 292 { 293 try { 294 restore(); 295 } 296 catch(...) { 297 } 298 } 299 private: 300 details::format_parser *fmt_; 301 bool restored_; 302 }; 303 format_output(stream_type & out,string_type const & sformat) const304 void format_output(stream_type &out,string_type const &sformat) const 305 { 306 char_type obrk='{'; 307 char_type cbrk='}'; 308 char_type eq='='; 309 char_type comma=','; 310 char_type quote='\''; 311 312 size_t pos = 0; 313 size_t size=sformat.size(); 314 CharType const *format=sformat.c_str(); 315 while(format[pos]!=0) { 316 if(format[pos] != obrk) { 317 if(format[pos]==cbrk && format[pos+1]==cbrk) { 318 out << cbrk; 319 pos+=2; 320 } 321 else { 322 out<<format[pos]; 323 pos++; 324 } 325 continue; 326 } 327 328 if(pos+1 < size && format[pos+1]==obrk) { 329 out << obrk; 330 pos+=2; 331 continue; 332 } 333 pos++; 334 335 details::format_parser fmt(out,static_cast<void *>(&out),&basic_format::imbue_locale); 336 337 format_guard guard(fmt); 338 339 while(pos < size) { 340 std::string key; 341 std::string svalue; 342 string_type value; 343 bool use_svalue = true; 344 for(;format[pos];pos++) { 345 char_type c=format[pos]; 346 if(c==comma || c==eq || c==cbrk) 347 break; 348 else { 349 key+=static_cast<char>(c); 350 } 351 } 352 353 if(format[pos]==eq) { 354 pos++; 355 if(format[pos]==quote) { 356 pos++; 357 use_svalue = false; 358 while(format[pos]) { 359 if(format[pos]==quote) { 360 if(format[pos+1]==quote) { 361 value+=quote; 362 pos+=2; 363 } 364 else { 365 pos++; 366 break; 367 } 368 } 369 else { 370 value+=format[pos]; 371 pos++; 372 } 373 } 374 } 375 else { 376 char_type c; 377 while((c=format[pos])!=0 && c!=comma && c!=cbrk) { 378 svalue+=static_cast<char>(c); 379 pos++; 380 } 381 } 382 } 383 384 if(use_svalue) { 385 fmt.set_one_flag(key,svalue); 386 } 387 else 388 fmt.set_flag_with_str(key,value); 389 390 if(format[pos]==comma) { 391 pos++; 392 continue; 393 } 394 else if(format[pos]==cbrk) { 395 unsigned position = fmt.get_position(); 396 out << get(position); 397 guard.restore(); 398 pos++; 399 break; 400 } 401 else { 402 guard.restore(); 403 break; 404 } 405 } 406 } 407 } 408 409 410 // 411 // Non-copyable 412 // 413 basic_format(basic_format const &other); 414 void operator=(basic_format const &other); 415 add(formattible_type const & param)416 void add(formattible_type const ¶m) 417 { 418 if(parameters_count_ >= base_params_) 419 ext_params_.push_back(param); 420 else 421 parameters_[parameters_count_] = param; 422 parameters_count_++; 423 } 424 get(unsigned id) const425 formattible_type get(unsigned id) const 426 { 427 if(id >= parameters_count_) 428 return formattible_type(); 429 else if(id >= base_params_) 430 return ext_params_[id - base_params_]; 431 else 432 return parameters_[id]; 433 } 434 imbue_locale(void * ptr,std::locale const & l)435 static void imbue_locale(void *ptr,std::locale const &l) 436 { 437 reinterpret_cast<stream_type *>(ptr)->imbue(l); 438 } 439 440 441 442 static unsigned const base_params_ = 8; 443 444 message_type message_; 445 string_type format_; 446 bool translate_; 447 448 449 formattible_type parameters_[base_params_]; 450 unsigned parameters_count_; 451 std::vector<formattible_type> ext_params_; 452 }; 453 454 /// 455 /// Write formatted message to stream. 456 /// 457 /// This operator actually causes actual text formatting. It uses the locale of \a out stream 458 /// 459 template<typename CharType> operator <<(std::basic_ostream<CharType> & out,basic_format<CharType> const & fmt)460 std::basic_ostream<CharType> &operator<<(std::basic_ostream<CharType> &out,basic_format<CharType> const &fmt) 461 { 462 fmt.write(out); 463 return out; 464 } 465 466 467 /// 468 /// Definition of char based format 469 /// 470 typedef basic_format<char> format; 471 472 /// 473 /// Definition of wchar_t based format 474 /// 475 typedef basic_format<wchar_t> wformat; 476 477 #ifdef BOOST_LOCALE_ENABLE_CHAR16_T 478 /// 479 /// Definition of char16_t based format 480 /// 481 typedef basic_format<char16_t> u16format; 482 #endif 483 484 #ifdef BOOST_LOCALE_ENABLE_CHAR32_T 485 /// 486 /// Definition of char32_t based format 487 /// 488 typedef basic_format<char32_t> u32format; 489 #endif 490 491 /// 492 /// @} 493 /// 494 495 } 496 } 497 498 #ifdef BOOST_MSVC 499 #pragma warning(pop) 500 #endif 501 502 #endif 503 504 /// 505 /// \example hello.cpp 506 /// 507 /// Basic example of using various functions provided by this library 508 /// 509 /// \example whello.cpp 510 /// 511 /// Basic example of using various functions with wide strings provided by this library 512 /// 513 /// 514 515 // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 516 517