Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
360 views
in Technique[技术] by (71.8m points)

polymorphism - C++: How to implement polymorphic object creator to populate a table

I have a table widget object that can be resized with set_num_col(int) and set_num_row(int). A call to each of these functions will call a resize_table() function to populate the widget with table_cells objects. However, I have two polymorphic types of cells: table_cell_default and table_cell_custom, derived from the same base class. Upon creation of the table, how can I populate it with mixed types of cells, considering that the client knows which cells will be custom and which will be of default type?

I thought about adding a map in the table class, and populate this map with for example set_custom_cells( vector<index>() ), with the ij indices of the cells as keys and the corresponding lamda creator returning the correct type as value, but this map will only be used once to populate the table and never again. Is there a more dynamic way, using a lambda as a table_cell creator to fill that widget in a better way?

Thanks

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here's an example of using a factory lambda to produce the initial cells in the Table's constructor. Refer to main function where the lambda is located, and the Table constructor for how it is used.

I do not know what your code looks like, so I just wrap each cell into an object_t and put that into the Table.

#include <cstdint>
#include <functional>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <vector>

// Not idempotent.  Should be last include.
#include <cassert>

using std::cout;
using std::function;
using std::make_shared;
using std::move;
using std::ostream;
using std::shared_ptr;
using std::size_t;
using std::string;
using std::stringstream;
using std::vector;

namespace {

template <typename T>
void draw_right_justified(T const& x, ostream& out, size_t width) {
    stringstream ss;
    ss << x;
    string s = ss.str();
    size_t pad_width = s.length() < width ? width - s.length() : 1;
    out << string(pad_width, ' ') << s;
}

class object_t {
public:
    template <typename T>
    object_t(T x) : self_{make_shared<model<T>>(move(x))}
    { }

    friend void draw_right_justified(object_t const& x, ostream& out, size_t width) {
        x.self_->draw_right_justified_thunk(out, width);
    }

private:
    struct concept_t {
        virtual ~concept_t() = default;
        virtual void draw_right_justified_thunk(ostream&, size_t) const = 0;
    };

    template <typename T>
    struct model : concept_t {
        model(T x) : data_{move(x)} { }

        void draw_right_justified_thunk(ostream& out, size_t width) const {
            draw_right_justified(data_, out, width);
        }

        T data_;
    };

    shared_ptr<const concept_t> self_;
};

class Table {
    size_t col;
    size_t row;
    // data will be constructed with col_ * row_ entries.
    vector<object_t> data;
public:
    using object_factory = function<object_t(size_t, size_t)>;
    Table(size_t col_, size_t row_, object_factory& fn);
    auto operator()(size_t x, size_t y) const -> object_t;
    void display(ostream& out) const;
};

Table::Table(size_t col_, size_t row_, Table::object_factory& fn)
    : col{col_}, row{row_}
{
    data.reserve(col * row);
    for (size_t y = 0; y < row; ++y) {
        for (size_t x = 0; x < row; ++x) {
            data.emplace_back(fn(x, y));
        }
    }
}

object_t Table::operator()(size_t x, size_t y) const {
    assert(x < col);
    assert(y < row);
    return data[y * row + x];
}

void Table::display(ostream& out) const {
    auto const& self = *this;
    for (size_t y = 0; y < row; ++y) {
        for (size_t x = 0; x < col; ++x) {
            draw_right_justified(self(x, y), out, 8);
        }
        out << "
";
    }
}

struct empty_t {};

void draw_right_justified(empty_t, ostream& out, size_t width) {
    string s = "(empty)";
    size_t pad_width = s.length() < width ? width - s.length() : 1;
    out << string(pad_width, ' ') << s;
}

struct bunny { string name; };

void draw_right_justified(bunny const& bunny, ostream& out, size_t width) {
    auto const& s = bunny.name;
    size_t pad_width = s.length() < width ? width - s.length() : 1;
    out << string(pad_width, ' ') << s;
}

} // anon

int main() {
    Table::object_factory maker = [](size_t x, size_t y) {
        if (x == 0 && y == 1) return object_t{bunny{"Bugs"}};
        if (x == 0 && y == 0) return object_t{empty_t{}};
        if (x == y) return object_t{string("EQUAL")};
        return object_t{x * y};
    };

    auto table = Table{3, 5, maker};
    table.display(cout);
}

Output...

 (empty)       0       0
    Bugs   EQUAL       2
       0       2   EQUAL
       0       3       6
       0       4       8

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...