All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
StreamIndenter.h
Go to the documentation of this file.
1 /**
2  * @file icaruscode/Utilities/StreamIndenter.h
3  * @brief Utility to have simple indentation in a stream.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date March 22, 2022
6  *
7  * This is a header-only library.
8  */
9 
10 #ifndef ICARUSCODE_UTILITIES_STREAMINDENTER_H
11 #define ICARUSCODE_UTILITIES_STREAMINDENTER_H
12 
13 
14 // C/C++ standard libraries
15 #include <utility> // std::move(), std::forward()
16 #include <sstream>
17 #include <string>
18 
19 
20 namespace util {
21 
22  /**
23  * @brief Stream modifier that makes it "indented".
24  *
25  * The intended use of this class is to be "inserted" into an output stream
26  * object in order to gain a simple indentation:
27  * * on the first insertion after this modifier, `firstIndent` will be used
28  * to indent the stream;
29  * * on the following insertions, `indent` will be used instead.
30  *
31  * Example:
32  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
33  * std::cout << util::addIndent("> ", "") << "First line: not indented."
34  * << "\nSecond line: indented by '> '."
35  * << "\nThird line: indented by '> '."
36  * ;
37  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
38  *
39  * Technically, after the first insertion (`std::cout << addIndent(...)`)
40  * the returned object is not `std::cout` any more, but a wrapper around it
41  * which intercepts all the insertion operations (`operator<<`).
42  *
43  * The wrapper is designed to steal the stream object if it is a temporary one
44  * (e.g. `std::ofstream{ "test.txt" } << addIndent("> ") << ...`) and to
45  * reference to it otherwise. Unless the wrapper object is somehow saved,
46  * it is destroyed (together with the stream if it was "stolen") as soon as
47  * the statement falls out of scope. Therefore, in this two-lines example:
48  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
49  * std::cout << util::addIndent("> ", "");
50  * std::cout << "First line: not indented."
51  * << "\nSecond line: also not indented."
52  * ;
53  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
54  * the first line does effectively nothing (the wrapper is created, ready to
55  * indent, but it is then immediately destroyed) while the second line
56  * performs normal `std::cout` output.
57  *
58  * @note The "first line" is actually the first insertion into the wrapped
59  * stream. For example,
60  * `std::cout << "A!" << util::addIndent("> ", "$ ") << "First line.";`
61  * will produce `A!$ First line.`.
62  *
63  *
64  * @note It is in principle possible to create modifiers with a special
65  * behaviour to interact directly with the wrapper (for example, change
66  * the indentation level or indentation string, reset to the first line
67  * indentation, ...). Such modifiers are not implemented so far.
68  */
69  struct addIndent {
70  std::string firstIndent, indent;
71 
72  addIndent(std::string indent, std::string firstIndent)
73  : firstIndent{ std::move(firstIndent) }, indent{ std::move(indent) }
74  {}
75  addIndent(std::string const& indent)
76  : addIndent{ indent, indent } {}
77 
78  }; // addIndent
79 
80 
81  //@{
82  /**
83  * @brief Creates an indented stream wrapper.
84  * @tparam Stream the type of stream being wrapped
85  * @param out the stream to wrap
86  * @param indent string inserted at the beginning of each new line
87  * @param firstIndent string inserted at the beginning of the first line
88  * @return an indented stream wrapper
89  * @see `addIndent()`
90  *
91  * The use of the wrapper stream is explained in `addIndent()`.
92  *
93  * This function wraps a stream `out`, stealing it if it's a temporary,
94  * and returns the wrapping object. This object can be used for indented
95  * output to `out` until it is destroyed (if `out` is referenced as opposed
96  * to stolen, `out` itself needs to stay valid).
97  *
98  * If not specified, `firstIndent` is assigned to be the same as `indent`.
99  *
100  * The equivalent two-line example of `addIndent()` becomes:
101  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
102  * auto out = util::makeIndented(std::cout, "> ", "$ ");
103  * out << "First line: indented with '$ '."
104  * << "\nSecond line: indented with '> '."
105  * ;
106  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
107  */
108  template <typename Stream>
109  auto makeIndented(Stream&& out, std::string indent, std::string firstIndent);
110 
111  template <typename Stream>
112  auto makeIndented(Stream&& out, std::string const& indent);
113  //@}
114 
115 
116  /// Helper function for `addIndent` (@see `addIndent`).
117  template <typename Stream>
118  auto operator<< (Stream&& out, addIndent&& adder);
119 
120 } // namespace util
121 
122 
123 namespace util::details {
124 
125  // ---------------------------------------------------------------------------
126  /**
127  * @brief Stream wapper.
128  */
129  template <typename Stream>
130  class IndentAdder {
131 
133  std::string const fFirstIndent;
134  std::string const fIndent;
135 
136  std::stringstream fSStr; // don't even try to be thread-safe
137  std::string const* fCurrentIndent { &fFirstIndent };
138 
139  public:
140  IndentAdder(Stream&& out, std::string indent, std::string firstIndent)
141  : fOut{ std::forward<Stream>(out) }
142  , fFirstIndent{ std::move(firstIndent) }, fIndent{ std::move(indent) }
143  {}
144  IndentAdder(Stream& out, std::string const& indent = "")
145  : IndentAdder{ std::forward<Stream>(out), indent, indent } {}
146 
147  template <typename T>
149  { fSStr << std::forward<T>(value); streamOut(); return *this; }
150 
151  private:
152 
153  void streamOut()
154  {
155  bool newLine = true;
156  char ch;
157  while(fSStr.get(ch)) {
158  if (newLine) { fOut << *fCurrentIndent; fCurrentIndent = &fIndent; }
159  fOut << ch;
160  newLine = (ch == '\n');
161  } // while
162  fSStr.clear();
163  }
164 
165  }; // class IndentAdder
166 
167 
168  // ---------------------------------------------------------------------------
169 
170 } // namespace util::details
171 
172 
173 // -----------------------------------------------------------------------------
174 template <typename Stream>
176  (Stream&& out, std::string indent, std::string firstIndent)
177 {
178  return details::IndentAdder
179  { std::forward<Stream>(out), std::move(indent), std::move(firstIndent) };
180 }
181 
182 
183 // -----------------------------------------------------------------------------
184 template <typename Stream>
185 auto util::makeIndented(Stream&& out, std::string const& indent)
186  { return makeIndented(out, indent, indent); }
187 
188 
189 // -----------------------------------------------------------------------------
190 template <typename Stream>
191 auto util::operator<< (Stream&& out, addIndent&& adder) {
192  return details::IndentAdder{
193  std::forward<Stream>(out),
194  std::move(adder.indent), std::move(adder.firstIndent)
195  };
196 } // operator<< (Stream, addIndent)
197 
198 
199 // -----------------------------------------------------------------------------
200 
201 
202 #endif // ICARUSCODE_UTILITIES_STREAMINDENTER_H
std::string const * fCurrentIndent
addIndent(std::string indent, std::string firstIndent)
auto makeIndented(Stream &&out, std::string indent, std::string firstIndent)
Creates an indented stream wrapper.
IndentAdder(Stream &out, std::string const &indent="")
Stream modifier that makes it &quot;indented&quot;.
std::string indent
std::ostream & operator<<(std::ostream &, Binner< T > const &)
Definition: Binner.h:218
IndentAdder & operator<<(T &&value)
std::string const fFirstIndent
temporary value
std::string firstIndent
IndentAdder(Stream &&out, std::string indent, std::string firstIndent)
bnb BNB Stream
std::string const fIndent
addIndent(std::string const &indent)