// inspired by github.com/dattanchu/bprinter
//#pragma once
#include <map>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
#include <stdexcept>
#include <sstream>
#ifdef auto_table_USE_SCOPED_COLOR
#include "scoped_color.h"
#endif
namespace auto_table {
template <typename K, typename V>
struct sum_values {
V operator()(V i, const std::pair<const K, V>& x) { return i + x.second; }
};
struct endl {};
template <typename my_stream_t = std::stringstream>
class auto_table {
my_stream_t my_stream;
typedef std::vector<std::string> row_t;
typedef std::vector<row_t> rows_t;
typedef std::map<size_t, size_t> column_widths_t;
column_widths_t column_widths;
rows_t rows;
size_t header_every_nth_row;
size_t horizontal_padding;
public:
auto_table() : header_every_nth_row(0), horizontal_padding(0) {}
private:
static size_t width(std::string const& s) { return s.length(); }
static size_t combine_width(size_t one_width, size_t another_width) {
return std::max(one_width, another_width);
}
private:
template <typename stream_t>
void print_horizontal_line(stream_t& stream) {
stream << '+';
size_t sum = std::accumulate(column_widths.begin(), column_widths.end(), 0,
sum_values<size_t, size_t>());
for (size_t i = 0;
i < sum + column_widths.size() +
2 * column_widths.size() * horizontal_padding - 1;
i++)
stream << '-';
stream << "+\n";
}
std::string pad_column(std::string const& s, size_t column) {
size_t s_width = width(s);
size_t column_width = column_widths[column];
if (s_width > column_width) return s;
return std::string(column_width - s_width + horizontal_padding, ' ') + s +
std::string(horizontal_padding, ' ');
}
template <typename stream_t>
void print_row(size_t r, stream_t& stream, int color = 7) {
size_t column_count = column_widths.size();
row_t const& row = rows[r];
size_t header_count = row.size();
for (size_t i = 0; i < header_count; i++) {
stream << '|';
#ifdef auto_table_USE_SCOPED_COLOR
scoped_console_color col(color);
#endif
stream << pad_column(row[i], i);
}
for (size_t i = header_count; i < column_count; i++)
stream << '|' << pad_column("", i);
stream << "|\n";
}
template <typename stream_t>
void print_header(stream_t& stream) {
print_horizontal_line(stream);
if (rows.size() > 0) {
print_row(0, stream, 10);
print_horizontal_line(stream);
}
}
template <typename stream_t>
void print_rows(stream_t& stream) {
size_t row_count = rows.size();
if (row_count == 0) return;
for (size_t row = 1; row < row_count - 1; row++) {
if (row > 1 && header_every_nth_row &&
(row - 1) % header_every_nth_row == 0)
print_header(stream);
print_row(row, stream, 15);
}
if (rows[row_count - 1].size() > 0) print_row(row_count - 1, stream, 15);
}
template <typename stream_t>
void print_footer(stream_t& stream) {
print_horizontal_line(stream);
}
public:
auto_table& add_column(std::string const& name, size_t min_width = 0) {
size_t new_width = combine_width(width(name), min_width);
column_widths[column_widths.size()] = new_width;
if (rows.size() < 1) rows.push_back(row_t());
rows.front().push_back(name);
return *this;
}
auto_table& with_header_every_nth_row(size_t n) {
header_every_nth_row = n;
return *this;
}
auto_table& with_horizontal_padding(size_t n) {
horizontal_padding = n;
return *this;
}
auto_table& operator<<(::auto_table::endl const& input) {
rows.push_back(row_t());
return *this;
}
template <typename TPar>
auto_table& operator<<(TPar const& input) {
if (column_widths.size() == 0)
throw std::runtime_error("no columns defined!");
if (rows.size() < 1) rows.push_back(row_t());
if (rows.back().size() >= column_widths.size()) rows.push_back(row_t());
my_stream << input;
std::string entry(my_stream.str());
size_t column = rows.back().size();
size_t new_width = combine_width(width(entry), column_widths[column]);
column_widths[column] = new_width;
rows.back().push_back(entry);
my_stream.str("");
return *this;
}
template <typename stream_t>
void print(stream_t& stream) {
this->print_header(stream);
this->print_rows(stream);
this->print_footer(stream);
}
my_stream_t& get_stream() { return my_stream; }
};
typedef auto_table<> printer;
}
#include <random>
#include <iostream>
int main() {
auto_table::printer tp;
tp
.add_column("Name")
.add_column("Age")
.add_column("Position")
.add_column("Allowance")
.with_header_every_nth_row(3)
.with_horizontal_padding(1)
;
tp << "Dat Chu" << 25 << "Research Assistant" << -0.00000000001337;
tp << "John Doe" << 26 << "Too much float" << 125456789.123456789;
tp << "John Doe" << 26 << "Typical Int" << 1254;
tp << "John Doe" << 26 << "Typical float" << 1254.36;
tp << "John Doe" << 26 << "Too much negative" << -125456789.123456789;
tp << "John Doe" << 26 << "Exact size int" << 125456789;
tp << "John Doe" << 26 << "Exact size int" << -12545678;
tp << "John Doe" << 26 << "Exact size float" << -1254567.8;
tp << "John Doe" << 26 << "Negative Int" << -1254;
tp << "Jane Doe" << auto_table::endl();
tp << "Tom Doe" << 7 << "Student" << -3.14;
tp.print(std::cout);
tp
.with_header_every_nth_row(0)
.with_horizontal_padding(0)
.print(std::cout)
;
}