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 // Escaped closing brace
299 out << cbrk;
300 pos += 2;
301 } else {
302 out << format[pos];
303 pos++;
304 }
305 continue;
306 }
307 pos++;
308 if(format[pos] == obrk) {
309 // Escaped opening brace
310 out << obrk;
311 pos++;
312 continue;
313 }
314
315 detail::format_parser fmt(out, static_cast<void*>(&out), &basic_format::imbue_locale);
316
317 format_guard guard(fmt);
318
319 while(pos < size) {
320 std::string key;
321 std::string svalue;
322 string_type value;
323 bool use_svalue = true;
324 for(char_type c = format[pos]; !(c == 0 || c == comma || c == eq || c == cbrk); c = format[++pos]) {
325 key += static_cast<char>(c);
326 }
327
328 if(format[pos] == eq) {
329 pos++;
330 if(format[pos] == quote) {
331 pos++;
332 use_svalue = false;
333 while(format[pos]) {
334 if(format[pos] == quote) {
335 if(format[pos + 1] == quote) {
336 value += quote;
337 pos += 2;
338 } else {
339 pos++;
340 break;
341 }
342 } else {
343 value += format[pos];
344 pos++;
345 }
346 }
347 } else {
348 char_type c;
349 while((c = format[pos]) != 0 && c != comma && c != cbrk) {
350 svalue += static_cast<char>(c);
351 pos++;
352 }
353 }
354 }
355
356 if(use_svalue)
357 fmt.set_one_flag(key, svalue);
358 else
359 fmt.set_flag_with_str(key, value);
360
361 if(format[pos] == comma)
362 pos++;
363 else {
364 if(format[pos] == cbrk) {
365 unsigned position = fmt.get_position();
366 out << get(position);
367 pos++;
368 }
369 break;
370 }
371 }
372 }
373 }
374
375 void add(const formattible_type& param)
376 {
377 if(parameters_count_ >= base_params_)
378 ext_params_.push_back(param);
379 else
380 parameters_[parameters_count_] = param;
381 parameters_count_++;
382 }
383
384 formattible_type get(unsigned id) const
385 {
386 if(id >= parameters_count_)
387 return formattible_type();
388 else if(id >= base_params_)
389 return ext_params_[id - base_params_];
390 else
391 return parameters_[id];
392 }
393
394 static void imbue_locale(void* ptr, const std::locale& l) { static_cast<stream_type*>(ptr)->imbue(l); }
395
396 static constexpr unsigned base_params_ = 8;
397
398 message_type message_;
399 string_type format_;
400 bool translate_;
401
402 formattible_type parameters_[base_params_];
403 unsigned parameters_count_;
404 std::vector<formattible_type> ext_params_;
405 };
406
410 template<typename CharType>
411 std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const basic_format<CharType>& fmt)
412 {
413 fmt.write(out);
414 return out;
415 }
416
421#ifndef BOOST_LOCALE_NO_CXX20_STRING8
424#endif
425
426#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
429#endif
430
431#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
434#endif
435
436 template<typename CharType>
438 {
439 if(parameters_count_)
440 throw std::invalid_argument("Can't move a basic_format with bound parameters");
441 return 0;
442 }
444}} // namespace boost::locale
445
446#ifdef BOOST_MSVC
447# pragma warning(pop)
448#endif
449
457
458#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:724
basic_format< wchar_t > wformat
Definition of wchar_t based format.
Definition: format.hpp:420
basic_format< char32_t > u32format
Definition of char32_t based format.
Definition: format.hpp:433
basic_format< char16_t > u16format
Definition of char16_t based format.
Definition: format.hpp:428
basic_format< char > format
Definition of char based format.
Definition: format.hpp:418
basic_format< char8_t > u8format
Definition of char8_t based format.
Definition: format.hpp:423
string_type str() const
Translate message to a string in the default global locale, using default domain.
Definition: message.hpp:238