Boost.Locale
format.hpp
1//
2// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
3// Copyright (c) 2021-2023 Alexander Grund
4//
5// Distributed under the Boost Software License, Version 1.0.
6// https://www.boost.org/LICENSE_1_0.txt
7
8#ifndef BOOST_LOCALE_FORMAT_HPP_INCLUDED
9#define BOOST_LOCALE_FORMAT_HPP_INCLUDED
10
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 <string>
17#include <vector>
18
19#ifdef BOOST_MSVC
20# pragma warning(push)
21# pragma warning(disable : 4275 4251 4231 4660)
22#endif
23
24namespace boost { namespace locale {
25
31
33 namespace detail {
34
35 template<typename CharType>
36 struct formattible {
37 typedef std::basic_ostream<CharType> stream_type;
38 typedef void (*writer_type)(stream_type& output, const void* ptr);
39
40 formattible() noexcept : pointer_(nullptr), writer_(&formattible::void_write) {}
41
42 formattible(const formattible&) noexcept = default;
43 formattible(formattible&&) noexcept = default;
44 formattible& operator=(const formattible&) noexcept = default;
45 formattible& operator=(formattible&&) noexcept = default;
46
47 template<typename Type>
48 explicit formattible(const Type& value) noexcept
49 {
50 pointer_ = static_cast<const void*>(&value);
51 writer_ = &write<Type>;
52 }
53
54 friend stream_type& operator<<(stream_type& out, const formattible& fmt)
55 {
56 fmt.writer_(out, fmt.pointer_);
57 return out;
58 }
59
60 private:
61 static void void_write(stream_type& output, const void* /*ptr*/)
62 {
63 CharType empty_string[1] = {0};
64 output << empty_string;
65 }
66
67 template<typename Type>
68 static void write(stream_type& output, const void* ptr)
69 {
70 output << *static_cast<const Type*>(ptr);
71 }
72
73 const void* pointer_;
74 writer_type writer_;
75 }; // formattible
76
77 class BOOST_LOCALE_DECL format_parser {
78 public:
79 format_parser(std::ios_base& ios, void*, void (*imbuer)(void*, const std::locale&));
80 ~format_parser();
81 format_parser(const format_parser&) = delete;
82 format_parser& operator=(const format_parser&) = delete;
83
84 unsigned get_position();
85
86 void set_one_flag(const std::string& key, const std::string& value);
87
88 template<typename CharType>
89 void set_flag_with_str(const std::string& key, const std::basic_string<CharType>& value)
90 {
91 if(key == "ftime" || key == "strftime") {
92 as::strftime(ios_);
93 ios_info::get(ios_).date_time_pattern(value);
94 }
95 }
96 void restore();
97
98 private:
99 void imbue(const std::locale&);
100
101 std::ios_base& ios_;
102 struct data;
104 };
105
106 } // namespace detail
107
109
180 template<typename CharType>
182 int throw_if_params_bound() const;
183
184 public:
185 typedef CharType char_type;
188 typedef detail::formattible<CharType> formattible_type;
190
191 typedef std::basic_string<CharType> string_type;
192 typedef std::basic_ostream<CharType> stream_type;
193
195 basic_format(const string_type& format_string) : format_(format_string), translate_(false), parameters_count_(0)
196 {}
199 basic_format(const message_type& trans) : message_(trans), translate_(true), parameters_count_(0) {}
200
202 basic_format(const basic_format& other) = delete;
203 void operator=(const basic_format& other) = delete;
206 message_((other.throw_if_params_bound(), std::move(other.message_))), format_(std::move(other.format_)),
207 translate_(other.translate_), parameters_count_(0)
208 {}
209 basic_format& operator=(basic_format&& other)
210 {
211 other.throw_if_params_bound();
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 // clang-format off
276 try { restore(); } catch(...) {}
277 // clang-format on
278 }
279
280 private:
281 detail::format_parser& fmt_;
282 bool restored_;
283 };
284
285 void format_output(stream_type& out, const string_type& sformat) const
286 {
287 constexpr char_type obrk = '{';
288 constexpr char_type cbrk = '}';
289 constexpr char_type eq = '=';
290 constexpr char_type comma = ',';
291 constexpr char_type quote = '\'';
292
293 const size_t size = sformat.size();
294 const CharType* format = sformat.c_str();
295 for(size_t pos = 0; format[pos];) {
296 if(format[pos] != obrk) {
297 if(format[pos] == cbrk && format[pos + 1] == cbrk) {
298 out << cbrk;
299 pos += 2;
300 } else {
301 out << format[pos];
302 pos++;
303 }
304 continue;
305 }
306 pos++;
307 if(format[pos] == obrk) {
308 out << obrk;
309 continue;
310 }
311
312 detail::format_parser fmt(out, static_cast<void*>(&out), &basic_format::imbue_locale);
313
314 format_guard guard(fmt);
315
316 while(pos < size) {
317 std::string key;
318 std::string svalue;
319 string_type value;
320 bool use_svalue = true;
321 for(char_type c = format[pos]; !(c == 0 || c == comma || c == eq || c == cbrk); c = format[++pos]) {
322 key += static_cast<char>(c);
323 }
324
325 if(format[pos] == eq) {
326 pos++;
327 if(format[pos] == quote) {
328 pos++;
329 use_svalue = false;
330 while(format[pos]) {
331 if(format[pos] == quote) {
332 if(format[pos + 1] == quote) {
333 value += quote;
334 pos += 2;
335 } else {
336 pos++;
337 break;
338 }
339 } else {
340 value += format[pos];
341 pos++;
342 }
343 }
344 } else {
345 char_type c;
346 while((c = format[pos]) != 0 && c != comma && c != cbrk) {
347 svalue += static_cast<char>(c);
348 pos++;
349 }
350 }
351 }
352
353 if(use_svalue)
354 fmt.set_one_flag(key, svalue);
355 else
356 fmt.set_flag_with_str(key, value);
357
358 if(format[pos] == comma)
359 pos++;
360 else {
361 if(format[pos] == cbrk) {
362 unsigned position = fmt.get_position();
363 out << get(position);
364 pos++;
365 }
366 break;
367 }
368 }
369 }
370 }
371
372 void add(const formattible_type& param)
373 {
374 if(parameters_count_ >= base_params_)
375 ext_params_.push_back(param);
376 else
377 parameters_[parameters_count_] = param;
378 parameters_count_++;
379 }
380
381 formattible_type get(unsigned id) const
382 {
383 if(id >= parameters_count_)
384 return formattible_type();
385 else if(id >= base_params_)
386 return ext_params_[id - base_params_];
387 else
388 return parameters_[id];
389 }
390
391 static void imbue_locale(void* ptr, const std::locale& l) { static_cast<stream_type*>(ptr)->imbue(l); }
392
393 static constexpr unsigned base_params_ = 8;
394
395 message_type message_;
396 string_type format_;
397 bool translate_;
398
399 formattible_type parameters_[base_params_];
400 unsigned parameters_count_;
401 std::vector<formattible_type> ext_params_;
402 };
403
407 template<typename CharType>
408 std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const basic_format<CharType>& fmt)
409 {
410 fmt.write(out);
411 return out;
412 }
413
418
419#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
422#endif
423
424#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
427#endif
428
429 template<typename CharType>
431 {
432 if(parameters_count_)
433 throw std::invalid_argument("Can't move a basic_format with bound parameters");
434 return 0;
435 }
437}} // namespace boost::locale
438
439#ifdef BOOST_MSVC
440# pragma warning(pop)
441#endif
442
450
451#endif
a printf like class that allows type-safe and locale aware message formatting
Definition: format.hpp:181
std::basic_string< CharType > string_type
string type for this type of character
Definition: format.hpp:191
CharType char_type
Underlying character type.
Definition: format.hpp:185
std::basic_ostream< CharType > stream_type
output stream type for this type of character
Definition: format.hpp:192
basic_message< char_type > message_type
Definition: format.hpp:186
string_type str(const std::locale &loc=std::locale()) const
Format a string using a locale loc.
Definition: format.hpp:242
basic_format(const string_type &format_string)
Create a format class for format_string.
Definition: format.hpp:195
basic_format(const basic_format &other)=delete
Non-copyable.
basic_format & operator%(const Formattible &object)
Definition: format.hpp:235
void write(stream_type &out) const
write a formatted string to output stream out using out's locale
Definition: format.hpp:251
basic_format(basic_format &&other)
Moveable.
Definition: format.hpp:205
basic_format(const message_type &trans)
Definition: format.hpp:199
This class represents a message that can be converted to a specific locale message.
Definition: message.hpp:142
a smart pointer similar to std::unique_ptr but the underlying object has the same constness as the po...
Definition: hold_ptr.hpp:17
void domain_id(int)
Set special message domain identification.
static ios_info & get(std::ios_base &ios)
Get ios_info instance for specific stream object.
std::basic_ostream< CharType > & operator<<(std::basic_ostream< CharType > &out, const date_time &t)
Definition: date_time.hpp:722
basic_format< wchar_t > wformat
Definition of wchar_t based format.
Definition: format.hpp:417
basic_format< char32_t > u32format
Definition of char32_t based format.
Definition: format.hpp:426
basic_format< char16_t > u16format
Definition of char16_t based format.
Definition: format.hpp:421
basic_format< char > format
Definition of char based format.
Definition: format.hpp:415
string_type str() const
Translate message to a string in the default global locale, using default domain.
Definition: message.hpp:238