https://mooseframework.inl.gov
VariadicTable.h
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #pragma once
11 
20 #include <iostream>
21 #include <iomanip>
22 #include <ios>
23 #include <vector>
24 #include <tuple>
25 #include <type_traits>
26 #include <cassert>
27 #include <cmath>
28 
33 {
34  AUTO,
35  SCIENTIFIC,
36  FIXED,
37  PERCENT
38 };
39 
60 template <class... Ts>
61 class VariadicTable
62 {
63 public:
65  typedef std::tuple<Ts...> DataTuple;
66 
73  VariadicTable(std::vector<std::string> headers,
74  unsigned int static_column_size = 0,
75  unsigned int cell_padding = 1)
76  : _headers(headers),
77  _num_columns(std::tuple_size<DataTuple>::value),
78  _static_column_size(static_column_size),
79  _cell_padding(cell_padding)
80  {
81  assert(headers.size() == _num_columns);
82  }
83 
92  void addRow(Ts... entries) { _data.emplace_back(std::make_tuple(entries...)); }
93 
97  template <typename StreamType>
98  void print(StreamType & stream)
99  {
100  size_columns();
101 
102  // Start computing the total width
103  // First - we will have _num_columns + 1 "|" characters
104  unsigned int total_width = _num_columns + 1;
105 
106  // Now add in the size of each colum
107  for (auto & col_size : _column_sizes)
108  total_width += col_size + (2 * _cell_padding);
109 
110  // Print out the top line
111  stream << std::string(total_width, '-') << "\n";
112 
113  // Print out the headers
114  stream << "|";
115  for (unsigned int i = 0; i < _num_columns; i++)
116  {
117  // Must find the center of the column
118  auto half = _column_sizes[i] / 2;
119  half -= _headers[i].size() / 2;
120 
121  stream << std::string(_cell_padding, ' ') << std::setw(_column_sizes[i]) << std::left
122  << std::string(half, ' ') + _headers[i] << std::string(_cell_padding, ' ') << "|";
123  }
124 
125  stream << "\n";
126 
127  // Print out the line below the header
128  stream << std::string(total_width, '-') << "\n";
129 
130  // Now print the rows of the table
131  for (auto & row : _data)
132  {
133  stream << "|";
134  print_each(row, stream);
135  stream << "\n";
136  }
137 
138  // Print out the line below the header
139  stream << std::string(total_width, '-') << std::endl;
140  }
141 
149  void setColumnFormat(const std::vector<VariadicTableColumnFormat> & column_format)
150  {
151  assert(column_format.size() == std::tuple_size<DataTuple>::value);
152 
153  _column_format = column_format;
154  }
155 
163  void setColumnPrecision(const std::vector<int> & precision)
164  {
165  assert(precision.size() == std::tuple_size<DataTuple>::value);
166  _precision = precision;
167  }
168 
169 protected:
170  // Attempts to figure out the correct justification for the data
171  template <typename T>
172  static auto justify(int /*firstchoice*/)
173  {
174  if constexpr (std::is_arithmetic<typename std::remove_reference<T>::type>::value)
175  // If it's a floating point value
176  return std::right;
177  else
178  // Otherwise
179  return std::left;
180  }
181 
195  template <std::size_t I, typename TupleType, typename StreamType>
196  void print_each(TupleType && t, StreamType & stream, std::integral_constant<size_t, I>)
197  {
198  auto & val = std::get<I>(t);
199 
200  auto original_precision = stream.precision();
201  auto original_flags = stream.flags();
202 
203  // Set the precision
204  if (!_precision.empty())
205  {
206  assert(_precision.size() ==
207  std::tuple_size<typename std::remove_reference<TupleType>::type>::value);
208 
209  stream << std::setprecision(_precision[I]);
210  }
211 
212  // Set the format
213  if (!_column_format.empty())
214  {
215  assert(_column_format.size() ==
216  std::tuple_size<typename std::remove_reference<TupleType>::type>::value);
217 
219  stream << std::scientific;
220 
222  stream << std::fixed;
223 
225  stream << std::fixed << std::setprecision(2);
226  }
227 
228  stream << std::string(_cell_padding, ' ') << std::setw(_column_sizes[I])
229  << justify<decltype(val)>(0) << val << std::string(_cell_padding, ' ') << "|";
230 
231  // Restore the format
232  if (!_column_format.empty())
233  stream.flags(original_flags);
234 
235  // Restore the precision
236  if (!_precision.empty())
237  stream.precision(original_precision);
238 
239  // Recursive call to print the next item (if there are any left)
240  if constexpr (I + 1 != std::tuple_size<typename std::remove_reference<TupleType>::type>::value)
241  print_each(std::forward<TupleType>(t), stream, std::integral_constant<size_t, I + 1>());
242  }
243 
247  template <typename TupleType, typename StreamType>
248  void print_each(TupleType && t, StreamType & stream)
249  {
250  print_each(std::forward<TupleType>(t), stream, std::integral_constant<size_t, 0>());
251  }
252 
258  template <class T>
259  size_t sizeOfData(const T & data, decltype(((T *)nullptr)->size()) * /*dummy*/ = nullptr)
260  {
261  return data.size();
262  }
263 
269  template <class T>
270  size_t sizeOfData(const T & data,
271  typename std::enable_if<std::is_integral<T>::value>::type * /*dummy*/ = nullptr)
272  {
273  if (data == 0)
274  return 1;
275 
276  return std::log10(data) + 1;
277  }
278 
282  size_t sizeOfData(...) { return _static_column_size; }
283 
292  template <typename TupleType>
293  void size_each(TupleType &&,
294  std::vector<unsigned int> & /*sizes*/,
295  std::integral_constant<
296  size_t,
297  std::tuple_size<typename std::remove_reference<TupleType>::type>::value>)
298  {
299  }
300 
304  template <std::size_t I,
305  typename TupleType,
306  typename = typename std::enable_if<
307  I != std::tuple_size<typename std::remove_reference<TupleType>::type>::value>::type>
308  void
309  size_each(TupleType && t, std::vector<unsigned int> & sizes, std::integral_constant<size_t, I>)
310  {
311  sizes[I] = sizeOfData(std::get<I>(t));
312 
313  // Override for Percent
314  if (!_column_format.empty())
316  sizes[I] = 6; // 100.00
317 
318  // Continue the recursion
319  size_each(std::forward<TupleType>(t), sizes, std::integral_constant<size_t, I + 1>());
320  }
321 
325  template <typename TupleType>
326  void size_each(TupleType && t, std::vector<unsigned int> & sizes)
327  {
328  size_each(std::forward<TupleType>(t), sizes, std::integral_constant<size_t, 0>());
329  }
330 
335  {
336  _column_sizes.resize(_num_columns);
337 
338  // Temporary for querying each row
339  std::vector<unsigned int> column_sizes(_num_columns);
340 
341  // Start with the size of the headers
342  for (unsigned int i = 0; i < _num_columns; i++)
343  _column_sizes[i] = _headers[i].size();
344 
345  // Grab the size of each entry of each row and see if it's bigger
346  for (auto & row : _data)
347  {
348  size_each(row, column_sizes);
349 
350  for (unsigned int i = 0; i < _num_columns; i++)
351  _column_sizes[i] = std::max(_column_sizes[i], column_sizes[i]);
352  }
353  }
354 
356  std::vector<std::string> _headers;
357 
359  unsigned int _num_columns;
360 
362  unsigned int _static_column_size;
363 
365  unsigned int _cell_padding;
366 
368  std::vector<DataTuple> _data;
369 
371  std::vector<unsigned int> _column_sizes;
372 
374  std::vector<VariadicTableColumnFormat> _column_format;
375 
377  std::vector<int> _precision;
378 };
size_t sizeOfData(...)
If it doesn&#39;t...
std::tuple< Ts... > DataTuple
The type stored for each row.
Definition: VariadicTable.h:65
std::vector< int > _precision
Precision For each column.
void size_each(TupleType &&, std::vector< unsigned int > &, std::integral_constant< size_t, std::tuple_size< typename std::remove_reference< TupleType >::type >::value >)
These three functions iterate over the Tuple, find the printed size of each element and set it in a v...
void print(StreamType &stream)
Pretty print the table of data.
Definition: VariadicTable.h:98
A class for "pretty printing" a table of data.
Definition: PerfGraph.h:34
size_t sizeOfData(const T &data, decltype(((T *) nullptr) ->size()) *=nullptr)
Try to find the size the column will take up.
void setColumnPrecision(const std::vector< int > &precision)
Set how many digits of precision to show for floating point numbers.
unsigned int _cell_padding
Size of the cell padding.
VariadicTableColumnFormat
Note! This class comes from https://github.com/friedmud/variadic_table.
Definition: VariadicTable.h:32
std::vector< VariadicTableColumnFormat > _column_format
Column Format.
std::vector< DataTuple > _data
The actual data.
static auto justify(int)
auto max(const L &left, const R &right)
unsigned int _num_columns
Number of columns in the table.
VariadicTable(std::vector< std::string > headers, unsigned int static_column_size=0, unsigned int cell_padding=1)
Construct the table with headers.
Definition: VariadicTable.h:73
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
void size_each(TupleType &&t, std::vector< unsigned int > &sizes)
The function that is actually called that starts the recursion.
void addRow(Ts... entries)
Add a row of data.
Definition: VariadicTable.h:92
void setColumnFormat(const std::vector< VariadicTableColumnFormat > &column_format)
Set how to format numbers for each column.
unsigned int _static_column_size
Size of columns that we can&#39;t get the size of.
void print_each(TupleType &&t, StreamType &stream)
This is what gets called first.
std::vector< unsigned int > _column_sizes
Holds the printable width of each column.
void print_each(TupleType &&t, StreamType &stream, std::integral_constant< size_t, I >)
These three functions print out each item in a Tuple into the table.
std::vector< std::string > _headers
The column headers.
void size_columns()
Finds the size each column should be and set it in _column_sizes.
void size_each(TupleType &&t, std::vector< unsigned int > &sizes, std::integral_constant< size_t, I >)
Recursively called for each element.
size_t sizeOfData(const T &data, typename std::enable_if< std::is_integral< T >::value >::type *=nullptr)
Try to find the size the column will take up.