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