C++: How to Implement Polymorphic Object Creator to Populate a Table

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

C++ Referencing inside a polymorphic object

How do I convince the compiler that it's OK to reference a, if I know
that the type of oPtr is A*?

Strictly I think the answer to that is: ((A*)(test->oPtr))->a. The better way to do that in C++ uses the cast operator: static_cast<A*>(test->oPtr)->a

HOWEVER This is not typically how this problem is addressed in c++. So I have provided a more usual approach that you may find useful:

class Poly
{
public:
virtual ~Poly() {}
virtual void do_something() = 0; // each sub-type has its own version of this
};

class A: public Poly
{
public:
void do_something() /* override */ // c++11 only
{
std::cout << "Doing something A specific\n";
}
};

class B: public Poly
{
public:
void do_something() /* override */ // c++11 only
{
std::cout << "Doing something B specific\n";
}
};

int main()
{
std::vector<Poly*> polys;

// create data structure
polys.push_back(new A);
polys.push_back(new A);
polys.push_back(new B);
polys.push_back(new A);

// use objects polymorphically
for(size_t i = 0; i < polys.size(); ++i)
polys[i]->do_something();

// clean up memory (consider using 'smart pointers')
for(size_t i = 0; i < polys.size(); ++i)
delete polys[i];
}

Modelling polymorphic associations database-first vs code-first

I personally stick with Database first when using EF on any schema that is this level of complexity. I have had issues with complex schemas in regards to code first. Maybe the newer versions are a little better, but worrying how to try and code complex relationships seems less straight forward then allowing the engine to generate it for you. Also when a relationship gets this complex I tend to avoid trying to generate it with EF and try and use stored procedures for easier troubleshooting of performance bottlenecks that can arise.

Best way to store an initializer list in a polymorphic vector

The first thing to note is that std::initializer_list<Parent<T>> will result in object slicing if some elements are initialized with Child<T> because the underlying array would hold Parent<T> objects.

There could be different solutions. For example, you could use a variadic template for the constructor to avoid slicing:

template<typename T>
struct Bar {
std::vector<std::unique_ptr<Parent<T>>> stuff;

template<typename... Us>
Bar(Us... us) {
stuff.reserve(sizeof...(Us));
(stuff.push_back(std::make_unique<Us>(std::move(us))), ...);
}
};

Here, (stuff.push_back(), ...) is a fold-expression that will be expanded into a sequence of push_back calls for each element of the us pack. Due to the const-ness of the initializer_list underlying array we can't do

Bar(Us... us) : stuff{std::make_unique<Us>(std::move(us))...}
{}

With C++17 deduction guides we can simplify the syntax a little bit.

template<typename> struct TypeTrait {};
template<typename T> struct TypeTrait<Parent<T>> { using Type = T; };
template<typename T> struct TypeTrait<Child<T>> { using Type = T; };

template<typename... Us>
Bar(Us...) -> Bar<std::common_type_t<typename TypeTrait<Us>::Type...>>;

Now we don't need to specify the template parameter explicitly, it will be deduced:

Parent<int> parent;
Child<int> child;
Bar bar{parent, child}; // T deduces to int

(If the member type Type could be put into Parent directly, a helper struct TypeTrait is not needed.)

How does the compiler determine which polymorphic type it is addressing

In effect, presuming a “normal” C implementation such as yours, much of the data of class B is a structure, and the first member of that structure is an object of class A. Since both B and the A that it contains start at the same place, they have the same address.

Because there is an A object at the start of B, you can convert a pointer to the B object to a pointer to the A object, and it acts like an A object because it is: It is a pointer to the A data.

Virtual functions are more complicated. Inside the A data, not usually visible to you, is a pointer to a table. If the object were really a plain A object, that pointer would point to a table that has the addresses of the virtual functions for A. If the object is a B object, then that pointer points to a table that has the addresses of the virtual functions for B. The result is that, if you have a pointer whose compile-time type appears to be pointer-to-A, the compiler calls its functions by looking up their addresses in the table. If the actual type of the pointed-to object is B, this table provides the addresses of the virtual functions for B.

sqlalchemy polymorphic many to many relation

Your example defines one-to-many relation between UserAction and News. It looks like mess to me since I see no reason why News inherits from UserAction. To allow several user actions refering single news you have to define intermediate table with two foreign keys: one refering to UserAction and other to News. I see two reasonable ways to make it polymorphic:

  1. Use separate intermediate table for each favourited model class and define different relations in each UserAction subclasses.
  2. Define separate foreign key for each favourited model in intermediate table and map it to class hierarchy with single-table inheritance (something like UserActionItem, UserActionNewsItem etc.).

But note, that all above is for linking UserAction and some entry models with many-to-many relation. While faouriting entries by users seems to me more like linking User with entry models.

Update: Below is working example. The only problem I see with it is that it allows duplicates.

from sqlalchemy import *
from sqlalchemy.orm import mapper, relation, sessionmaker
from sqlalchemy.ext.associationproxy import association_proxy

metadata = MetaData()

users = Table(
'users', metadata,
Column('id', Integer, nullable=False, primary_key=True),
)

news = Table(
'news', metadata,
Column('id', Integer, nullable=False, primary_key=True),
)

comments = Table(
'comments', metadata,
Column('id', Integer, nullable=False, primary_key=True),
)

favitems = Table(
'favitems', metadata,
Column('id', Integer, nullable=False, primary_key=True),
Column('user_id', Integer, ForeignKey(users.c.id), nullable=False),
Column('item_type', Integer, nullable=False),
Column('news_id', Integer, ForeignKey(news.c.id)),
Column('comment_id', Integer, ForeignKey(comments.c.id)),
)

class News(object): pass

class Comment(object): pass

class FavItem(object):
TYPE_NEWS = 1
TYPE_COMMENT = 2
def __new__(cls, item=None):
if isinstance(item, News):
cls = FavNews
elif isinstance(item, Comment):
cls = FavComment
return object.__new__(cls)

class FavNews(FavItem):
def __init__(self, item):
self.item_type = self.TYPE_NEWS
self.item = item

class FavComment(FavItem):
def __init__(self, item):
self.item_type = self.TYPE_COMMENT
self.item = item

class User(object):
favorites = association_proxy('_favitems', 'item', creator=FavItem)

mapper(News, news)

mapper(Comment, comments)

mapper(FavItem, favitems,
polymorphic_on=favitems.c.item_type)

mapper(FavNews, favitems,
inherits=FavItem,
polymorphic_identity=FavItem.TYPE_NEWS,
properties={
'item': relation(News),
})

mapper(FavComment, favitems,
inherits=FavItem,
polymorphic_identity=FavItem.TYPE_COMMENT,
properties={
'item': relation(Comment),
})

mapper(User, users,
properties={
'_favitems': relation(FavItem),
})

engine = create_engine('sqlite://')
metadata.create_all(engine)
session = sessionmaker(engine)()

user = User()
news1 = News()
news2 = News()
comment1 = Comment()
comment2 = Comment()
user.favorites = [news1, news2, comment1, comment2]
session.add(user)
session.commit()
user_id = user.id

session.expunge_all()
user = session.query(User).get(user_id)
print user.favorites


Related Topics



Leave a reply



Submit