从结构向量打印数据网格

时间:2018-10-08 12:12:02

标签: c++ std-variant

我创建了一个呈现数据表的代码。我想使其更通用。

我的代码:

#include <algorithm>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>

struct Student
{
    std::string name;
    unsigned points;
    unsigned height;
    unsigned weight;
};

namespace console
{
    constexpr unsigned max_chars_in_line = 79;

    void draw_line(unsigned count = max_chars_in_line, char sign = '-')
    {
        std::cout << std::string(count, sign) << "\n";
    }
}

int main()
{
    std::cout << std::fixed << std::setprecision(2);

    std::vector<Student> students
    {
        { "John", 30, 200, 300 },
        { "Peter", 80, 160, 80 },
        { "Albert", 200, 175, 1230 },
        { "Hans", 100, 178, 130 }
    };

    console::draw_line(45);
    std::cout 
        << std::right 
        << std::setw(10) << "name"
        << std::setw(10) << "points" 
        << std::setw(10) << "height" 
        << std::setw(10) << "weight" 
        << "\n";
    console::draw_line(45);
    for (auto& student : students)
    {
        std::cout 
            << std::setw(10) << student.name
            << std::setw(10) << student.points
            << std::setw(10) << student.height 
            << std::setw(10) << student.weight
            << "\n";
    }
    console::draw_line(45);

    std::cout << "\n\n";

    auto total_weight = std::accumulate(students.begin(), students.end(), 0u, [](unsigned sum, const Student& student) {return sum += student.weight; });
    std::sort(students.begin(), students.end(), [](const Student& a, const Student& b) {return a.weight > b.weight; });

    console::draw_line(40);
    std::cout
        << std::setw(10) << "name"
        << std::setw(10) << "weight"
        << std::setw(15) << "percent (%)"
        << "\n";
    console::draw_line(40);

    for (auto& student : students)
    {
        std::cout
            << std::setw(10) << student.name
            << std::setw(10) << student.weight
            << std::setw(15) << 100 * static_cast<double>(student.weight) / total_weight
            << "\n";
    }
    console::draw_line(40);

    std::cout
        << std::setw(10) << "total"
        << std::setw(10) << total_weight
        << std::setw(15) << 100.
        << "\n";
    console::draw_line(40);
}

我希望代码看起来如何:

#include <algorithm>
#include <iomanip>
#include <iostream>
#include <functional>
#include <fstream>
#include <numeric>
#include <string>
#include <vector>

struct Student
{
    std::string name;
    unsigned points;
    unsigned height;
    unsigned weight;
};

class GridView { /*...*/ };

int main()
{
    std::cout << std::fixed << std::setprecision(2);

    std::vector<Student> students
    {
        { "John", 30, 200, 300 },
        { "Peter", 80, 160, 80 },
        { "Albert", 200, 175, 1230 },
        { "Hans", 100, 178, 130 }
    };

    GridView<Student> diagram1(&students); // pointer?
    diagram1.addCol("name", &Student::name);
    diagram1.addCol("points", &Student::points);
    diagram1.addCol("height", &Student::height);
    diagram1.addCol("weight", &Student::weight);
    diagram1.print(std::cout); // or |std::cout << diagram1;|

    std::cout << "\n\n";

    auto total_weight = std::accumulate(students.begin(), students.end(), 0u, [](unsigned sum, const Student& student) {return sum += student.weight; });
    std::sort(students.begin(), students.end(), [](const Student& a, const Student& b) {return a.weight > b.weight; });

    std::ofstream file("grid.txt");
    if (!file.is_open())
    {
        return -1;
    }

    GridView<Student> diagram2(&students);
    diagram2.addCol("name", &Student::name, 10);
    diagram2.addCol("weight", &Student::weight, 10);
    diagram2.addCol("percent (%)", [=]() {return 100 * static_cast<double>(Student::weight) / total_weight; }, 15); // bind? how to do it?
    diagram2.addFooter("total", total_weight, 100.);
    diagram2.print(file);
}

我应该如何开始编写GridView类?加百分数的列应该是什么样的?也许我可以做得更好(更好)?看来我的帖子大部分是代码;抱歉,细节太少了。

编辑:代码:

#include <algorithm>
#include <iomanip>
#include <iostream>
#include <functional>
#include <fstream>
#include <numeric>
#include <string>
#include <vector>
#include <variant>

struct Student
{
    std::string name;
    unsigned points;
    unsigned height;
    unsigned weight;
};

template <typename DataType>
class GridView 
{
    std::vector<DataType>* data_ptr;

    using variant_cell_type = std::variant<int, float, double, unsigned, std::string>;

    struct output_visitor
    {
        std::ostream& out;

        output_visitor(std::ostream& out)
            :out(out)
        {

        }

        template <typename T>
        void operator() (const T& value) const
        {
            out << value;
        }
    };

    struct Col
    {
        std::string name;
        std::function<variant_cell_type(DataType&)> access_data;
        unsigned width;
    };

    std::vector<Col> cols;

    unsigned max_chars_in_line;

    std::vector<variant_cell_type> footer;

public:
    GridView(std::vector<DataType>* data_ptr)
        :data_ptr(data_ptr)
    {
        max_chars_in_line = 5;
    }

    template <class Magic, class Type>
    void addCol(const std::string& colName, Magic Type::* ceilType, unsigned width = 10)
    {
        cols.push_back({colName, std::mem_fn(ceilType), width});
        max_chars_in_line += width;
    }

    void addFooter(const std::vector<variant_cell_type>& footer)
    {
        this->footer = footer;
    }

    void print(std::ostream& out)
    {
        using std::string;

        out << std::right;
        out << string(max_chars_in_line, '-') << "\n";
        for (auto& col : cols)
        {
            out << std::setw(col.width) << col.name;
        }
        out << "\n";
        out << string(max_chars_in_line, '-') << "\n";

        for (auto student : *data_ptr)
        {
            for (auto& col : cols)
            {
                out << std::setw(col.width);
                std::visit(output_visitor{out}, col.access_data(student));
            }
            out << "\n";
        }

        if (footer.size() != 0)
        {
            if (footer.size() != cols.size())
            {
                throw std::invalid_argument("Footer must have " + std::to_string(cols.size()) + " arguments.");
            }
            out << string(max_chars_in_line, '-') << "\n";
            auto foo = std::begin(footer);
            auto col = std::begin(cols);
            for (; foo != footer.end(); ++foo, ++col)
            {
                out << std::setw(col->width);
                std::visit(output_visitor{ out }, *foo);
            }
            out << "\n";
        }

        out << string(max_chars_in_line, '-') << "\n";
    }
};

int main()
{
    std::cout << std::fixed << std::setprecision(2);

    std::vector<Student> students
    {
        { "John", 30, 200, 300 },
        { "Peter", 80, 160, 80 },
        { "Albert", 200, 175, 1230 },
        { "Hans", 100, 178, 130 }
    };

    try
    {
        GridView<Student> diagram1(&students);
        diagram1.addCol("name", &Student::name);
        diagram1.addCol("points", &Student::points);
        diagram1.addCol("height", &Student::height);
        diagram1.addCol("weight", &Student::weight);
        diagram1.print(std::cout);

        std::cout << "\n\n";

        auto total_weight = std::accumulate(students.begin(), students.end(), 0u, [](unsigned sum, const Student& student) {return sum += student.weight; });
        std::sort(students.begin(), students.end(), [](const Student& a, const Student& b) {return a.weight > b.weight; });

        GridView<Student> diagram2(&students);
        diagram2.addCol("name", &Student::name, 10);
        diagram2.addCol("weight", &Student::weight, 10);
        //auto calculate_weight = [=](Student student) {return 100 * static_cast<double>(student.weight) / total_weight; };
        //diagram2.addCol("percent (%)", calculate_weight, 15); // bind? how to do it?
        diagram2.addFooter({ "total", total_weight/*, 100.*/ });
        diagram2.print(std::cout);
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << "\n";
    }
}

我该怎么做才能使注释片段起作用?

0 个答案:

没有答案