format.hpp 16 KB

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