Ookii.CommandLine for C++  2.0.0
line_wrapping_stream.h
Go to the documentation of this file.
1 #pragma once
5 
6 #include <cassert>
7 #include <vector>
8 #include "vt_helper.h"
9 
10 namespace ookii
11 {
12 
15  constexpr size_t use_console_width = std::numeric_limits<size_t>::max();
16 
40  template<typename CharType, typename Traits = std::char_traits<CharType>>
41  class basic_line_wrapping_streambuf : public std::basic_streambuf<CharType, Traits>
42  {
43  public:
45  using base_type = std::basic_streambuf<CharType, Traits>;
47  using int_type = typename base_type::int_type;
49  using char_type = typename base_type::char_type;
51  using traits_type = typename base_type::traits_type;
52 
56  basic_line_wrapping_streambuf() noexcept = default;
57 
65  basic_line_wrapping_streambuf(base_type *streambuf, size_t max_line_length)
66  {
67  init(streambuf, max_line_length);
68  }
69 
72  {
73  swap(other);
74  }
75 
78  {
79  swap(other);
80  return *this;
81  }
82 
85 
90  {
91  sync(true);
92  }
93 
103  void init(base_type *streambuf, size_t max_line_length, bool count_formatting = false) noexcept
104  {
105  if (_base_streambuf != nullptr)
106  {
107  flush_buffer(true);
108  }
109 
110  _base_streambuf = streambuf;
111  _count_formatting = count_formatting;
112 
113  // Check if the caller wants to use the console width.
114  if (max_line_length == use_console_width)
115  {
116  _max_line_length = get_console_width();
117  if (_max_line_length != 0)
118  {
119  // Subtract one because it looks nicer.
120  --_max_line_length;
121  }
122  }
123  else
124  {
125  _max_line_length = max_line_length;
126  }
127 
128  // Don't allow super long lines, to avoid allocating large buffers.
129  if (_max_line_length > c_max_allowed_line_length)
130  {
131  _max_line_length = c_max_allowed_line_length;
132  }
133 
134  // Allocate a buffer only if line wrapping is used.
135  if (_max_line_length > 0)
136  {
137  // Make the buffer twice the length of the line to simplify the line wrapping code.
138  _buffer.resize(_max_line_length * 2);
139  reset_put_area();
140  }
141  else
142  {
143  // Clear out the buffer if there was one.
144  _buffer = {};
145  this->setp(nullptr, nullptr);
146  }
147  }
148 
150  size_t indent() const
151  {
152  return _indent_count;
153  }
154 
157  void indent(size_t indent) noexcept
158  {
159  if (_max_line_length > 0 && indent >= _max_line_length)
160  _indent_count = 0;
161  else
162  _indent_count = indent;
163  }
164 
172  {
173  if (_base_streambuf == nullptr)
174  {
175  return false;
176  }
177 
178  if (this->pubsync() != 0)
179  {
180  return false;
181  }
182 
183  if (this->pptr() != this->pbase())
184  {
185  if (is_eof(this->sputc(_new_line)))
186  {
187  return false;
188  }
189 
190  if (this->pubsync() != 0)
191  {
192  return false;
193  }
194 
195  assert(this->pptr() == this->pbase());
196  }
197 
198  _need_indent = false;
199  return true;
200  }
201 
204  void swap(basic_line_wrapping_streambuf &other) noexcept
205  {
206  if (this != std::addressof(other))
207  {
208  base_type::swap(other);
209  std::swap(_base_streambuf, other._base_streambuf);
210  std::swap(_max_line_length, other._max_line_length);
211  std::swap(_buffer, other._buffer);
212  std::swap(_new_line, other._new_line);
213  std::swap(_space, other._space);
214  std::swap(_indent_count, other._indent_count);
215  std::swap(_need_indent, other._need_indent);
216  std::swap(_count_formatting, other._count_formatting);
217  }
218  }
219 
235  virtual int sync(bool flush_last_line)
236  {
237  if (_base_streambuf == nullptr)
238  {
239  return -1;
240  }
241 
242  // Nothing to flush if not using a buffer, but ask the base buffer to sync.
243  if (_buffer.empty())
244  {
245  return _base_streambuf->pubsync();
246  }
247 
248  // Attempt to flush the buffer if it's not empty.
249  if (this->pptr() > this->pbase() && !flush_buffer(flush_last_line))
250  {
251  return -1;
252  }
253 
254  return _base_streambuf->pubsync();
255  }
256 
257  protected:
268  virtual int_type overflow(int_type ch = traits_type::eof()) override
269  {
270  if (_base_streambuf == nullptr)
271  {
272  return traits_type::eof();
273  }
274 
275  // If there is no maximum line length set, just forward the characters to the base stream.
276  if (_buffer.size() == 0)
277  {
278  // Nothing to do if this is eof.
279  if (is_eof(ch))
280  {
281  return traits_type::not_eof(ch);
282  }
283 
284  // If a line break, indicate the next line needs indentation, unless the previous
285  // line was blank.
286  if (is_new_line(ch))
287  {
288  _need_indent = !_blank_line;
289  _blank_line = true;
290  }
291  else
292  {
293  // Write indentation if needed.
294  // N.B. Don't indent blank lines.
295  if (_need_indent && !write_indent())
296  {
297  return traits_type::eof();
298  }
299 
300  _blank_line = false;
301  }
302 
303  // Write the character to the base stream.
304  return _base_streambuf->sputc(traits_type::to_char_type(ch));
305  }
306 
307  // If the buffer is not empty full, try to flush it.
308  if (this->pptr() > this->pbase() && !flush_buffer())
309  {
310  return traits_type::eof();
311  }
312 
313  if (!is_eof(ch))
314  {
315  // If the buffer is still full, try to grow it.
316  if (this->pptr() == this->epptr() && !grow_buffer())
317  {
318  return traits_type::eof();
319  }
320 
321  // The buffer must now have space for at least one character.
322  assert(this->pptr() != this->epptr());
323  this->sputc(traits_type::to_char_type(ch));
324  }
325 
326  // Return a character other than eof to indicate success.
327  return traits_type::not_eof(ch);
328  }
329 
338  virtual int sync() override
339  {
340  return sync(false);
341  }
342 
345  virtual void imbue(const std::locale &loc) override
346  {
347  if (_base_streambuf != nullptr)
348  {
349  // Change the locale of the underlying buffer.
350  _base_streambuf->pubimbue(loc);
351  update_locale_characters(loc);
352  }
353  }
354 
355  private:
356  using vt_helper_type = vt::details::vt_helper<CharType, Traits>;
357 
358  // Write the contents of the buffer to the underlying stream buffer, wrapping lines and
359  // adding indentation as necessary.
360  bool flush_buffer(bool flush_last_line = false)
361  {
362  if (_base_streambuf == nullptr)
363  {
364  return false;
365  }
366 
367  auto start = this->pbase();
368  auto end = this->pptr();
369 
370  // Only called when there is a buffer.
371  assert(start != nullptr && end != nullptr);
372 
373  std::streamsize count;
374  char_type *potential_line_break{};
375  char_type *new_start{};
376  auto locale = this->getloc();
377  size_t line_length{};
378  if (_need_indent)
379  {
380  line_length = _indent_count;
381  }
382 
383  // Loop over the contents of the buffer.
384  for (auto current = start; current < end; ++current)
385  {
386  // Check if this character can be used as a line break.
387  if (std::isspace(*current, locale))
388  {
389  potential_line_break = current;
390  }
391 
392  if (!_count_formatting && traits_type::eq(*current, vt_helper_type::c_escape))
393  {
394  auto vt_end = vt_helper_type::find_sequence_end(current + 1, end, locale);
395  if (vt_end == nullptr)
396  {
397  // Incomplete VT sequence, so we can't flush until the end is found.
398  break;
399  }
400 
401  // Continue with the next character after the sequence.
402  current = vt_end;
403  continue;
404  }
405 
406  // Check if we need to output a line.
407  if (line_length >= _max_line_length || traits_type::eq(*current, _new_line))
408  {
409  // Before writing the line, add indentation if necessary.
410  // N.B. Don't indent empty lines.
411  if (_need_indent)
412  {
413  line_length -= _indent_count;
414  if (line_length > 0 && !write_indent())
415  {
416  return false;
417  }
418  }
419 
420  // If no place was found to break the line, break it here.
421  if (potential_line_break == nullptr)
422  {
423  potential_line_break = current;
424  new_start = current;
425  }
426  else
427  {
428  new_start = potential_line_break + 1;
429  }
430 
431  // Write the line up to the break spot.
432  count = potential_line_break - start;
433  if (_base_streambuf->sputn(start, count) < count)
434  {
435  return false;
436  }
437 
438  // Write the line break.
439  if (is_eof(_base_streambuf->sputc(_new_line)))
440  {
441  return false;
442  }
443 
444  // Update the state for the new line.
445  start = new_start;
446  potential_line_break = nullptr;
447  _need_indent = line_length > 0;
448  line_length = ((current + 1) - start);
449  if (_need_indent)
450  {
451  line_length += _indent_count;
452  }
453  }
454  else
455  {
456  ++line_length;
457  }
458  }
459 
460  if (flush_last_line)
461  {
462  if (end > start)
463  {
464  if (_need_indent && !write_indent())
465  {
466  return false;
467  }
468 
469  count = end - start;
470  // Write the remainder of the buffer plus a new line.
471  if (_base_streambuf->sputn(start, count) < count ||
472  is_eof(_base_streambuf->sputc(_new_line)))
473  {
474  return false;
475  }
476  }
477 
478  reset_put_area(0);
479  }
480  else if (start > this->pbase())
481  {
482  // If we flushed any characters, move the remaining characters to the front for the
483  // next flush.
484  // N.B. line_length can't be used for this because it includes indent character
485  // count.
486  count = 0;
487  if (end > start)
488  {
489  count = end - start;
490  traits_type::move(this->pbase(), start, static_cast<size_t>(count));
491  }
492 
493  // Reset the put area, and indicate the number of characters moved to the front.
494  reset_put_area(static_cast<int>(count));
495  }
496 
497  return true;
498  }
499 
500  bool grow_buffer()
501  {
502  assert(this->epptr() == this->pptr());
503  auto old_size = static_cast<int>(_buffer.size());
504  auto new_size = old_size * 2;
505  // Cap consumed memory at 2GB if overflow happened.
506  if (new_size < old_size)
507  {
508  new_size = std::numeric_limits<std::int32_t>::max();
509  }
510 
511  if (new_size == old_size)
512  {
513  return false;
514  }
515 
516  _buffer.resize(new_size);
517  reset_put_area(old_size);
518  return true;
519  }
520 
521  // Reset the put area pointers.
522  void reset_put_area(int valid_data = 0)
523  {
524  this->setp(_buffer.data(), _buffer.data() + _buffer.size());
525  if (valid_data > 0)
526  {
527  this->pbump(valid_data);
528  }
529  }
530 
531  // Update the new line and space characters based on the specified locale.
532  void update_locale_characters(const std::locale &loc)
533  {
534  auto &ctype = std::use_facet<std::ctype<char_type>>(loc);
535  _new_line = ctype.widen('\n');
536  _space = ctype.widen(' ');
537  }
538 
539  // Write indentation to the underlying stream buffer.
540  bool write_indent()
541  {
542  if (_base_streambuf == nullptr)
543  {
544  return false;
545  }
546 
547  assert(_need_indent);
548  for (size_t i = 0; i < _indent_count; ++i)
549  {
550  if (is_eof(_base_streambuf->sputc(_space)))
551  {
552  return false;
553  }
554  }
555 
556  _need_indent = false;
557  return true;
558  }
559 
560  // Checks if the character (in integer representation) equals a new line.
561  bool is_new_line(int_type ch) const
562  {
563  return traits_type::eq_int_type(ch, traits_type::to_int_type(_new_line));
564  }
565 
566  // Checks if the character (in integer representation) is an end-of-file character.
567  static bool is_eof(int_type ch)
568  {
569  return traits_type::eq_int_type(ch, traits_type::eof());
570  }
571 
572  static constexpr size_t c_max_allowed_line_length = 65536;
573 
574  base_type *_base_streambuf{};
575  size_t _max_line_length{};
576  std::vector<char_type> _buffer;
577  char_type _new_line{};
578  char_type _space{};
579  size_t _indent_count{};
580  bool _need_indent{};
581  bool _blank_line{true};
582  bool _count_formatting{};
583  };
584 
589 
590  namespace details
591  {
592 
593  // Returns a stream's buffer, or NULL if the stream doesn't use a basic_line_wrapping_streambuf.
594  //
595  // N.B. This may require RTTI.
596  template<typename CharType, typename Traits>
597  auto get_line_wrapping_streambuf(std::basic_ostream<CharType, Traits> &stream)
598  {
599  return dynamic_cast<basic_line_wrapping_streambuf<CharType, Traits>*>(stream.rdbuf());
600  }
601 
602  // Stream manipulator to change the indent.
603  struct set_indent_helper
604  {
605  size_t indent;
606  };
607 
608  // Changes the indent size of the stream.
609  //
610  // N.B. Has no effect if the stream does not use a basic_line_wrapping_streambuf.
611  template<typename CharType, typename Traits>
612  std::basic_ostream<CharType, Traits> &operator<<(std::basic_ostream<CharType, Traits> &stream, const set_indent_helper &helper)
613  {
614  auto buffer = get_line_wrapping_streambuf(stream);
615  if (buffer != nullptr)
616  {
617  buffer->indent(helper.indent);
618  }
619 
620  return stream;
621  }
622 
623  struct flush_helper
624  {
625  bool flush_last_line;
626  };
627 
628  template<typename CharType, typename Traits>
629  std::basic_ostream<CharType, Traits> &operator<<(std::basic_ostream<CharType, Traits> &stream, const flush_helper &helper)
630  {
631  auto buffer = get_line_wrapping_streambuf(stream);
632  if (buffer != nullptr)
633  {
634  buffer->sync(helper.flush_last_line);
635  }
636  else
637  {
638  stream.flush();
639  }
640 
641  return stream;
642  }
643  }
644 
658  inline details::set_indent_helper set_indent(size_t indent)
659  {
660  return details::set_indent_helper{indent};
661  }
662 
679  template<typename CharType, typename Traits>
680  std::basic_ostream<CharType, Traits> &reset_indent(std::basic_ostream<CharType, Traits> &stream)
681  {
682  auto buffer = details::get_line_wrapping_streambuf(stream);
683  if (buffer != nullptr)
684  {
685  if (!buffer->reset_indent())
686  {
687  stream.setstate(std::ios::failbit);
688  }
689  }
690 
691  return stream;
692  }
693 
719  inline details::flush_helper flush(bool flush_last_line)
720  {
721  return {flush_last_line};
722  }
723 
747  template<typename CharType, typename Traits = std::char_traits<CharType>>
748  class basic_line_wrapping_ostream : public std::basic_ostream<CharType, Traits>
749  {
750  public:
752  using base_type = std::basic_ostream<CharType, Traits>;
754  using streambuf_type = std::basic_streambuf<CharType, Traits>;
755 
760  : base_type{std::addressof(_buffer)}
761  {
762  }
763 
773  basic_line_wrapping_ostream(base_type &base_stream, size_t max_line_length, bool count_formatting = false)
774  : base_type{std::addressof(_buffer)}
775  {
776  init(base_stream, max_line_length, count_formatting);
777  }
778 
788  void init(base_type &base_stream, size_t max_line_length, bool count_formatting = false)
789  {
790  _buffer.init(base_stream.rdbuf(), max_line_length, count_formatting);
791  this->imbue(base_stream.rdbuf()->getloc());
792  }
793 
797  : base_type{std::addressof(_buffer)}
798  {
799  swap(other);
800  }
801 
805  {
806  swap(other);
807  return *this;
808  }
809 
815  static basic_line_wrapping_ostream for_cout(short default_width = 80)
816  {
817  return {console_stream<CharType>::cout(), static_cast<size_t>(get_console_width(default_width))};
818  }
819 
825  static basic_line_wrapping_ostream for_cerr(short default_width = 80)
826  {
827  return {console_stream<CharType>::cerr(), static_cast<size_t>(get_console_width(default_width))};
828  }
829 
833  void swap(basic_line_wrapping_ostream &other) noexcept
834  {
835  if (this != std::addressof(other))
836  {
837  base_type::swap(other);
838  _buffer.swap(other._buffer);
839  }
840  }
841 
857  void flush(bool flush_last_line)
858  {
859  if (_buffer.sync(flush_last_line) < 0)
860  {
861  this->setstate(basic_line_wrapping_ostream::badbit);
862  }
863  }
864 
865  private:
867  };
868 
873 
874  namespace details
875  {
876  template <class T, class = void>
877  struct is_allocator : std::false_type
878  {
879  };
880 
881  template <class T>
882  struct is_allocator<T, std::void_t<typename T::value_type, decltype(std::declval<T&>().deallocate(
883  std::declval<T&>().allocate(size_t{ 1 }), size_t{ 1 })) >>
884  : std::true_type
885  {
886  };
887  }
888 
909  template<typename CharType, typename Traits = std::char_traits<CharType>, typename Allocator = std::allocator<CharType>>
911  {
912  public:
916  using ostringstream_type = std::basic_ostringstream<CharType, Traits, Allocator>;
918  using string_type = std::basic_string<CharType, Traits, Allocator>;
919 
928  basic_line_wrapping_ostringstream(size_t max_line_length, bool count_formatting = false)
929  {
930  this->init(_stringstream, max_line_length, count_formatting);
931  }
932 
937  string_type str() const&
938  {
939  return _stringstream.str();
940  }
941 
946  template<typename SAlloc, std::enable_if_t<details::is_allocator<SAlloc>::value, int> = 0>
947  std::basic_string<CharType, Traits, SAlloc> str(const SAlloc &alloc) const
948  {
949  return _stringstream.str(alloc);
950  }
951 
957  {
958  return std::move(_stringstream).str();
959  }
960 
963  void str(const string_type &s)
964  {
965  _stringstream.str(s);
966  }
967 
970  template<typename SAlloc>
971  void str(const std::basic_string<CharType, Traits, SAlloc> &s)
972  {
973  _stringstream.str(s);
974  }
975 
978  void str(string_type &&s)
979  {
980  _stringstream.str(std::move(s));
981  }
982 
987  std::basic_string_view<CharType, Traits> view() const noexcept
988  {
989  return _stringstream.view();
990  }
991 
992  private:
993  ostringstream_type _stringstream;
994  };
995 
1000 }
Output stream that wraps lines on white-space characters at the specified line length,...
Definition: line_wrapping_stream.h:749
void swap(basic_line_wrapping_ostream &other) noexcept
Swaps this basic_line_wrapping_ostream instance with another.
Definition: line_wrapping_stream.h:833
void flush(bool flush_last_line)
Flushes the buffer to the underlying stream buffer, optionally including the the final,...
Definition: line_wrapping_stream.h:857
static basic_line_wrapping_ostream for_cout(short default_width=80)
Creates a basic_line_wrapping_ostream that writes to the standard output stream, using the console wi...
Definition: line_wrapping_stream.h:815
static basic_line_wrapping_ostream for_cerr(short default_width=80)
Creates a basic_line_wrapping_ostream that writes to the standard error stream, using the console wid...
Definition: line_wrapping_stream.h:825
basic_line_wrapping_ostream(base_type &base_stream, size_t max_line_length, bool count_formatting=false)
Initializes a new instance of the basic_line_wrapping_ostream class with the specified underlying str...
Definition: line_wrapping_stream.h:773
std::basic_streambuf< CharType, Traits > streambuf_type
The concrete base stream buffer type used by this stream.
Definition: line_wrapping_stream.h:754
basic_line_wrapping_ostream()
Initializes a new instance of the basic_line_wrapping_ostream class.
Definition: line_wrapping_stream.h:759
basic_line_wrapping_ostream(basic_line_wrapping_ostream &&other) noexcept
Move constructor.
Definition: line_wrapping_stream.h:796
std::basic_ostream< CharType, Traits > base_type
The concrete type that this class derives from.
Definition: line_wrapping_stream.h:752
void init(base_type &base_stream, size_t max_line_length, bool count_formatting=false)
Initializes a new instance of the basic_line_wrapping_ostream class with the specified underlying str...
Definition: line_wrapping_stream.h:788
basic_line_wrapping_ostream & operator=(basic_line_wrapping_ostream &&other) noexcept
Move assignment operator.
Definition: line_wrapping_stream.h:804
Output string stream that wraps lines on white-space characters at the specified line length,...
Definition: line_wrapping_stream.h:911
void str(const std::basic_string< CharType, Traits, SAlloc > &s)
Replaces the contents of the underlying string.
Definition: line_wrapping_stream.h:971
std::basic_string< CharType, Traits, SAlloc > str(const SAlloc &alloc) const
Gets a copy of the underlying string object, using alloc as allocator.
Definition: line_wrapping_stream.h:947
basic_line_wrapping_ostringstream(size_t max_line_length, bool count_formatting=false)
Initializes a new instance of the basic_line_wrapping_ostringstream class with the specified maximum ...
Definition: line_wrapping_stream.h:928
std::basic_ostringstream< CharType, Traits, Allocator > ostringstream_type
The concrete type of std::basic_ostringstream used.
Definition: line_wrapping_stream.h:916
std::basic_string_view< CharType, Traits > view() const noexcept
Gets a view over the underlying string.
Definition: line_wrapping_stream.h:987
void str(const string_type &s)
Replaces the contents of the underlying string.
Definition: line_wrapping_stream.h:963
void str(string_type &&s)
Replaces the contents of the underlying string.
Definition: line_wrapping_stream.h:978
std::basic_string< CharType, Traits, Allocator > string_type
The concrete type of std::basic_string used.
Definition: line_wrapping_stream.h:918
string_type str() &&
Gets a string move-constructed from the underlying string.
Definition: line_wrapping_stream.h:956
string_type str() const &
Gets a copy of the underlying string object.
Definition: line_wrapping_stream.h:937
Stream buffer that wraps lines on white-space characters at the specified line length,...
Definition: line_wrapping_stream.h:42
size_t indent() const
Gets the current number of spaces that each line is indented with.
Definition: line_wrapping_stream.h:150
void swap(basic_line_wrapping_streambuf &other) noexcept
Swaps the contents basic_line_wrapping_streambuf instance with another.
Definition: line_wrapping_stream.h:204
void indent(size_t indent) noexcept
Sets the number of spaces that each line is indented with.
Definition: line_wrapping_stream.h:157
basic_line_wrapping_streambuf() noexcept=default
Initializes a new instance of the basic_line_wrapping_streambuf class.
typename base_type::int_type int_type
Integer type used by the base type.
Definition: line_wrapping_stream.h:47
basic_line_wrapping_streambuf & operator=(basic_line_wrapping_streambuf &&other) noexcept
Move assignment operator.
Definition: line_wrapping_stream.h:77
void init(base_type *streambuf, size_t max_line_length, bool count_formatting=false) noexcept
Initializes this basic_line_wrapping_streambuf instance with the specified underlying stream buffer a...
Definition: line_wrapping_stream.h:103
virtual int sync() override
Flushes the buffer to the underlying stream buffer.
Definition: line_wrapping_stream.h:338
virtual int sync(bool flush_last_line)
Flushes the buffer to the underlying stream buffer, optionally including the the final,...
Definition: line_wrapping_stream.h:235
bool reset_indent()
Disables indentation for the next line.
Definition: line_wrapping_stream.h:171
virtual int_type overflow(int_type ch=traits_type::eof()) override
Ensure there is space to write at least one character to the buffer.
Definition: line_wrapping_stream.h:268
typename base_type::char_type char_type
Character type used by the base type.
Definition: line_wrapping_stream.h:49
virtual void imbue(const std::locale &loc) override
Change the locale of the stream buffer.
Definition: line_wrapping_stream.h:345
std::basic_streambuf< CharType, Traits > base_type
The concrete type that this class derives from.
Definition: line_wrapping_stream.h:45
basic_line_wrapping_streambuf(basic_line_wrapping_streambuf &&other) noexcept
Move constructor.
Definition: line_wrapping_stream.h:71
typename base_type::traits_type traits_type
Traits type used by the base type.
Definition: line_wrapping_stream.h:51
virtual ~basic_line_wrapping_streambuf()
Destructor for the basic_line_wrapping_streambuf class.
Definition: line_wrapping_stream.h:89
Namespace containing the core Ookii.CommandLine.Cpp types.
Definition: command_line_argument.h:16
std::basic_ostream< CharType, Traits > & reset_indent(std::basic_ostream< CharType, Traits > &stream)
IO manipulator that lets the next line start at the beginning of the line, without indenting it.
Definition: line_wrapping_stream.h:680
constexpr size_t use_console_width
Indicates that the basic_line_wrapping_streambuf should use the console width as the line length.
Definition: line_wrapping_stream.h:15
details::flush_helper flush(bool flush_last_line)
IO manipulator that flushes a stream using a basic_line_wrapping_streambuf, optionally flushing the l...
Definition: line_wrapping_stream.h:719
details::set_indent_helper set_indent(size_t indent)
IO manipulator that changes the number of spaces that each line is indented with for a line wrapping ...
Definition: line_wrapping_stream.h:658
short get_console_width(short default_width=80) noexcept
Determines the width of the console.
Definition: console_helper.h:19
Template to determine the correct console streams based on the character type.
Definition: console_helper.h:49
Provides types for using virtual terminal sequences with console output.