Boost.Locale
format.hpp
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 
7 #ifndef BOOST_LOCALE_FORMAT_HPP_INCLUDED
8 #define BOOST_LOCALE_FORMAT_HPP_INCLUDED
9 
10 #include <boost/locale/config.hpp>
11 #include <boost/locale/formatting.hpp>
12 #include <boost/locale/hold_ptr.hpp>
13 #include <boost/locale/message.hpp>
14 #include <sstream>
15 #include <stdexcept>
16 #include <vector>
17 
18 #ifdef BOOST_MSVC
19 # pragma warning(push)
20 # pragma warning(disable : 4275 4251 4231 4660)
21 #endif
22 
23 namespace boost { namespace locale {
24 
30 
32  namespace detail {
33 
34  template<typename CharType>
35  struct formattible {
36  typedef std::basic_ostream<CharType> stream_type;
37  typedef void (*writer_type)(stream_type& output, const void* ptr);
38 
39  formattible() : pointer_(0), writer_(&formattible::void_write) {}
40 
41  formattible(const formattible& other) = default;
42  formattible(formattible&& other) = default;
43  formattible& operator=(const formattible& other) = default;
44  formattible& operator=(formattible&& other) = default;
45 
46  template<typename Type>
47  explicit formattible(const Type& value)
48  {
49  pointer_ = static_cast<const void*>(&value);
50  writer_ = &write<Type>;
51  }
52 
53  friend stream_type& operator<<(stream_type& out, const formattible& fmt)
54  {
55  fmt.writer_(out, fmt.pointer_);
56  return out;
57  }
58 
59  private:
60  static void void_write(stream_type& output, const void* /*ptr*/)
61  {
62  CharType empty_string[1] = {0};
63  output << empty_string;
64  }
65 
66  template<typename Type>
67  static void write(stream_type& output, const void* ptr)
68  {
69  output << *static_cast<const Type*>(ptr);
70  }
71 
72  const void* pointer_;
73  writer_type writer_;
74  }; // formattible
75 
76  class BOOST_LOCALE_DECL format_parser {
77  public:
78  format_parser(std::ios_base& ios, void*, void (*imbuer)(void*, const std::locale&));
79  ~format_parser();
80  format_parser(const format_parser&) = delete;
81  format_parser& operator=(const format_parser&) = delete;
82 
83  unsigned get_position();
84 
85  void set_one_flag(const std::string& key, const std::string& value);
86 
87  template<typename CharType>
88  void set_flag_with_str(const std::string& key, const std::basic_string<CharType>& value)
89  {
90  if(key == "ftime" || key == "strftime") {
91  as::strftime(ios_);
92  ios_info::get(ios_).date_time_pattern(value);
93  }
94  }
95  void restore();
96 
97  private:
98  void imbue(const std::locale&);
99 
100  std::ios_base& ios_;
101  struct data;
102  hold_ptr<data> d;
103  };
104 
105  } // namespace detail
106 
108 
179  template<typename CharType>
180  class basic_format {
181  public:
182  typedef CharType char_type;
184  typedef detail::formattible<CharType> formattible_type;
187 
188  typedef std::basic_string<CharType> string_type;
189  typedef std::basic_ostream<CharType> stream_type;
190 
192  basic_format(const string_type& format_string) : format_(format_string), translate_(false), parameters_count_(0)
193  {}
196  basic_format(const message_type& trans) : message_(trans), translate_(true), parameters_count_(0) {}
197 
199  basic_format(const basic_format& other) = delete;
200  void operator=(const basic_format& other) = delete;
203  message_(std::move(other.message_)), format_(std::move(other.format_)), translate_(other.translate_),
204  parameters_count_(0)
205  {
206  if(other.parameters_count_)
207  throw std::invalid_argument("Can't move a basic_format with bound parameters");
208  }
209  basic_format& operator=(basic_format&& other)
210  {
211  if(other.parameters_count_)
212  throw std::invalid_argument("Can't move a basic_format with bound parameters");
213  message_ = std::move(other.message_);
214  format_ = std::move(other.format_);
215  translate_ = other.translate_;
216  parameters_count_ = 0;
217  ext_params_.clear();
218  return *this;
219  }
220 
235  template<typename Formattible>
236  basic_format& operator%(const Formattible& object)
237  {
238  add(formattible_type(object));
239  return *this;
240  }
241 
243  string_type str(const std::locale& loc = std::locale()) const
244  {
245  std::basic_ostringstream<CharType> buffer;
246  buffer.imbue(loc);
247  write(buffer);
248  return buffer.str();
249  }
250 
252  void write(stream_type& out) const
253  {
255  if(translate_)
256  format = message_.str(out.getloc(), ios_info::get(out).domain_id());
257  else
258  format = format_;
259 
260  format_output(out, format);
261  }
262 
263  private:
264  class format_guard {
265  public:
266  format_guard(detail::format_parser& fmt) : fmt_(&fmt), restored_(false) {}
267  void restore()
268  {
269  if(restored_)
270  return;
271  fmt_->restore();
272  restored_ = true;
273  }
274  ~format_guard()
275  {
276  try {
277  restore();
278  } catch(...) {
279  }
280  }
281 
282  private:
283  detail::format_parser* fmt_;
284  bool restored_;
285  };
286 
287  void format_output(stream_type& out, const string_type& sformat) const
288  {
289  char_type obrk = '{';
290  char_type cbrk = '}';
291  char_type eq = '=';
292  char_type comma = ',';
293  char_type quote = '\'';
294 
295  size_t pos = 0;
296  size_t size = sformat.size();
297  const CharType* format = sformat.c_str();
298  while(format[pos] != 0) {
299  if(format[pos] != obrk) {
300  if(format[pos] == cbrk && format[pos + 1] == cbrk) {
301  out << cbrk;
302  pos += 2;
303  } else {
304  out << format[pos];
305  pos++;
306  }
307  continue;
308  }
309 
310  if(pos + 1 < size && format[pos + 1] == obrk) {
311  out << obrk;
312  pos += 2;
313  continue;
314  }
315  pos++;
316 
317  detail::format_parser fmt(out, static_cast<void*>(&out), &basic_format::imbue_locale);
318 
319  format_guard guard(fmt);
320 
321  while(pos < size) {
322  std::string key;
323  std::string svalue;
324  string_type value;
325  bool use_svalue = true;
326  for(; format[pos]; pos++) {
327  char_type c = format[pos];
328  if(c == comma || c == eq || c == cbrk)
329  break;
330  else {
331  key += static_cast<char>(c);
332  }
333  }
334 
335  if(format[pos] == eq) {
336  pos++;
337  if(format[pos] == quote) {
338  pos++;
339  use_svalue = false;
340  while(format[pos]) {
341  if(format[pos] == quote) {
342  if(format[pos + 1] == quote) {
343  value += quote;
344  pos += 2;
345  } else {
346  pos++;
347  break;
348  }
349  } else {
350  value += format[pos];
351  pos++;
352  }
353  }
354  } else {
355  char_type c;
356  while((c = format[pos]) != 0 && c != comma && c != cbrk) {
357  svalue += static_cast<char>(c);
358  pos++;
359  }
360  }
361  }
362 
363  if(use_svalue) {
364  fmt.set_one_flag(key, svalue);
365  } else
366  fmt.set_flag_with_str(key, value);
367 
368  if(format[pos] == comma) {
369  pos++;
370  continue;
371  } else if(format[pos] == cbrk) {
372  unsigned position = fmt.get_position();
373  out << get(position);
374  guard.restore();
375  pos++;
376  break;
377  } else {
378  guard.restore();
379  break;
380  }
381  }
382  }
383  }
384 
385  void add(const formattible_type& param)
386  {
387  if(parameters_count_ >= base_params_)
388  ext_params_.push_back(param);
389  else
390  parameters_[parameters_count_] = param;
391  parameters_count_++;
392  }
393 
394  formattible_type get(unsigned id) const
395  {
396  if(id >= parameters_count_)
397  return formattible_type();
398  else if(id >= base_params_)
399  return ext_params_[id - base_params_];
400  else
401  return parameters_[id];
402  }
403 
404  static void imbue_locale(void* ptr, const std::locale& l) { reinterpret_cast<stream_type*>(ptr)->imbue(l); }
405 
406  static constexpr unsigned base_params_ = 8;
407 
408  message_type message_;
409  string_type format_;
410  bool translate_;
411 
412  formattible_type parameters_[base_params_];
413  unsigned parameters_count_;
414  std::vector<formattible_type> ext_params_;
415  };
416 
420  template<typename CharType>
421  std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const basic_format<CharType>& fmt)
422  {
423  fmt.write(out);
424  return out;
425  }
426 
431 
432 #ifdef BOOST_LOCALE_ENABLE_CHAR16_T
435 #endif
436 
437 #ifdef BOOST_LOCALE_ENABLE_CHAR32_T
440 #endif
441 
443 }} // namespace boost::locale
444 
445 #ifdef BOOST_MSVC
446 # pragma warning(pop)
447 #endif
448 
456 
457 #endif
basic_format< char32_t > u32format
Definition of char32_t based format.
Definition: format.hpp:439
a printf like class that allows type-safe and locale aware message formatting
Definition: format.hpp:180
basic_format(const message_type &trans)
Definition: format.hpp:196
std::basic_string< CharType > string_type
string type for this type of character
Definition: format.hpp:188
std::ios_base & strftime(std::ios_base &ios)
Definition: formatting.hpp:254
basic_format & operator%(const Formattible &object)
Definition: format.hpp:236
string_type str() const
Translate message to a string in the default global locale, using default domain.
Definition: message.hpp:229
string_type str(const std::locale &loc=std::locale()) const
Format a string using a locale loc.
Definition: format.hpp:243
static ios_info & get(std::ios_base &ios)
Get ios_info instance for specific stream object.
basic_format(const string_type &format_string)
Create a format class for format_string.
Definition: format.hpp:192
basic_format(basic_format &&other)
Moveable.
Definition: format.hpp:202
std::basic_ostream< CharType > stream_type
output stream type for this type of character
Definition: format.hpp:189
std::basic_ostream< CharType > & operator<<(std::basic_ostream< CharType > &out, const date_time &t)
Definition: date_time.hpp:719
void domain_id(int)
Set special message domain identification.
void date_time_pattern(const std::basic_string< CharType > &str)
Set date/time pattern (strftime like)
Definition: formatting.hpp:131
void write(stream_type &out) const
write a formatted string to output stream out using out's locale
Definition: format.hpp:252
CharType char_type
Underlying character type.
Definition: format.hpp:182
basic_format< wchar_t > wformat
Definition of wchar_t based format.
Definition: format.hpp:430
basic_format< char16_t > u16format
Definition of char16_t based format.
Definition: format.hpp:434
basic_format< char > format
Definition of char based format.
Definition: format.hpp:428
basic_message< char_type > message_type
Definition: format.hpp:183