Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/Base/Print.hpp
4 : : \copyright 2012-2015 J. Bakosi,
5 : : 2016-2018 Los Alamos National Security, LLC.,
6 : : 2019-2021 Triad National Security, LLC.,
7 : : 2022-2024 J. Bakosi
8 : : All rights reserved. See the LICENSE file for details.
9 : : \brief General purpose pretty printer functionality
10 : : \details This file contains general purpose printer functions. Using the
11 : : functions defined here provides formatting, and a consistent look with
12 : : simple client-side code.
13 : : */
14 : : // *****************************************************************************
15 : : #pragma once
16 : :
17 : : #include <iostream>
18 : : #include <cmath>
19 : :
20 : : #include "Timer.hpp"
21 : : #include "Exception.hpp"
22 : : #include "PrintUtil.hpp"
23 : :
24 : : namespace tk {
25 : :
26 : : //! Pretty printer base. Contains general purpose printer functions. Using the
27 : : //! functions defined here provides formatting, and a consistent look with
28 : : //! simple client-side code.
29 : : class Print {
30 : :
31 : : public:
32 : : //! Constructor
33 : 9745 : explicit Print() : m_stream( std::cout ) {}
34 : :
35 : : //! Operator << for printing any type to the verbose stream.
36 : : //! \param[in] os Reference to pretty printer object
37 : : //! \param[in] t Reference to an arbitrary object of type T. T must define
38 : : //! operator<< for std::ostream-compatible streams.
39 : : //! \return The internal stream buffer of the stream
40 : : template< typename T >
41 : 129890 : friend const Print& operator<<( const Print& os, const T& t ) {
42 : 129890 : os.m_stream << t << std::flush;
43 : 129890 : return os;
44 : : }
45 : :
46 : : //! Formatted print of section title
47 : : //! \param[in] t Section title to be printed
48 : 2331 : void section( const std::string& t ) const {
49 [ + - ]: 2331 : std::string underline( t.size(), '-' );
50 [ + - ][ + - ]: 2331 : m_stream << '\n' << t.c_str() << '\n' << underline.c_str() << std::endl;
[ + - ][ + - ]
[ + - ]
51 : 2331 : }
52 : :
53 : : //! Formatted print of item: name : value
54 : : //! \param[in] name Item name to be printed
55 : : //! \param[in] value Item value to be printed
56 : : template< typename T >
57 : 4271 : void item( const std::string& name, const T& value ) const {
58 : 4271 : m_stream << name.c_str() << ": ";
59 : : if constexpr( std::is_same_v< T, std::string > ) {
60 : 2717 : m_stream << value.c_str();
61 : : } else {
62 : 1554 : m_stream << value;
63 : : }
64 : 4271 : m_stream << std::endl;
65 : 4271 : }
66 : :
67 : : //! Formatted print of item: h:m:s.
68 : : //! \param[in] name Item name to be printed
69 : : //! \param[in] watch Watch (in hours, minutes, seconds) to be printed as
70 : : //! item value
71 : 618 : void item( const std::string& name, const tk::Timer::Watch& watch ) const {
72 : 618 : m_stream << name.c_str() << ": "
73 : 618 : << watch.hrs.count() << ':'
74 : 618 : << watch.min.count() << ':'
75 : 618 : << watch.sec.count() << std::endl;
76 : 618 : }
77 : :
78 : : //! Formatted print of a performance statistic (an item of a list)
79 : : //! \param[in] name Performance statistic name to be printed
80 : : //! \param[in] value Performance statistic value
81 : : void perfitem( const std::string& name, tk::real value ) const {
82 : : m_stream << name.c_str() << " : " << value << std::endl;
83 : : }
84 : :
85 : : //! Formatted print of elapsed times
86 : : //! \param[in] t Title of section containing a list of elapsed times
87 : : //! \param[in] clock std::vector of strings (clock names) and associated
88 : : //! timers which could be in various formats as long as there is a
89 : : //! corresponding item() overload that can apply operator << for outputing
90 : : //! their value to an output stream. Examples of allowed ClockFormats are:
91 : : //! tk::Timer::Watch, which is a struct containing a timestamp in h:m:s
92 : : //! format, and the return value of Timer::dsec(), which is a tk::real.
93 : : template< class ClockFormat >
94 : 273 : void time( const std::string& t,
95 : : const std::vector<
96 : : std::pair< std::string, ClockFormat > >& clock ) const
97 : : {
98 : 273 : section( t );
99 [ + - ][ + + ]: 891 : for (const auto& c : clock) item( c.first, c.second );
100 : 273 : m_stream << std::endl;
101 : 273 : }
102 : :
103 : : //! Echo formatted print of a diagnostics message within a progress section
104 : : //! \param[in] labels Label parts of diagnostics message
105 : : //! \param[in] values Value parts of diagnostics message
106 : : //! \note The number of labels and values must equal.
107 : 35 : void diag( const std::vector< std::string >& labels,
108 : : const std::vector< std::string >& values ) const
109 : : {
110 [ - + ][ - - ]: 35 : Assert( labels.size() == values.size(), "Size mismatch" );
[ - - ][ - - ]
111 [ + - ]: 35 : if (!labels.empty()) {
112 : 35 : m_stream << labels[0] << ": " << values[0];
113 [ + + ]: 210 : for (std::size_t i=1; i<labels.size(); ++i) {
114 : 175 : m_stream << ", " << labels[i] << ": " << values[i];
115 : : }
116 : 35 : m_stream << std::flush;
117 : : }
118 : 35 : }
119 : :
120 : : //! Start formatted print of a diagnostics message
121 : : //! Start formatted print of a diagnostics message
122 : : //! \param[in] msg First part of message to print as a diagnostics message
123 : 557 : void diagstart( const std::string& msg ) const {
124 : 557 : m_stream << msg.c_str() << ' ' << std::flush;
125 : 557 : }
126 : :
127 : : //! Finish formatted print of a diagnostics message
128 : : //! \param[in] msg Last part of message to print as a diagnostics message
129 : 557 : void diagend( const std::string& msg ) const {
130 : 557 : m_stream << msg.c_str() << std::endl;
131 : 557 : }
132 : :
133 : : //! Echo formatted print of a progress message
134 : : //! \param[in] prefix Strings to output prefixing the progress report
135 : : //! \param[in] done Array of integers indicating how many have been done
136 : : //! \param[in] max Array of integers indicating how many to be done
137 : : //! \param[in] progress_size Size of previous progress report (to overwrite)
138 : : //! \details All input arrays are the same size. The prefix strings
139 : : //! are optional, i.e., they can be empty strings. The function generates
140 : : //! an output to the stream configured in the following fashion:
141 : : //! pre1[done1/max1], pre2[done2/max2], ..., e.g., r:[1/3], b[2/8].
142 : : //! Whenever this function is called, a number of backspaces are put into
143 : : //! the stream so that the new progress report string overwrites the old
144 : : //! one. In order to backtrack the correct amount, the length of the old
145 : : //! progress report is stored (by whatever object holds us) and passed in
146 : : //! by reference in progress_size, which is overwritten here once it has
147 : : //! been used for backtracking. Therefore, for restarting a new series of
148 : : //! progress reports, this variable must be zeroed. Also, it is best to
149 : : //! not to interleave multiple tasks, because even if a different
150 : : //! progress_size is kept for each, there is no regard as to which line we
151 : : //! output to in the stream. In other words, multiple task outputs will
152 : : //! be intermingled, leading to confusing output.
153 : : template< std::size_t N >
154 : 230 : void progress( const std::array< std::string, N >& prefix,
155 : : const std::array< int, N >& done,
156 : : const std::array< int, N >& max,
157 : : std::size_t& progress_size ) const
158 : : {
159 : : // lambda to determine the number of digits in an integer
160 : 2596 : auto numdig = []( int i ) -> std::size_t {
161 [ + - ][ + + ]: 2596 : return i > 0 ?
162 : 1402 : static_cast< std::size_t >( std::log10(static_cast<double>(i)) ) + 1
163 : 2596 : : 1; };
164 : : // Backspace so that new progress can overwrite old one
165 [ + - ][ + - ]: 230 : m_stream << std::string( progress_size, '\b' ).c_str();
166 [ + - ]: 230 : std::stringstream ss;
167 : 230 : auto ip = prefix.cbegin();
168 : 230 : auto id = done.cbegin();
169 : 230 : auto im = max.cbegin();
170 : 230 : progress_size = 0;
171 [ + + ]: 1528 : while (ip != prefix.cend()) {
172 : : // Compute new length of progress string
173 : 1298 : progress_size += 4 + ip->size() + numdig(*id) + numdig(*im);
174 : : // Construct and output new progress string to stream
175 [ + - ][ + - ]: 1298 : ss << *ip << ":[" << *id << '/' << *im << ']';
[ + - ][ + - ]
[ + - ][ + - ]
176 : 1298 : ++ip; ++id; ++im;
177 : : // if next subprogress is not the last one, put in a comma
178 [ + + ]: 1298 : if (ip != prefix.cend()) {
179 [ + - ]: 1068 : ss << ", ";
180 : 1068 : progress_size += 2;
181 : : } else {
182 [ + - ]: 230 : ss << ' ';
183 : 230 : ++progress_size;
184 : : }
185 : : }
186 [ + - ][ + - ]: 230 : m_stream << ss.str().c_str() << std::flush;
[ + - ]
187 : 230 : }
188 : :
189 : : //! Print version information
190 : : //! \param[in] executable Name of executable to output version for
191 : : //! \param[in] git_commit Git commit sha1 to output
192 : 3 : void version( const std::string& executable,
193 : : const std::string& git_commit ) const {
194 : 3 : m_stream << "\nXyst::" << executable.c_str()
195 : 3 : << ", revision " << git_commit.c_str() << '\n' << std::endl;
196 : 3 : }
197 : :
198 : : //! Print mandatory arguments information
199 : : //! \param[in] args Mandaatory-arguments infor to output
200 : : void mandatory( const std::string& args ) const {
201 : : m_stream << "\n>>> ERROR: " << args.c_str() << std::endl;
202 : : }
203 : :
204 : : //! Print example usage information
205 : : //! \param[in] example Example command line to output
206 : : //! \param[in] msg Message to output after example
207 : : void usage( const std::string& example, const std::string& msg ) const {
208 : : m_stream << "\nUsage: " << example.c_str() << '\n'
209 : : << msg.c_str() << ". See also -h." << '\n' << std::endl;
210 : : }
211 : :
212 : : //! Print lower and upper bounds for a keyword if defined
213 : : template< typename Info >
214 : : void bounds( const Info& info ) const {
215 : : if (info.lower) {
216 : : m_stream << splitLines( *info.lower, " ", "Lower bound: " ).c_str();
217 : : }
218 : : if (info.upper) {
219 : : m_stream << splitLines( *info.upper, " ", "Upper bound: " ).c_str();
220 : : }
221 : : m_stream << std::flush;
222 : : }
223 : :
224 : : //! Print unit tests header (with legend)
225 : : //! \param[in] t Section title
226 : : //! \param[in] group String attempting to match unit test groups
227 : 2 : void unithead( const std::string& t, const std::string& group ) const {
228 : 2 : section( t );
229 [ + + ][ + - ]: 4 : m_stream << "Groups: " + (group.empty() ? "all" : group) +
[ + - ][ + - ]
[ + - ][ + + ]
[ - - ]
230 [ + - ]: 4 : " (use -g str to match groups)\n" +
231 [ + - ][ + - ]: 2 : "Legend: [done/failed] group/test: result\n" << std::endl;
232 : 2 : }
233 : :
234 : : //! Print one-liner info for test
235 : : //! \details Columns:
236 : : //! [done/failed]
237 : : //! - done: number of tests completed so far
238 : : //! - failed: number of failed tests so far
239 : : //! name of the test group
240 : : //! name of the test
241 : : //! result (with additional info if failed)
242 : : //! Assumed fields for status:
243 : : //! - status[0]: test group name
244 : : //! - status[1]: test name
245 : : //! - status[2]: result (tut::test_result::result_type as string)
246 : : //! - status[3]: exception message for failed test
247 : : //! - status[4]: exception type id for failed test
248 : 2527 : void test( std::size_t ncomplete,
249 : : std::size_t nfail,
250 : : const std::vector< std::string >& status )
251 : : {
252 [ + + ]: 2527 : if (status[2] != "8") { // if not dummy
253 [ + - ]: 365 : std::stringstream ss;
254 [ + - ][ + - ]: 365 : ss << " [" << ncomplete << '/' << nfail << "] " << status[0] << ':'
[ + - ][ + - ]
255 [ + - ][ + - ]: 365 : << status[1];
[ + - ][ + - ]
256 [ + - ][ + - ]: 365 : auto s = ss.str() + ' ' + std::string(80-ss.str().size(),'.') + " ";
[ + - ][ + - ]
[ + - ][ + - ]
257 [ + - ][ + - ]: 365 : m_stream << s << result( status[2], status[3], status[4] ) << std::endl;
[ + - ][ + - ]
258 : 365 : }
259 : 2527 : }
260 : :
261 : : //! Print Inciter header. Text ASCII Art Generator used for executable
262 : : //! names: http://patorjk.com/software/taag.
263 : 253 : void headerInciter() const {
264 : 253 : m_stream << R"(
265 : : ____ ___ __ __ .___ .__ __
266 : : \ \/ /___.__. _______/ |_ \ \ | | ____ ____ |__|/ |_ ___________
267 : : \ /< | |/ ___/\ __\ \ \ | |/ \_/ ___\| \ __\/ __ \_ __ \
268 : : / \ \___ |\___ \ | | / / | | | \ \___| || | \ ___/| | \/
269 : : /___/\ \/ ____/____ > |__| /_/ |___|___| /\___ >__||__| \___ >__|
270 : : \_/\/ \/ \/ \/ \/)"
271 : 253 : << std::endl;
272 : 253 : }
273 : :
274 : : //! Print UnitTest header. Text ASCII Art Generator used for executable
275 : : //! names: http://patorjk.com/software/taag.
276 : 2 : void headerUnitTest() const {
277 : 2 : m_stream << R"(
278 : : ____ ___ __ __ ____ ___ .__ __ ___________ __
279 : : \ \/ /___.__. _______/ |_ \ \ | | \____ |__|/ |\__ ___/___ _______/ |_
280 : : \ /< | |/ ___/\ __\ \ \ | | / \| \ __\| |_/ __ \ / ___/\ __\
281 : : / \ \___ |\___ \ | | / / | | / | \ || | | |\ ___/ \___ \ | |
282 : : /___/\ \/ ____/____ > |__| /_/ |______/|___| /__||__| |____| \___ >____ > |__|
283 : : \_/\/ \/ \/ \/ \/)"
284 : 2 : << std::endl;
285 : 2 : }
286 : :
287 : : //! Print MeshConv header. Text ASCII Art Generator used for executable
288 : : //! names: http://patorjk.com/software/taag.
289 : 20 : void headerMeshConv() const {
290 : 20 : m_stream << R"(
291 : : ____ ___ __ __ _____ .__ _________
292 : : \ \/ /___.__. _______/ |_ \ \ / \ ____ _____| |__ \_ ___ \ ____ _______ __
293 : : \ /< | |/ ___/\ __\ \ \ / \ / \_/ __ \ / ___/ | \/ \ \/ / _ \ / \ \/ /
294 : : / \ \___ |\___ \ | | / / / Y \ ___/ \___ \| Y \ \___( <_> ) | \ /
295 : : /___/\ \/ ____/____ > |__| /_/ \____|__ /\___ >____ >___| /\______ /\____/|___| /\_/
296 : : \_/\/ \/ \/ \/ \/ \/ \/ \/)"
297 : 20 : << std::endl;
298 : 20 : }
299 : :
300 : : private:
301 : : std::ostream& m_stream; //!< Output stream
302 : :
303 : : //! Return human-readable test result based on result code
304 : : //! \param[in] code Result code
305 : : //! \param[in] msg Message to append
306 : : //! \param[in] ex Expection message to attach to exceptions cases
307 : 365 : std::string result( const std::string& code,
308 : : const std::string& msg,
309 : : const std::string& ex ) const
310 : : {
311 [ + - ][ + - ]: 365 : if (code == "0") return "Pass";
312 [ - - ]: 0 : else if (code == "1") return "Fail: " + msg;
313 [ - - ][ - - ]: 0 : else if (code == "2") return "Except: " + msg + ex;
[ - - ]
314 [ - - ]: 0 : else if (code == "3") return "Warning: " + msg;
315 [ - - ]: 0 : else if (code == "4") return "Terminate: " + msg;
316 [ - - ][ - - ]: 0 : else if (code == "5") return "Ex_ctor: " + msg + ex;
[ - - ]
317 [ - - ][ - - ]: 0 : else if (code == "6") return "Rethrown: " + msg + ex;
[ - - ]
318 [ - - ]: 0 : else if (code == "7") return "Skipped: " + msg;
319 [ - - ][ - - ]: 0 : else if (code == "8") return "Dummy";
320 [ - - ][ - - ]: 0 : else Throw( "No such unit test result code found" );
[ - - ]
321 : : }
322 : : };
323 : :
324 : : } // tk::
|