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

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 << "\n";
    }
}

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

Leave a Comment