format.hpp 18 KB

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