LCOV - code coverage report
Current view: top level - include/utils - VariadicTable.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 88 89 98.9 %
Date: 2026-05-29 20:35:17 Functions: 142 142 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      12             : /**
      13             :  * Note!  This class comes from https://github.com/friedmud/variadic_table
      14             :  *
      15             :  *
      16             :  * DO NOT MODIFY THIS CLASS HERE... MODIFY IT THERE AND COPY IT HERE
      17             :  *
      18             :  */
      19             : 
      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             : 
      29             : /**
      30             :  * Used to specify the column format
      31             :  */
      32             : enum class VariadicTableColumnFormat
      33             : {
      34             :   AUTO,
      35             :   SCIENTIFIC,
      36             :   FIXED,
      37             :   PERCENT
      38             : };
      39             : 
      40             : /**
      41             :  * A class for "pretty printing" a table of data.
      42             :  *
      43             :  * Requries C++11 (and nothing more)
      44             :  *
      45             :  * It's templated on the types that will be in each column
      46             :  * (all values in a column must have the same type)
      47             :  *
      48             :  * For instance, to use it with data that looks like:  "Fred", 193.4, 35, "Sam"
      49             :  * with header names: "Name", "Weight", "Age", "Brother"
      50             :  *
      51             :  * You would invoke the table like so:
      52             :  * VariadicTable<std::string, double, int, std::string> vt("Name", "Weight", "Age", "Brother");
      53             :  *
      54             :  * Then add the data to the table:
      55             :  * vt.addRow("Fred", 193.4, 35, "Sam");
      56             :  *
      57             :  * And finally print it:
      58             :  * vt.print();
      59             :  */
      60             : template <class... Ts>
      61             : class VariadicTable
      62             : {
      63             : public:
      64             :   /// The type stored for each row
      65             :   typedef std::tuple<Ts...> DataTuple;
      66             : 
      67             :   /**
      68             :    * Construct the table with headers
      69             :    *
      70             :    * @param headers The names of the columns
      71             :    * @param static_column_size The size of columns that can't be found automatically
      72             :    */
      73       61173 :   VariadicTable(std::vector<std::string> headers,
      74             :                 unsigned int static_column_size = 0,
      75             :                 unsigned int cell_padding = 1)
      76       61173 :     : _headers(headers),
      77       61173 :       _num_columns(std::tuple_size<DataTuple>::value),
      78       61173 :       _static_column_size(static_column_size),
      79       61173 :       _cell_padding(cell_padding)
      80             :   {
      81             :     assert(headers.size() == _num_columns);
      82       61173 :   }
      83             : 
      84             :   /**
      85             :    * Add a row of data
      86             :    *
      87             :    * Easiest to use like:
      88             :    * table.addRow({data1, data2, data3});
      89             :    *
      90             :    * @param data A Tuple of data to add
      91             :    */
      92      674643 :   void addRow(Ts... entries) { _data.emplace_back(std::make_tuple(entries...)); }
      93             : 
      94             :   /**
      95             :    * Pretty print the table of data
      96             :    */
      97             :   template <typename StreamType>
      98       58179 :   void print(StreamType & stream)
      99             :   {
     100       58179 :     size_columns();
     101             : 
     102             :     // Start computing the total width
     103             :     // First - we will have _num_columns + 1 "|" characters
     104       58179 :     unsigned int total_width = _num_columns + 1;
     105             : 
     106             :     // Now add in the size of each colum
     107      611333 :     for (auto & col_size : _column_sizes)
     108      553154 :       total_width += col_size + (2 * _cell_padding);
     109             : 
     110             :     // Print out the top line
     111       58480 :     stream << std::string(total_width, '-') << "\n";
     112             : 
     113             :     // Print out the headers
     114       58179 :     stream << "|";
     115      611333 :     for (unsigned int i = 0; i < _num_columns; i++)
     116             :     {
     117             :       // Must find the center of the column
     118      553154 :       auto half = _column_sizes[i] / 2;
     119      553154 :       half -= _headers[i].size() / 2;
     120             : 
     121     1107211 :       stream << std::string(_cell_padding, ' ') << std::setw(_column_sizes[i]) << std::left
     122     2765770 :              << std::string(half, ' ') + _headers[i] << std::string(_cell_padding, ' ') << "|";
     123             :     }
     124             : 
     125       58179 :     stream << "\n";
     126             : 
     127             :     // Print out the line below the header
     128       58480 :     stream << std::string(total_width, '-') << "\n";
     129             : 
     130             :     // Now print the rows of the table
     131      732822 :     for (auto & row : _data)
     132             :     {
     133      674643 :       stream << "|";
     134      674643 :       print_each(row, stream);
     135      674643 :       stream << "\n";
     136             :     }
     137             : 
     138             :     // Print out the line below the header
     139       58480 :     stream << std::string(total_width, '-') << std::endl;
     140       58179 :   }
     141             : 
     142             :   /**
     143             :    * Set how to format numbers for each column
     144             :    *
     145             :    * Note: this is ignored for std::string columns
     146             :    *
     147             :    * @column_format The format for each column: MUST be the same length as the number of columns.
     148             :    */
     149       55424 :   void setColumnFormat(const std::vector<VariadicTableColumnFormat> & column_format)
     150             :   {
     151             :     assert(column_format.size() == std::tuple_size<DataTuple>::value);
     152             : 
     153       55424 :     _column_format = column_format;
     154       55424 :   }
     155             : 
     156             :   /**
     157             :    * Set how many digits of precision to show for floating point numbers
     158             :    *
     159             :    * Note: this is ignored for std::string columns
     160             :    *
     161             :    * @column_format The precision for each column: MUST be the same length as the number of columns.
     162             :    */
     163       55424 :   void setColumnPrecision(const std::vector<int> & precision)
     164             :   {
     165             :     assert(precision.size() == std::tuple_size<DataTuple>::value);
     166       55424 :     _precision = precision;
     167       55424 :   }
     168             : 
     169             : protected:
     170             :   // Attempts to figure out the correct justification for the data
     171             :   template <typename T>
     172     6695879 :   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     6004239 :       return std::right;
     177             :     else
     178             :       // Otherwise
     179      691640 :       return std::left;
     180             :   }
     181             : 
     182             :   /**
     183             :    * These three functions print out each item in a Tuple into the table
     184             :    *
     185             :    * Original Idea From From https://stackoverflow.com/a/26908596
     186             :    *
     187             :    * BTW: This would all be a lot easier with generic lambdas
     188             :    * there would only need to be one of this sequence and then
     189             :    * you could pass in a generic lambda.  Unfortunately, that's C++14
     190             :    */
     191             : 
     192             :   /**
     193             :    * This gets called on each item
     194             :    */
     195             :   template <std::size_t I, typename TupleType, typename StreamType>
     196     6695879 :   void print_each(TupleType && t, StreamType & stream, std::integral_constant<size_t, I>)
     197             :   {
     198     6695879 :     auto & val = std::get<I>(t);
     199             : 
     200     6695879 :     auto original_precision = stream.precision();
     201     6695879 :     auto original_flags = stream.flags();
     202             : 
     203             :     // Set the precision
     204     6695879 :     if (!_precision.empty())
     205             :     {
     206             :       assert(_precision.size() ==
     207             :              std::tuple_size<typename std::remove_reference<TupleType>::type>::value);
     208             : 
     209     6676132 :       stream << std::setprecision(_precision[I]);
     210             :     }
     211             : 
     212             :     // Set the format
     213     6695879 :     if (!_column_format.empty())
     214             :     {
     215             :       assert(_column_format.size() ==
     216             :              std::tuple_size<typename std::remove_reference<TupleType>::type>::value);
     217             : 
     218     6676132 :       if (_column_format[I] == VariadicTableColumnFormat::SCIENTIFIC)
     219           0 :         stream << std::scientific;
     220             : 
     221     6676132 :       if (_column_format[I] == VariadicTableColumnFormat::FIXED)
     222     2665802 :         stream << std::fixed;
     223             : 
     224     6676132 :       if (_column_format[I] == VariadicTableColumnFormat::PERCENT)
     225     1332901 :         stream << std::fixed << std::setprecision(2);
     226             :     }
     227             : 
     228     6701858 :     stream << std::string(_cell_padding, ' ') << std::setw(_column_sizes[I])
     229    20087637 :            << justify<decltype(val)>(0) << val << std::string(_cell_padding, ' ') << "|";
     230             : 
     231             :     // Restore the format
     232     6695879 :     if (!_column_format.empty())
     233     6676132 :       stream.flags(original_flags);
     234             : 
     235             :     // Restore the precision
     236     6695879 :     if (!_precision.empty())
     237     6676132 :       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     6021236 :       print_each(std::forward<TupleType>(t), stream, std::integral_constant<size_t, I + 1>());
     242     6695879 :   }
     243             : 
     244             :   /**
     245             :    * This is what gets called first
     246             :    */
     247             :   template <typename TupleType, typename StreamType>
     248      674643 :   void print_each(TupleType && t, StreamType & stream)
     249             :   {
     250      674643 :     print_each(std::forward<TupleType>(t), stream, std::integral_constant<size_t, 0>());
     251      674643 :   }
     252             : 
     253             :   /**
     254             :    * Try to find the size the column will take up
     255             :    *
     256             :    * If the datatype has a size() member... let's call it
     257             :    */
     258             :   template <class T>
     259      691640 :   size_t sizeOfData(const T & data, decltype(((T *)nullptr)->size()) * /*dummy*/ = nullptr)
     260             :   {
     261      691640 :     return data.size();
     262             :   }
     263             : 
     264             :   /**
     265             :    * Try to find the size the column will take up
     266             :    *
     267             :    * If the datatype is an integer - let's get it's length
     268             :    */
     269             :   template <class T>
     270     2005536 :   size_t sizeOfData(const T & data,
     271             :                     typename std::enable_if<std::is_integral<T>::value>::type * /*dummy*/ = nullptr)
     272             :   {
     273     2005536 :     if (data == 0)
     274      976226 :       return 1;
     275             : 
     276     1029310 :     return std::log10(data) + 1;
     277             :   }
     278             : 
     279             :   /**
     280             :    * If it doesn't... let's just use a statically set size
     281             :    */
     282     3998703 :   size_t sizeOfData(...) { return _static_column_size; }
     283             : 
     284             :   /**
     285             :    * These three functions iterate over the Tuple, find the printed size of each element and set it
     286             :    * in a vector
     287             :    */
     288             : 
     289             :   /**
     290             :    * End the recursion
     291             :    */
     292             :   template <typename TupleType>
     293      674643 :   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      674643 :   }
     300             : 
     301             :   /**
     302             :    * Recursively called for each element
     303             :    */
     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     6695879 :   size_each(TupleType && t, std::vector<unsigned int> & sizes, std::integral_constant<size_t, I>)
     310             :   {
     311     6695879 :     sizes[I] = sizeOfData(std::get<I>(t));
     312             : 
     313             :     // Override for Percent
     314     6695879 :     if (!_column_format.empty())
     315     6676132 :       if (_column_format[I] == VariadicTableColumnFormat::PERCENT)
     316     1332901 :         sizes[I] = 6; // 100.00
     317             : 
     318             :     // Continue the recursion
     319     6695879 :     size_each(std::forward<TupleType>(t), sizes, std::integral_constant<size_t, I + 1>());
     320     6695879 :   }
     321             : 
     322             :   /**
     323             :    * The function that is actually called that starts the recursion
     324             :    */
     325             :   template <typename TupleType>
     326      674643 :   void size_each(TupleType && t, std::vector<unsigned int> & sizes)
     327             :   {
     328      674643 :     size_each(std::forward<TupleType>(t), sizes, std::integral_constant<size_t, 0>());
     329      674643 :   }
     330             : 
     331             :   /**
     332             :    * Finds the size each column should be and set it in _column_sizes
     333             :    */
     334       58179 :   void size_columns()
     335             :   {
     336       58179 :     _column_sizes.resize(_num_columns);
     337             : 
     338             :     // Temporary for querying each row
     339       58179 :     std::vector<unsigned int> column_sizes(_num_columns);
     340             : 
     341             :     // Start with the size of the headers
     342      611333 :     for (unsigned int i = 0; i < _num_columns; i++)
     343      553154 :       _column_sizes[i] = _headers[i].size();
     344             : 
     345             :     // Grab the size of each entry of each row and see if it's bigger
     346      732822 :     for (auto & row : _data)
     347             :     {
     348      674643 :       size_each(row, column_sizes);
     349             : 
     350     7370522 :       for (unsigned int i = 0; i < _num_columns; i++)
     351     6695879 :         _column_sizes[i] = std::max(_column_sizes[i], column_sizes[i]);
     352             :     }
     353       58179 :   }
     354             : 
     355             :   /// The column headers
     356             :   std::vector<std::string> _headers;
     357             : 
     358             :   /// Number of columns in the table
     359             :   unsigned int _num_columns;
     360             : 
     361             :   /// Size of columns that we can't get the size of
     362             :   unsigned int _static_column_size;
     363             : 
     364             :   /// Size of the cell padding
     365             :   unsigned int _cell_padding;
     366             : 
     367             :   /// The actual data
     368             :   std::vector<DataTuple> _data;
     369             : 
     370             :   /// Holds the printable width of each column
     371             :   std::vector<unsigned int> _column_sizes;
     372             : 
     373             :   /// Column Format
     374             :   std::vector<VariadicTableColumnFormat> _column_format;
     375             : 
     376             :   /// Precision For each column
     377             :   std::vector<int> _precision;
     378             : };

Generated by: LCOV version 1.14