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