VCCC  2024.05
VisualCamp Common C++ library
stream_wrapper.hpp
Go to the documentation of this file.
1 # /*
2 # * Created by YongGyu Lee on 2021/03/20.
3 # */
4 #
5 # ifndef VCCC_LOG_STREAM_WRAPPER_HPP
6 # define VCCC_LOG_STREAM_WRAPPER_HPP
7 #
8 # include <chrono>
9 # include <functional>
10 # include <ostream>
11 # include <iomanip>
12 # include <memory>
13 # include <mutex>
14 # include <ratio>
15 # include <sstream>
16 # include <string>
17 # include <tuple>
18 # include <type_traits>
19 # include <utility>
20 #
21 # include "vccc/__core/nodiscard.hpp"
25 # include "vccc/optional.hpp"
26 # include "vccc/__ranges/range.hpp"
29 # include "vccc/string_view.hpp"
30 # include "vccc/type_traits.hpp"
31 # include "vccc/variant.hpp"
32 #
33 # if __cplusplus >= 201703
34 # include <filesystem>
35 # include <optional>
36 # include <string_view>
37 # include <variant>
38 # include "boost/pfr.hpp"
39 # endif
40 
41 namespace vccc {
42 namespace detail {
43 
44 template<typename T, typename = void>
45 struct has_typename_key_type : std::false_type {};
46 
47 template<typename T>
48 struct has_typename_key_type<T, void_t<typename T::key_type>> : std::true_type {};
49 
50 template<typename T, typename = void>
51 struct has_typename_mapped_type : std::false_type {};
52 
53 template<typename T>
54 struct has_typename_mapped_type<T, void_t<typename T::mapped_type>> : std::true_type {};
55 
57 struct is_mapped_range : std::false_type {};
58 
59 template<typename T>
60 struct is_mapped_range<T, true>
61  : conjunction<
62  has_typename_key_type<T>,
63  has_typename_mapped_type<T>,
64  is_specialization<ranges::range_value_t<T>, std::pair>
65  > {};
66 
67 #if __cplusplus >= 201703L
69 struct empty_aggregate : std::false_type {};
70 
71 template<typename T>
72 struct empty_aggregate<T, true> : bool_constant<boost::pfr::tuple_size<T>::value == 0> {};
73 #else
74 #endif
75 
76 } // namespace detail
77 
80 
85  public:
86  StreamManipulator() = default;
87  ~StreamManipulator() = default;
88 };
89 
96 template<typename String, typename Stream>
98  public:
99  using stream_type = Stream;
100  using string_type = String;
101  using traits_type = typename stream_type::traits_type;
102 
109  static string_type s;
110  return s;
111  }
112 
118  const string_type& separator() const { return separator_; }
119 
126  auto prev = std::move(separator_);
127  separator_ = std::move(new_separator);
128  return prev;
129  }
130 
131  VCCC_NODISCARD bool quote_string() const noexcept {
132  return quote_string_;
133  }
134 
135  void quote_string(bool new_value) noexcept {
136  quote_string_ = new_value;
137  }
138 
139  VCCC_NODISCARD bool expand_aggregate() const noexcept {
140  return expand_aggregate_;
141  }
142 
143  void expand_aggregate(bool new_value) noexcept {
144  expand_aggregate_ = new_value;
145  }
146 
148  return expand_array_;
149  }
150 
151  void expand_array(bool new_value) {
152  expand_array_ = new_value;
153  }
154 
155  protected:
156  struct {
158  bool first_ = true;
162  };
163 };
164 
183 template<typename CharT,
184  typename String = std::basic_string<CharT>,
185  typename Stream = std::basic_stringstream<CharT>>
186 class BasicStreamWrapper : public StreamWrapperBase<String, Stream> {
187  public:
189  using char_type = CharT;
190  using stream_type = typename base::stream_type;
191  using string_type = typename base::string_type;
192  using traits_type = typename base::traits_type;
193 
194  BasicStreamWrapper() = default;
195  explicit BasicStreamWrapper(string_type str) : stream_(std::move(str)) {}
196  explicit BasicStreamWrapper(stream_type&& stream) : stream_(std::move(stream)) {}
197 
198  // for std::endl
199  BasicStreamWrapper& operator<<(std::ostream& (*pf)(std::ostream&)) {
200  pf(stream_);
201  return *this;
202  }
203 
204  // I/O manipulators
205  BasicStreamWrapper& operator<<(std::ios_base& (&io_manip)(std::ios_base&)) {
206  stream_ << io_manip;
207  return *this;
208  }
209 
210  // Custom I/O manipulators
211  template<typename T, std::enable_if_t<
212  std::is_base_of<StreamManipulator, remove_cvref_t<T>>
213  ::value, int> = 0>
214  BasicStreamWrapper& operator<<(const T& manipulator) {
215  manipulator(*this);
216  return *this;
217  }
218 
219  // All printable types
220  template<typename T, std::enable_if_t<
221  !std::is_base_of<StreamManipulator, remove_cvref_t<T>>
222  ::value, int> = 0>
224  if (base::first_) {
225  base::first_ = false;
226  } else {
227  stream_ << base::separator();
228  }
229  try_write(typename is_printable<T>::type{}, value);
230  return *this;
231  }
232 
237  stream_type& stream() { return stream_; }
238  const stream_type& stream() const { return stream_; }
239 
244  string_type str() const { return stream().str(); }
245 
250  void str(string_type s) { stream().str(std::move(s)); }
251 
252  private:
253  stream_type stream_;
254 
255  using default_printable_t = std::true_type;
256  using not_default_printable_t = std::false_type;
257 
258  // Default-printable types
259  template<typename T>
260  void try_write(default_printable_t, const T& value) {
261  stream_ << value;
262  }
263 
264  // Raw string
265  template<std::size_t N>
266  void try_write(default_printable_t, const char(&str)[N]) {
267  stream_ << str;
268  }
269 
270  // Array types
271  template<typename T, std::size_t N>
272  void try_write(default_printable_t, const T(&arr)[N]) {
274  stream_ << arr;
275  return;
276  }
277 
278  if (N == 0) {
279  stream_ << "{}";
280  return;
281  }
282 
283  stream_ << "{ ";
284  *this << *arr;
285  for (std::size_t i = 1; i < N; ++i) {
286  stream_ << ", ";
287  *this << *(arr + i);
288  }
289  stream_ << " }";
290  }
291 
292  // Strings
293  void try_write(default_printable_t, const std::string& s) {
295  stream_ << "\"" << s << "\"";
296  else
297  stream_ << s;
298  }
299 
300  void try_write(default_printable_t, const string_view& sv) {
302  stream_ << "\"" << sv << "\"";
303  else
304  stream_ << sv;
305  }
306 
307 #if __cplusplus >= 201703L
308  void try_write(default_printable_t, const std::string_view& sv) {
310  stream_ << "\"" << sv << "\"";
311  else
312  stream_ << sv;
313  }
314 #endif
315 
316  template<typename T>
317  constexpr void try_write(not_default_printable_t, const T& value) {
318  write(value);
319  }
320 
321 #if __cplusplus >= 201703
322  // any aggregate type
323  template<typename T, std::enable_if_t<conjunction<
324  negation<ranges::range<T>>,
325  std::is_aggregate<T>,
326  negation<detail::empty_aggregate<T>>
327  >::value, int> = 0>
328  void write(const T& aggr) {
330  writeAggregate(aggr, std::make_index_sequence<boost::pfr::tuple_size_v<T>>{});
331  else
332  writeAddress(aggr);
333  }
334 
335  template<typename T>
336  void writeAggregate(const T& aggr, std::index_sequence<>) {
337  stream_ << "{}";
338  }
339 
340  template<typename T>
341  void writeAggregate(const T& aggr, std::index_sequence<0>) {
342  stream_ << "{ ";
343  (*this) << boost::pfr::get<0>(aggr);
344  stream_ << " }";
345  }
346 
347  template<typename T, std::size_t ...I>
348  void writeAggregate(const T& aggr, std::index_sequence<0, I...>) {
349  stream_ << "{ ";
350  (*this) << boost::pfr::get<0>(aggr);
351  (((*this) << ", " << boost::pfr::get<I>(aggr) ), ...);
352  stream_ << " }";
353  }
354 #endif
355 
356 #if __cplusplus < 201703L
358 #else
359  template<typename T, std::enable_if_t<conjunction<
360  negation<ranges::range<T>>,
361  disjunction<
362  negation<std::is_aggregate<T>>,
363  detail::empty_aggregate<T>
364  >
365  >::value, int> = 0>
366 #endif
367  void write(const T& value) {
368  writeAddress(value);
369  }
370 
371  template<typename T>
372  void writeAddress(const T& value) {
373  stream_ << '@' << vccc::addressof(value);
374  }
375 
376  // ranges
378  void write(const T& value) {
379  writeRange(value);
380  }
381 
382  template<typename T>
383  void writeRange(const T& value) {
384  auto first = value.begin();
385  auto last = value.end();
386 
387  if (first == last) {
388  stream_ << "{}";
389  return;
390  }
391 
392  stream_ << "{ ";
393  writeRangeElement(*first, detail::is_mapped_range<T>::value);
394  for (++first; first != last; ++first) {
395  stream_ << ", ";
396  writeRangeElement(*first, detail::is_mapped_range<T>::value);
397  }
398  stream_ << " }";
399  }
400 
401  template<typename T>
402  void writeRangeElement(const T& value, bool) {
403  (*this) << value;
404  }
405 
406  template<typename T1, typename T2>
407  void writeRangeElement(const std::pair<T1, T2>& value, bool as_map) {
408  if (as_map)
409  (*this) << value.first << " => " << value.second;
410  else
411  (*this) << "{" << value.first << ", " << value.second << "}";
412  }
413 
414  // chrono types
415  template<typename Rep, typename Period>
416  void write(const std::chrono::duration<Rep, Period>& duration);
417 
418  template<typename Duration>
419  void write(const std::chrono::time_point<std::chrono::system_clock, Duration>& time_point);
420 
421  template<typename Clock, typename Duration>
422  void write(const std::chrono::time_point<Clock, Duration>& time_point) {
423  stream_ << "+";
424  write(time_point.time_since_epoch());
425  }
426 
427  void write(const std::time_t* tt);
428 
429  // Tuple-like types
430  template<typename ...Ts>
431  void write(const std::tuple<Ts...>& value) {
432  writeTupleLike(value, std::index_sequence_for<Ts...>{});
433  }
434 
435  template<typename T1, typename T2>
436  void write(const std::pair<T1, T2>& value) {
437  writeTupleLike(value, std::index_sequence_for<T1, T2>{});
438  }
439 
440  template<typename T>
441  void writeTupleLike(const T&, std::index_sequence<>) {
442  stream_ << "{}";
443  }
444 
445  template<typename T>
446  void writeTupleLike(const T& value, std::index_sequence<0>) {
447  stream_ << "{ ";
448  (*this) << std::get<0>(value);
449  stream_ << " }";
450  (*this) << "{ " << std::get<0>(value) << " }";
451  }
452 
453  template<typename T, std::size_t... I>
454  void writeTupleLike(const T& value, std::index_sequence<0, I...>) {
455  stream_ << "{ ";
456  (*this) << std::get<0>(value);
457 
458  int dummy[sizeof...(I)] = {
459  (((*this) << ", " << std::get<I>(value)), 0)...
460  };
461 
462  stream_ << " }";
463  }
464 
465  // integer sequence
466  template<typename T, T ...v>
467  void write(const std::integer_sequence<T, v...>&) {
468  writeTupleLike(std::make_tuple(v...), std::make_index_sequence<sizeof...(v)>{});
469  }
470 
471  // optional
472  template<typename T>
473  void write(const optional<T>& op) {
474  if (op)
475  *this << *op;
476  else
477  stream_ << "nullopt";
478  }
479 
480  // variant
481  template<typename... T>
482  void write(const variant<T...>& v) {
483  if (v.valueless_by_exception()) {
484  stream_ << "valueless_by_exception";
485  return;
486  }
487  v.visit([this](const auto& elem) {
488  *this << elem;
489  });
490  }
491 
492  template<bool v>
493  void write(const std::integral_constant<bool, v>& t) {
494  stream_ << (v ? "true_type" : "false_type");
495  }
496 
497  // unique_ptr<T>
498  template<typename T>
499  inline void write(const std::unique_ptr<T>& p) {
500  stream_ << "std::unique_ptr -> ";
501  writePointer(p.get());
502  }
503 
504  // shared_ptr<T>
505  template<typename T>
506  inline void write(const std::shared_ptr<T>& p) {
507  stream_ << "std::shared_ptr -> ";
508  writePointer(p.get());
509  }
510 
511  // weak_ptr<T>
512  template<typename T>
513  inline void write(const std::weak_ptr<T>& p) {
514  stream_ << "std::weak_ptr -> ";
515  writePointer(p.get());
516  }
517 
518  template<typename T>
519  void writePointer(const T* ptr) {
520  stream_ << ptr;
521  }
522 
523  // directory_entry(Apple Clang defects)
524 # if __cplusplus >= 201703L
525  void write(const std::filesystem::directory_entry& d) {
526  stream_ << d.path();
527  }
528 
529  // std::optional
530  template<typename T>
531  void write(const std::optional<T>& op) {
532  if (op)
533  *this << *op;
534  else
535  stream_ << "nullopt";
536  }
537 
538  // std::variant
539  template<typename... T>
540  void write(const std::variant<T...>& v) {
541  if (v.valueless_by_exception()) {
542  stream_ << "valueless_by_exception";
543  return;
544  }
545 
546  std::visit([this](const auto& elem) {
547  *this << elem;
548  }, v);
549  }
550 # endif
551 };
552 
554 
555 template<typename CharT, typename String, typename Stream>
556 template<typename Rep, typename Period>
557 void BasicStreamWrapper<CharT, String, Stream>::write(const std::chrono::duration<Rep, Period>& duration_) {
558  IOSFlagsSaver<stream_type> saver(stream_);
559 
560  auto duration =
561  duration_ >= std::chrono::duration<Rep, Period>::zero() ?
562  duration_ : -duration_;
563 
564  if(duration < std::chrono::minutes(1)) {
565  auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
566  stream_ << std::setprecision(5);
567 
568  if(duration < std::chrono::milliseconds(1)) {
569  if(duration < std::chrono::nanoseconds(1'000))
570  stream_ << ns.count() << "ns";
571  else
572  stream_ << ns.count() / 1'000.L << "us";
573  }
574  else { // duration >= std::chrono::milliseconds(1)
575  if(duration < std::chrono::milliseconds(1'000))
576  stream_ << ns.count() / 1'000'000.L << "ms";
577  else
578  stream_ << ns.count() / 1'000'000'000.L << "s";
579  }
580  }
581  else {
582  using days = std::chrono::duration<long, std::ratio< 86400>>;
583  using months = std::chrono::duration<long, std::ratio< 2629746>>;
584  using years = std::chrono::duration<long, std::ratio<31556952>>;
585 
586  auto hh = std::chrono::duration_cast<std::chrono::hours >(duration % days (1)).count();
587  auto mm = std::chrono::duration_cast<std::chrono::minutes >(duration % std::chrono::hours (1)).count();
588  auto ss = std::chrono::duration_cast<std::chrono::seconds >(duration % std::chrono::minutes(1)).count();
589  auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration % std::chrono::seconds(1)).count();
590 
591  if(duration >= days(1)) {
592  auto DD = std::chrono::duration_cast<days >(duration % months(1)).count();
593  auto MM = std::chrono::duration_cast<months>(duration % years(1)).count();
594  auto YY = std::chrono::duration_cast<years >(duration).count();
595 
596  if(YY) stream_ << YY << "Y ";
597  if(MM) stream_ << MM << "M ";
598  if(DD) stream_ << DD << "D ";
599  stream_ << std::setw(2) << std::setfill(' ');
600  }
601 
602  if(hh || mm || ss || ms) {
603  if (duration >= std::chrono::hours(1))
604  stream_ << hh << "h "
605  << std::setw(2) << std::setfill(' ') << mm << "m ";
606  else if (duration >= std::chrono::minutes(1))
607  stream_ << mm << "m ";
608 
609  stream_ << std::setw(2) << std::setfill(' ') << ss;
610  if (ms) stream_ << '.' << std::setw(3) << std::setfill('0') << ms;
611  stream_ << 's';
612  }
613  }
614 }
615 
616 template<typename CharT, typename String, typename Stream>
617 template<typename Duration>
618 void BasicStreamWrapper<CharT, String, Stream>::write(const std::chrono::time_point<std::chrono::system_clock, Duration>& time_point) {
619  static auto localtime_m = new std::mutex();
620 
621  std::time_t tt = std::chrono::system_clock::to_time_t(time_point);
622 
623  std::unique_lock<std::mutex> lck(*localtime_m);
624  const std::tm* tm_obj = std::localtime(&tt);
625 
626  if (tm_obj == nullptr) {// failed parsing
627  const auto unix_timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(time_point.time_since_epoch()).count();
628  stream_ << unix_timestamp;
629  return;
630  }
631 
632  stream_ << std::put_time(tm_obj, "%Y-%m-%d %H:%M:%S.");
633  lck.unlock();
634 
635  // calculate milliseconds
636  auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
637  time_point.time_since_epoch() % std::chrono::seconds(1)).count();
638  IOSFlagsSaver<stream_type> saver(stream_);
639  stream_ << std::setfill('0') << std::setw(3) << ms;
640 }
641 
642 template<typename CharT, typename String, typename Stream>
643 void BasicStreamWrapper<CharT, String, Stream>::write(const std::time_t *tt) {
644  static std::mutex localtime_m;
645  std::lock_guard<std::mutex> lck(localtime_m);
646 
647 #pragma warning( disable : 4996)
648  const std::tm* tm_obj = std::localtime(tt);
649  if(tm_obj == nullptr) // failed parsing
650  return
651 
652  stream_ << std::put_time(tm_obj, "%Y-%m-%d %H:%M:%S.");
653 }
654 
656 
658 
660 
661 } // namespace vccc
662 
663 # endif // VCCC_LOG_STREAM_WRAPPER_HPP
stream wrapper that supports extended operator overloading
Definition: stream_wrapper.hpp:186
CharT char_type
Definition: stream_wrapper.hpp:189
const stream_type & stream() const
Definition: stream_wrapper.hpp:238
BasicStreamWrapper & operator<<(std::ostream &(*pf)(std::ostream &))
Definition: stream_wrapper.hpp:199
BasicStreamWrapper(stream_type &&stream)
Definition: stream_wrapper.hpp:196
stream_type & stream()
Return internal stream.
Definition: stream_wrapper.hpp:237
BasicStreamWrapper & operator<<(std::ios_base &(&io_manip)(std::ios_base &))
Definition: stream_wrapper.hpp:205
BasicStreamWrapper(string_type str)
Definition: stream_wrapper.hpp:195
typename base::stream_type stream_type
Definition: stream_wrapper.hpp:190
BasicStreamWrapper & operator<<(const T &value)
Definition: stream_wrapper.hpp:223
void str(string_type s)
Equals to stream().str(string_type)
Definition: stream_wrapper.hpp:250
typename base::traits_type traits_type
Definition: stream_wrapper.hpp:192
BasicStreamWrapper & operator<<(const T &manipulator)
Definition: stream_wrapper.hpp:214
typename base::string_type string_type
Definition: stream_wrapper.hpp:191
string_type str() const
Equals to stream().str()
Definition: stream_wrapper.hpp:244
static bool expand_aggregate()
Definition: global_stream_wrapper_settings.hpp:30
static bool expand_array()
Definition: global_stream_wrapper_settings.hpp:38
static bool quote_string()
Definition: global_stream_wrapper_settings.hpp:21
Manipulator for vccc::StreamWrapper.
Definition: stream_wrapper.hpp:84
Definition: stream_wrapper.hpp:97
VCCC_NODISCARD bool quote_string() const noexcept
Definition: stream_wrapper.hpp:131
bool expand_aggregate_
Definition: stream_wrapper.hpp:160
static string_type & global_separator()
Get global separator.
Definition: stream_wrapper.hpp:108
VCCC_NODISCARD bool expand_aggregate() const noexcept
Definition: stream_wrapper.hpp:139
String string_type
Definition: stream_wrapper.hpp:100
string_type separator_
Definition: stream_wrapper.hpp:157
const string_type & separator() const
Get current separator.
Definition: stream_wrapper.hpp:118
bool quote_string_
Definition: stream_wrapper.hpp:159
void expand_aggregate(bool new_value) noexcept
Definition: stream_wrapper.hpp:143
typename stream_type::traits_type traits_type
Definition: stream_wrapper.hpp:101
void expand_array(bool new_value)
Definition: stream_wrapper.hpp:151
void quote_string(bool new_value) noexcept
Definition: stream_wrapper.hpp:135
bool expand_array_
Definition: stream_wrapper.hpp:161
bool first_
Definition: stream_wrapper.hpp:158
string_type separator(string_type new_separator)
Set current separator.
Definition: stream_wrapper.hpp:125
Stream stream_type
Definition: stream_wrapper.hpp:99
VCCC_NODISCARD bool expand_array()
Definition: stream_wrapper.hpp:147
constexpr VCCC_INLINE_OR_STATIC detail::prev_niebloid prev
Definition: prev.hpp:53
std::enable_if_t< std::is_object< T >::value, T * > addressof(T &t) noexcept
Definition: addressof.hpp:33
basic_string_view< char > string_view
Definition: string_view.hpp:794
std::integral_constant< bool, v > bool_constant
Definition: bool_constant.hpp:19
void void_t
Definition: void_t.hpp:19
constexpr R visit(Visitor &&vis, Variants &&... vars)
Definition: visit.hpp:93
Definition: matrix.hpp:495
Definition: directory.h:12
constexpr VCCC_INLINE_OR_STATIC detail::element_niebloid< 0 > first
Definition: key_value.hpp:34
constexpr VCCC_INLINE_OR_STATIC detail::element_niebloid< 1 > value
Definition: key_value.hpp:35
#define VCCC_NODISCARD
Definition: nodiscard.hpp:14
Definition: is_printable.hpp:26