www.mooseframework.org
VariadicTable.h
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
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 
19 #include <iostream>
20 #include <iomanip>
21 #include <ios>
22 #include <vector>
23 #include <tuple>
24 #include <type_traits>
25 #include <cassert>
26 #include <cmath>
27 
32 {
33  AUTO,
34  SCIENTIFIC,
35  FIXED,
36  PERCENT
37 };
38 
59 template <class... Ts>
60 class VariadicTable
61 {
62 public:
64  typedef std::tuple<Ts...> DataTuple;
65 
72  VariadicTable(std::vector<std::string> headers,
73  unsigned int static_column_size = 0,
74  unsigned int cell_padding = 1)
75  : _headers(headers),
76  _num_columns(std::tuple_size<DataTuple>::value),
77  _static_column_size(static_column_size),
78  _cell_padding(cell_padding)
79  {
80  assert(headers.size() == _num_columns);
81  }
82 
91  void addRow(Ts... entries) { _data.emplace_back(std::make_tuple(entries...)); }
92 
96  template <typename StreamType>
97  void print(StreamType & stream)
98  {
99  size_columns();
100 
101  // Start computing the total width
102  // First - we will have _num_columns + 1 "|" characters
103  unsigned int total_width = _num_columns + 1;
104 
105  // Now add in the size of each colum
106  for (auto & col_size : _column_sizes)
107  total_width += col_size + (2 * _cell_padding);
108 
109  // Print out the top line
110  stream << std::string(total_width, '-') << "\n";
111 
112  // Print out the headers
113  stream << "|";
114  for (unsigned int i = 0; i < _num_columns; i++)
115  {
116  // Must find the center of the column
117  auto half = _column_sizes[i] / 2;
118  half -= _headers[i].size() / 2;
119 
120  stream << std::string(_cell_padding, ' ') << std::setw(_column_sizes[i]) << std::left
121  << std::string(half, ' ') + _headers[i] << std::string(_cell_padding, ' ') << "|";
122  }
123 
124  stream << "\n";
125 
126  // Print out the line below the header
127  stream << std::string(total_width, '-') << "\n";
128 
129  // Now print the rows of the table
130  for (auto & row : _data)
131  {
132  stream << "|";
133  print_each(row, stream);
134  stream << "\n";
135  }
136 
137  // Print out the line below the header
138  stream << std::string(total_width, '-') << std::endl;
139  }
140 
148  void setColumnFormat(const std::vector<VariadicTableColumnFormat> & column_format)
149  {
150  assert(column_format.size() == std::tuple_size<DataTuple>::value);
151 
152  _column_format = column_format;
153  }
154 
162  void setColumnPrecision(const std::vector<int> & precision)
163  {
164  assert(precision.size() == std::tuple_size<DataTuple>::value);
165  _precision = precision;
166  }
167 
168 protected:
169  // Attempts to figure out the correct justification for the data
170  template <typename T>
171  static auto justify(int /*firstchoice*/)
172  {
173  if constexpr (std::is_arithmetic<typename std::remove_reference<T>::type>::value)
174  // If it's a floating point value
175  return std::right;
176  else
177  // Otherwise
178  return std::left;
179  }
180 
194  template <std::size_t I, typename TupleType, typename StreamType>
195  void print_each(TupleType && t, StreamType & stream, std::integral_constant<size_t, I>)
196  {
197  auto & val = std::get<I>(t);
198 
199  auto original_precision = stream.precision();
200  auto original_flags = stream.flags();
201 
202  // Set the precision
203  if (!_precision.empty())
204  {
205  assert(_precision.size() ==
206  std::tuple_size<typename std::remove_reference<TupleType>::type>::value);
207 
208  stream << std::setprecision(_precision[I]);
209  }
210 
211  // Set the format
212  if (!_column_format.empty())
213  {
214  assert(_column_format.size() ==
215  std::tuple_size<typename std::remove_reference<TupleType>::type>::value);
216 
218  stream << std::scientific;
219 
221  stream << std::fixed;
222 
224  stream << std::fixed << std::setprecision(2);
225  }
226 
227  stream << std::string(_cell_padding, ' ') << std::setw(_column_sizes[I])
228  << justify<decltype(val)>(0) << val << std::string(_cell_padding, ' ') << "|";
229 
230  // Restore the format
231  if (!_column_format.empty())
232  stream.flags(original_flags);
233 
234  // Restore the precision
235  if (!_precision.empty())
236  stream.precision(original_precision);
237 
238  // Recursive call to print the next item (if there are any left)
239  if constexpr (I + 1 != std::tuple_size<typename std::remove_reference<TupleType>::type>::value)
240  print_each(std::forward<TupleType>(t), stream, std::integral_constant<size_t, I + 1>());
241  }
242 
246  template <typename TupleType, typename StreamType>
247  void print_each(TupleType && t, StreamType & stream)
248  {
249  print_each(std::forward<TupleType>(t), stream, std::integral_constant<size_t, 0>());
250  }
251 
257  template <class T>
258  size_t sizeOfData(const T & data, decltype(((T *)nullptr)->size()) * /*dummy*/ = nullptr)
259  {
260  return data.size();
261  }
262 
268  template <class T>
269  size_t sizeOfData(const T & data,
270  typename std::enable_if<std::is_integral<T>::value>::type * /*dummy*/ = nullptr)
271  {
272  if (data == 0)
273  return 1;
274 
275  return std::log10(data) + 1;
276  }
277 
281  size_t sizeOfData(...) { return _static_column_size; }
282 
291  template <typename TupleType>
292  void size_each(TupleType &&,
293  std::vector<unsigned int> & /*sizes*/,
294  std::integral_constant<
295  size_t,
296  std::tuple_size<typename std::remove_reference<TupleType>::type>::value>)
297  {
298  }
299 
303  template <std::size_t I,
304  typename TupleType,
305  typename = typename std::enable_if<
306  I != std::tuple_size<typename std::remove_reference<TupleType>::type>::value>::type>
307  void
308  size_each(TupleType && t, std::vector<unsigned int> & sizes, std::integral_constant<size_t, I>)
309  {
310  sizes[I] = sizeOfData(std::get<I>(t));
311 
312  // Override for Percent
313  if (!_column_format.empty())
315  sizes[I] = 6; // 100.00
316 
317  // Continue the recursion
318  size_each(std::forward<TupleType>(t), sizes, std::integral_constant<size_t, I + 1>());
319  }
320 
324  template <typename TupleType>
325  void size_each(TupleType && t, std::vector<unsigned int> & sizes)
326  {
327  size_each(std::forward<TupleType>(t), sizes, std::integral_constant<size_t, 0>());
328  }
329 
334  {
335  _column_sizes.resize(_num_columns);
336 
337  // Temporary for querying each row
338  std::vector<unsigned int> column_sizes(_num_columns);
339 
340  // Start with the size of the headers
341  for (unsigned int i = 0; i < _num_columns; i++)
342  _column_sizes[i] = _headers[i].size();
343 
344  // Grab the size of each entry of each row and see if it's bigger
345  for (auto & row : _data)
346  {
347  size_each(row, column_sizes);
348 
349  for (unsigned int i = 0; i < _num_columns; i++)
350  _column_sizes[i] = std::max(_column_sizes[i], column_sizes[i]);
351  }
352  }
353 
355  std::vector<std::string> _headers;
356 
358  unsigned int _num_columns;
359 
361  unsigned int _static_column_size;
362 
364  unsigned int _cell_padding;
365 
367  std::vector<DataTuple> _data;
368 
370  std::vector<unsigned int> _column_sizes;
371 
373  std::vector<VariadicTableColumnFormat> _column_format;
374 
376  std::vector<int> _precision;
377 };
size_t sizeOfData(...)
If it doesn&#39;t...
std::tuple< Ts... > DataTuple
The type stored for each row.
Definition: VariadicTable.h:64
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:97
A class for "pretty printing" a table of data.
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:31
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:72
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:91
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.