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