Is there a way to do
container< container<Base> >
When you have a bunch of container<Derived>s that you wish to keep together (to iterator over?)
A concrete example follows.
Say you have
struct Animal { } ;
struct Dog : public Animal { } ;
struct StripedDog : public Dog { } ;
struct Cat : public Animal { } ;
struct SpottedCat : public Cat { } ;
You wish to keep collections of Cats, SpottedCats, Dogs & StripedDogs, in vectors or lists naturally,
vector<Dog*> doggies ;
vector<StripedDog*> stripedDoggies ;
vector<Cat*> catties ;
vector<SpottedCat*> spottedCatties ;
You wish to iterate over ALL animals, however, so you wish to slam references to all dogs & cats collections into a single object,
vector< vector<Animal *>* > zoo ;
zoo.push_back( &doggies ) ;
zoo.push_back( &stripedDoggies ) ;
zoo.push_back( &catties ) ;
zoo.push_back( &spottedCatties ) ;
So now you can
feed( zoo ) ;
Of course, this doesn't compile. The vectors of cats & dogs are not vector<Animal*>, but rather vectors of their concrete types. Without retaining redundant lists and without losing the concrete type info (ie NOT using lists of base type Animal* as in vector<Animal*> stripedDoggies), is there a way to achieve equivalent behavior from C++?
I think you're looking at something like this, but not quite sure. Let me know if it isn't near to what you need and I'll drop it for favor of someone that does. The output demonstrates the virtual feed() operation does its business appropriately. Arranging a potential variadic parameter pack to that function would take me some time to cook on for awhile, and I'm not even sure it is possible.
But this should get you close.
#include <iostream>
#include <algorithm>
#include <type_traits>
#include <vector>
// base. enforces inheritance by SFINAE
template<typename Base, typename T, template<typename, typename...> class V>
typename std::enable_if<std::is_base_of<Base, T>::value>::type
invoke(void (Base::*func)(), const class V<T*>& vec)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
for (auto p : vec)
(p->*func)();
}
// chain.
template<typename Base, typename T, template<typename, typename...> class V, typename... Args>
typename std::enable_if<std::is_base_of<Base, T>::value>::type
invoke(void (Base::*func)(), const class V<T*>& vec, Args... args)
{
invoke(func, vec);
invoke(func, args...);
}
int main()
{
struct Animal
{
virtual void feed()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
} ;
struct Dog : public Animal
{
void feed()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
} ;
struct StripedDog : public Dog {};
struct Cat : public Animal {};
struct SpottedCat : public Cat {};
std::vector<Dog*> doggies ;
std::vector<StripedDog*> stripedDoggies ;
std::vector<Cat*> catties ;
std::vector<SpottedCat*> spottedCatties ;
Dog dog;
doggies.push_back(&dog);
StripedDog sdog;
stripedDoggies.push_back(&sdog);
Cat cat;
catties.push_back(&cat);
invoke(&Animal::feed, doggies, stripedDoggies, catties, spottedCatties);
return 0;
}
Output
typename std::enable_if<std::is_base_of<Base, T>::value>::type invoke(void (Base::*)(), const class V<T *> &) [Base = Animal, T = Dog, V = vector]
virtual void main()::Dog::feed()
typename std::enable_if<std::is_base_of<Base, T>::value>::type invoke(void (Base::*)(), const class V<T *> &) [Base = Animal, T = StripedDog, V = vector]
virtual void main()::Dog::feed()
typename std::enable_if<std::is_base_of<Base, T>::value>::type invoke(void (Base::*)(), const class V<T *> &) [Base = Animal, T = Cat, V = vector]
virtual void main()::Animal::feed()
typename std::enable_if<std::is_base_of<Base, T>::value>::type invoke(void (Base::*)(), const class V<T *> &) [Base = Animal, T = SpottedCat, V = vector]
Sorry about having to scroll to the right to see the types in that pretty-print, but they're pretty telling and should be looked at to see how this works. Note that both Dog and StripedDog containers properly fire the Dog::feed() member, while the Cat container properly fires the Animal::feed() base member since it provides no override.
Good luck, and I hope it helps.
Since you are using pointers, which are quite cheap to copy, you could probably do:
vector< Animal * > zoo;
zoo.append( zoo.end(), doggies.begin(), doggies.end() );
// ditto with the others
feed( zoo ); // just receives *one* vector with animals to feed
Another option if you don't want to copy/merge the vectors:
void feed() {}
template< typename V >
void feed( const V& v )
{
for( A* a : v )
{
// ...do something with 'a'
}
}
template< typename V, typename V2, typename... Vs >
void feed( const V& v, const V2& v2, const Vs&... vs )
{
feed( v );
feed( v2, vs... );
}
Now you can call feed( doggies, stripedDoggies, catties, spottedCatties );.
In general, if you are doing something like this, you would store them all in a single vector:
std::vector<std::shared_ptr<Animal>> animals;
If Animal defines a feed method, iterating over it simply means calling that function:
animals[i]->feed();
If you want to call specific functions based on types, you'll need to do some casting:
std::shared_ptr<Dog> pDog = std::dynamic_pointer_cast<Dog>(animals[i]);
std::shared_ptr<Cat> pCat = std::dynamic_pointer_cast<Cat>(animals[i]);
// other casts
if (pDog)
{
// do something with a dog
}
else if (pCat)
{
// do something with a cat
}
// etc
If you really wanted to store all of the animals in additional vectors, you can do that by wrapping the whole zoo:
class Zoo
{
private:
std::vector<std::shared_ptr<Animal>> animals;
std::vector<std::shared_ptr<Dog>> dogs;
// other vectors
public:
void AddDog(const Dog& d)
{
std::shared_ptr<Dog> pD = std::make_shared<Dog>(d);
dogs.push_back(pD);
std::shared_ptr<Animal> pA = std::static_pointer_cast<Animal>(pD);
animals.push_back(pA);
}
};
It doubles the number of pointers you are storing in memory, but pointers are fairly cheap. Then you can pass the whole zoo, or individual animal types without the need to do the casting every time.
Polymorphic iterators? shudder
Usage example:
#include <iostream>
struct Animal
{
virtual void print() = 0;
};
struct Elephant : Animal
{
virtual void print() override { std::cout << "toot"; }
};
struct Cat : Animal
{
virtual void print() override { std::cout << "meow"; }
};
struct Fox : Animal
{
virtual void print() override
{ std::cout << "ring-ding-ding-ding-dingeringeding"; }
};
#include <vector>
template<class T>
using simple_vector = std::vector<T>;
int main()
{
std::vector<Elephant> elephants(5);
std::vector<Cat> cats(3);
std::vector<Fox> foxes(1);
polymorphic_range_container<simple_vector, Animal> animals;
animals.push_back( std::make_pair(elephants.begin(), elephants.end()) );
animals.push_back( std::make_pair(cats.begin(), cats.end()) );
animals.push_back( std::make_pair(foxes.begin(), foxes.end()) );
for(auto& animal : animals)
{
animal.print(); std::cout << ", ";
}
std::cout << std::endl;
}
Implementation (basics):
#include <memory>
#include <algorithm>
#include <iterator>
#include <utility>
template<class T>
struct iterator_base
{
virtual void advance(int i) = 0;
virtual T& get() const = 0;
virtual bool equal(iterator_base const&) const = 0;
};
template<class ValueType, class Container, class Id>
struct polymorphic_iterator
{
polymorphic_iterator& operator++()
{
impl->advance(1);
if(container->is_end(*impl, id))
{
impl = container->next(id);
}
return *this;
}
ValueType& operator*() const { return impl->get(); }
friend bool operator==(polymorphic_iterator const& l,
polymorphic_iterator const& r)
{
if(l.impl == nullptr) return r.impl == nullptr;
if(r.impl == nullptr) return false;
return l.impl->equal( *(r.impl) );
}
friend bool operator!=(polymorphic_iterator const& l,
polymorphic_iterator const& r)
{
return not (l == r);
}
private:
std::unique_ptr< iterator_base<ValueType> > impl;
Container* container;
Id id;
friend Container;
polymorphic_iterator(Container* pc, Id pid, decltype(impl) p)
: impl( std::move(p) ), container(pc), id(std::move(pid))
{}
};
template<template<class> class Container, class Base>
class polymorphic_range_container
{
private:
using self = polymorphic_range_container;
struct IRange
{
using piterator = std::unique_ptr < iterator_base<Base> >;
virtual piterator begin() = 0;
virtual piterator end() = 0;
};
template<class It>
struct range : IRange
{
range(It p_begin, It p_end) : m_begin(p_begin), m_end(p_end) {}
using typename IRange::piterator;
piterator begin() override { return piterator{new iterator_impl(m_begin)}; }
piterator end() override { return piterator{new iterator_impl(m_end)}; }
private:
struct iterator_impl : iterator_base<Base>
{
iterator_impl(It p) : it(p) {}
virtual void advance(int i) override { std::advance(it, i); }
virtual Base& get() const override { return *it; }
virtual bool equal(iterator_base<Base> const& other) const override
{
iterator_impl const* pOther
= dynamic_cast<iterator_impl const*>(&other);
if(nullptr == pOther) return false;
else return it == pOther->it;
}
private:
It it;
};
iterator_impl m_begin;
iterator_impl m_end;
};
using container_type = Container< std::unique_ptr<IRange> >;
container_type ranges;
public:
template<class T>
void push_back(std::pair<T, T> p_range)
{
ranges.push_back( std::unique_ptr<IRange>{new range<T>(p_range.first, p_range.second)} );
}
using iterator = polymorphic_iterator<Base, self, typename container_type::const_iterator>;
iterator begin()
{
return iterator{this, ranges.cbegin(), ranges.front()->begin()};
}
iterator end()
{
return iterator{this, ranges.cend(), {nullptr}};
}
private:
friend iterator;
std::unique_ptr< iterator_base<Base> > next(typename container_type::const_iterator& p)
{
++p;
if(p == ranges.end()) return {nullptr};
else return (**p).begin();
}
bool is_end(iterator_base<Base> const& it, typename container_type::const_iterator const& id)
{
if(ranges.end() == id) return false;
else return (**id).end()->equal(it);
}
};
Related
I'm trying to use metaprogramming to prevent duplicate code in a parent-child structure. I got it working up to a certain point.
The code shown at the bottom compilers and runt correctly, but some relations (/*Tree_tag,*/ and /*Parasite_tag*/) are commented out. If uncommented, MSVS2017 shows
error C2664: 'void Obj<std::tuple<Human_tag>,std::tuple<>>::removeParent(const Obj<std::tuple<>,std::tuple<Tree_tag,Dog_tag>> *const )': cannot convert argument 1 from 'Obj<std::tuple<>,std::tuple<Dog_tag>> *' to 'const Obj<std::tuple<>,std::tuple<Tree_tag,Dog_tag>> *const '
note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
and G++ shows
In instantiation of ‘void Obj<std::tuple<>, std::tuple<_El0, _El ...> ::removeAllChildren() [with TChildTag = Dog_tag; TChildTags = {}]’:
Main.cpp:126:1: required from here
Main.cpp:73:43: error: invalid conversion from ‘Obj<std::tuple<>, std::tuple<Dog_tag> >*’ to ‘const TParent* {aka const Obj<std::tuple<>, std::tuple<Tree_tag, Dog_tag> >*}’ [-fpermissive]
for (auto&& child : childrenPtrs) child->removeParent(this);
The problem is with the this type qualifiers. Because I iteratively strip off template arguments with for instance
class Obj<std::tuple<>, std::tuple<TChildTag, TChildTags...>>
: public Obj<std::tuple<>, std::tuple<TChildTags...>>
the resulting this of the base type does not match the original type. Like the error shows: the original type of Human = Obj<std::tuple<>,std::tuple<Tree_tag,Dog_tag>>. However, due to the iterative stripping, the type of this in the base is Obj<std::tuple<>,std::tuple<Dog_tag>>.
I tried to use reinterpret_cast as suggested:
template<typename T>
void addParent(T* const parentPtr) {
parentsPtrs.push_back(reinterpret_cast<TParent* const>(parentPtr));
}
template<typename T>
void removeParent(T const* const parentPtr) {
auto it = std::find(std::cbegin(parentsPtrs), std::cend(parentsPtrs),
reinterpret_cast<TParent const* const>(parentPtr));
if (it != std::cend(parentsPtrs)) parentsPtrs.erase(it);
}
But then the problem is that everything is cast to an allowed parameter. I.e. this code works:
int main() {
Human h1;
Parasite p1;
addRelation(&h1, &p1);
}
...Which should not be possible, as Human and Parasite are not related directly.
So how can I correctly keep the this type qualifiers of the top (most derivative) class, complying to Human, Dog, etc types?
Working code(with comments):
#include <tuple>
#include <vector>
template<class T>
using prtVector = std::vector<T*>;
class BaseObject {
public:
virtual prtVector<BaseObject> getAllParents() const = 0;
virtual prtVector<BaseObject> getAllChildren() const = 0;
virtual void removeAllParents() = 0;
virtual void removeAllChildren() = 0;
};
template<typename TParentTuple, typename TChilderenTuple>
class Obj;
template<typename TParentTag, typename... TParentTags, typename... TChildTags>
class Obj<std::tuple<TParentTag, TParentTags...>, std::tuple<TChildTags...>>
: public Obj<std::tuple<TParentTags...>, std::tuple<TChildTags...>>
{
using TParent = typename TParentTag::obj_type;
prtVector<TParent> parentsPtrs;
public:
void addParent(TParent* const parentPtr) { parentsPtrs.push_back(parentPtr); }
void removeParent(TParent const* const parentPtr) {
auto it = std::find(std::cbegin(parentsPtrs), std::cend(parentsPtrs), parentPtr);
if (it != std::cend(parentsPtrs)) parentsPtrs.erase(it);
}
virtual prtVector<BaseObject> getAllParents() const override {
auto result = Obj<std::tuple<TParentTags...>, std::tuple<TChildTags...>>::getAllParents();
result.insert(std::begin(result), std::cbegin(parentsPtrs), std::cend(parentsPtrs));
return result;
}
virtual prtVector<BaseObject> getAllChildren() const override {
return Obj<std::tuple<TParentTags...>, std::tuple<TChildTags...>>::getAllChildren();
}
virtual void removeAllParents() override {
Obj<std::tuple<TParentTags...>, std::tuple<TChildTags...>>::removeAllParents();
for (auto&& parent : parentsPtrs) parent->removeChild(this);
}
virtual void removeAllChildren() override {
Obj<std::tuple<TParentTags...>, std::tuple<TChildTags...>>::removeAllChildren();
}
};
template<typename TChildTag, typename... TChildTags>
class Obj<std::tuple<>, std::tuple<TChildTag, TChildTags...>>
: public Obj<std::tuple<>, std::tuple<TChildTags...>>
{
using TChild = typename TChildTag::obj_type;
prtVector<TChild> childrenPtrs;
public:
void addChild(TChild* const childPtr) { childrenPtrs.push_back(childPtr); }
void removeChild(TChild const* const childPtr) {
auto it = std::find(std::cbegin(childrenPtrs), std::cend(childrenPtrs), childPtr);
if (it != std::cend(childrenPtrs)) childrenPtrs.erase(it);
}
virtual prtVector<BaseObject> getAllParents() const override {
return Obj<std::tuple<>, std::tuple<TChildTags...>>::getAllChildren();
}
virtual prtVector<BaseObject> getAllChildren() const override {
auto result = Obj<std::tuple<>, std::tuple<TChildTags...>>::getAllChildren();
result.insert(std::begin(result), std::cbegin(childrenPtrs), std::cend(childrenPtrs));
return result;
}
virtual void removeAllParents() override {}
virtual void removeAllChildren() override {
Obj<std::tuple<>, std::tuple<TChildTags...>>::removeAllChildren();
for (auto&& child : childrenPtrs) child->removeParent(this);
}
};
template<>
class Obj<std::tuple<>, std::tuple<>> : public BaseObject {
public:
virtual prtVector<BaseObject> getAllParents() const override {
return prtVector<BaseObject>();
}
virtual prtVector<BaseObject> getAllChildren() const override {
return prtVector<BaseObject>();
}
virtual void removeAllParents() override {}
virtual void removeAllChildren() override {}
};
struct Human_tag;
struct Tree_tag;
struct Dog_tag;
struct Parasite_tag;
using Human = Obj<std::tuple<>, std::tuple</*Tree_tag,*/ Dog_tag>>;
using Tree = Obj<std::tuple<Human_tag>, std::tuple<>>;
using Dog = Obj<std::tuple<Human_tag>, std::tuple</*Parasite_tag*/>>;
using Parasite = Obj<std::tuple<Dog_tag>, std::tuple<>>;
struct Human_tag { using obj_type = Human; };
struct Tree_tag { using obj_type = Tree; };
struct Dog_tag { using obj_type = Dog; };
struct Parasite_tag { using obj_type = Parasite; };
template<class A, class B>
void addRelation(A* a, B* b)
{
a->addChild(b);
b->addParent(a);
}
#include <iostream>
int main() {
Human h1;
Dog d1, d2;
addRelation(&h1, &d1);
addRelation(&h1, &d2);
auto result = h1.getAllChildren();
std::cout << result.size() << "\n"; //print 2
d1.removeAllParents();
result = h1.getAllChildren();
std::cout << result.size() << "\n"; //print 1
std::cin.ignore();
}
With C++17, you may do (without cast):
template<typename TParentTuple, typename TChilderenTuple>
class Obj;
template<typename... ParentTags,
typename... ChildTags>
class Obj<std::tuple<ParentTags...>, std::tuple<ChildTags...>> : public BaseObject
{
std::tuple<std::vector<typename ParentTags::obj_type*>...> parents;
std::tuple<std::vector<typename ChildTags::obj_type*>...> children;
public:
template <typename T>
void addParent(T* parent) { std::get<std::vector<T*>>(parents).push_back(parent); }
template <typename T>
void removeParent(const T* parent) {
auto& v = std::get<std::vector<T*>>(parents);
auto it = std::find(std::cbegin(v), std::cend(v), parent);
if (it != std::cend(v)) { v.erase(it); }
}
template <typename T>
void addChild(T* child) { std::get<std::vector<T*>>(children).push_back(child); }
template <typename T>
void removeChild(const T* child) {
auto& v = std::get<std::vector<T*>>(children);
auto it = std::find(std::cbegin(v), std::cend(v), child);
if (it != std::cend(v)) { v.erase(it); }
}
std::vector<BaseObject*> getAllParents() const override {
std::vector<BaseObject*> res;
std::apply([&](auto&... v){ (res.insert(res.end(), v.begin(), v.end()), ...); },
parents);
return res;
}
std::vector<BaseObject*> getAllChildren() const override {
std::vector<BaseObject*> res;
std::apply([&](auto&... v){ (res.insert(res.end(), v.begin(), v.end()), ...); },
children);
return res;
}
void removeAllParents() override {
std::apply(
[this](auto&... v)
{
[[maybe_unused]] auto clean = [this](auto& v) {
for (auto* parent : v) {
parent->removeChild(this);
}
v.clear();
};
(clean(v), ...);
},
parents);
}
void removeAllChildren() override {
std::apply(
[this](auto&... v)
{
[[maybe_unused]] auto clean = [this](auto& v) {
for (auto* child : v) {
child->removeParent(this);
}
v.clear();
};
( clean(v), ...);
},
children);
}
};
Demo
With C++14, it would be more verbose to replace the "for_each_tuple" done with std::apply and folding expression.
and in C++11, even more with std::get<T>(tuple).
OK, I've thought a lot about this, and decided to add the original type as another template argument.
So the class signature will be something like:
template<typename TOwnTag, typename TParentTag, typename... TParentTags, typename... TChildTags>
class Obj<TOwnTag, std::tuple<TParentTag, TParentTags...>, std::tuple<TChildTags...>>
: public Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>
{
using TOwn = typename TOwnTag::obj_type;
This seems to fix my issue, as I can now use
reinterpret_cast<TOwn* const>(this)
The complete working code then becomes:
#include <tuple>
#include <vector>
#include <algorithm>
template<class T>
using prtVector = std::vector<T*>;
class BaseObject {
public:
virtual prtVector<BaseObject> getAllParents() const = 0;
virtual prtVector<BaseObject> getAllChildren() const = 0;
virtual void removeAllParents() = 0;
virtual void removeAllChildren() = 0;
};
template<typename TOwnTag, typename TParentTuple, typename TChilderenTuple>
class Obj;
template<typename TOwnTag, typename TParentTag, typename... TParentTags, typename... TChildTags>
class Obj<TOwnTag, std::tuple<TParentTag, TParentTags...>, std::tuple<TChildTags...>>
: public Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>
{
using TOwn = typename TOwnTag::obj_type;
using TParent = typename TParentTag::obj_type;
prtVector<TParent> parentsPtrs;
public:
//prevent base function hiding
using Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::addParent;
using Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::removeParent;
void addParent(TParent* const parentPtr) { parentsPtrs.push_back(parentPtr); }
void removeParent(TParent const* const parentPtr) {
auto it = std::find(std::cbegin(parentsPtrs), std::cend(parentsPtrs), parentPtr);
if (it != std::cend(parentsPtrs)) parentsPtrs.erase(it);
}
virtual prtVector<BaseObject> getAllParents() const override {
auto result = Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::getAllParents();
result.insert(std::begin(result), std::cbegin(parentsPtrs), std::cend(parentsPtrs));
return result;
}
virtual prtVector<BaseObject> getAllChildren() const override {
return Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::getAllChildren();
}
virtual void removeAllParents() override {
Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::removeAllParents();
for (auto&& parent : parentsPtrs) parent->removeChild(reinterpret_cast<TOwn* const>(this));
}
virtual void removeAllChildren() override {
Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::removeAllChildren();
}
};
template<typename TOwnTag, typename TChildTag, typename... TChildTags>
class Obj<TOwnTag, std::tuple<>, std::tuple<TChildTag, TChildTags...>>
: public Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>
{
using TOwn = typename TOwnTag::obj_type;
using TChild = typename TChildTag::obj_type;
prtVector<TChild> childrenPtrs;
public:
void addParent() {}
void removeParent() {}
//prevent base function hiding
using Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::addChild;
using Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::removeChild;
void addChild(TChild* const childPtr) { childrenPtrs.push_back(childPtr); }
void removeChild(TChild const* const childPtr) {
auto it = std::find(std::cbegin(childrenPtrs), std::cend(childrenPtrs), childPtr);
if (it != std::cend(childrenPtrs)) childrenPtrs.erase(it);
}
virtual prtVector<BaseObject> getAllParents() const override {
return Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::getAllParents();
}
virtual prtVector<BaseObject> getAllChildren() const override {
auto result = Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::getAllChildren();
result.insert(std::begin(result), std::cbegin(childrenPtrs), std::cend(childrenPtrs));
return result;
}
virtual void removeAllParents() override {}
virtual void removeAllChildren() override {
Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::removeAllChildren();
for (auto&& child : childrenPtrs) child->removeParent(reinterpret_cast<TOwn* const>(this));
}
};
template<typename TOwnTag>
class Obj<TOwnTag, std::tuple<>, std::tuple<>> : public BaseObject {
public:
void addChild() {}
void removeChild() {}
void addParent() {}
void removeParent() {}
//
virtual prtVector<BaseObject> getAllParents() const override {
return prtVector<BaseObject>();
}
virtual prtVector<BaseObject> getAllChildren() const override {
return prtVector<BaseObject>();
}
virtual void removeAllParents() override {}
virtual void removeAllChildren() override {}
};
struct Human_tag;
struct Tree_tag;
struct Dog_tag;
struct Parasite_tag;
using Human = Obj<Human_tag, std::tuple<>, std::tuple<Tree_tag, Dog_tag>>;
using Tree = Obj<Tree_tag, std::tuple<Human_tag>, std::tuple<>>;
using Dog = Obj<Dog_tag, std::tuple<Human_tag>, std::tuple<Parasite_tag>>;
using Parasite = Obj<Parasite_tag, std::tuple<Dog_tag>, std::tuple<>>;
struct Human_tag { using obj_type = Human; };
struct Tree_tag { using obj_type = Tree; };
struct Dog_tag { using obj_type = Dog; };
struct Parasite_tag { using obj_type = Parasite; };
template<class A, class B>
void addRelation(A* const a, B* const b)
{
a->addChild(b);
b->addParent(a);
}
#include <iostream>
int main() {
Human h1;
Dog d1, d2;
//Parasite p1;
addRelation(&h1, &d1);
addRelation(&h1, &d2);
//addRelation(&h1, &p1); // compiler error
auto result = h1.getAllChildren();
std::cout << result.size() << "\n"; //print 2
d1.removeAllParents();
result = h1.getAllChildren();
std::cout << result.size() << "\n"; //print 1
std::cin.ignore();
}
Live Example
There are no similar concept of (Java) Collection in C++.
I can understand the reason, but I want to know whether there is any way to fake it elegantly.
Example
I have implemented many custom Collections.
They all have Iterator that works correctly, similar to std::vector, std::unordered_set, etc.
They are MyArray<T> , MyBinaryTree<T> and MySet<T>.
Here I will show a working code that show the location I want to fake it.
Let's say that I have 2 levels of program : library and user.
It does only one thing - User commands Library to eat all Orange*s in a bucket.
Library.h
class Library{
public: static void eatAll(const MyArray<Orange*>& bucket);
};
Library.cpp
#include "Orange.h"
void Library::eatAll(const MyArray<Orange*>& bucket){
for(auto orange:bucket){
orange->eaten();
}
}
User.h
MyArray<Orange*> bucket;
Library::eatAll(bucket);
It is OK.
Now, I want Library::eatAll to also support MyBinaryTree<Orange*>, I have some not-so-desirable approaches as below.
My poor solution
1. Java way
Make MyBinaryTree<T> and MyArray<Orange*> (and their iterator) inherit from a new class Collection<T> (and CollectionIterator<T>).
change signature to Library::eatAll(const Collection<T>&)
Disadvantage : performance penalty from "virtual" of some functions in Collection<T>.
2. Template v1
//Library.h
template<class T> void eatAll(const T&t ){
for(auto orange : t){
orange->eaten();
}
}
make eatAll a template function
Disadvantage : The implementation of the eatAll must be in header.
I have to #include orange.h in Library.h.
Sometimes, I really want to just forward declaration.
3. Template v2
//Library.h
template<class T> void eatAll(const T&t ){
for(auto orange : t){
eatAnOrange(orange)
}
}
private: void eatAnOrange(Orange* orange){
//below implementation is inside Library.cpp
orange->eaten();
}
create a middle-man function eatAnOrange
Disadvantage :
Code is less readable, not concise, cause a little maintainability problem.
If there are a lot of other functions e.g. squeezeAll(), I probably have to create a lot of middle-man functions, e.g. squeezeAnOrange().
4. Operator=()
Create converter among the 3 collection classes via implicit constructor.
Disadvantage : Suffer performance from creating a new instance of collection.
//Here is what it will do, internally (roughly speaking)
MyBinaryTree<Orange*> bucket;
Library::eatAll(MyArray<Orange*>(bucket));
I believe my solutions are inelegant.
Are there any solutions that don't suffer the mentioned disadvantage?
Edit:
Both of current answers are elegant than my approaches (thank!), but still has disadvantage :-
- Oliv's requires #include "orange.h" in User.h.
- Richard Hodges's has virtual function calling.
In C++, collections are traversed using the iterator design pattern. The entire STL is designed around this concept. It may fit your needs:
You could define eatAll as a function that accept two iterators:
template<class Iterator,class Sentinel>
void eatAll(Iterator it, Sentinel s){
for (;it!=s;++it)
it->eaten();
}
Or the range like algorithm interface:
template<class Range>
void eatAll(Range& r){
for (auto& v:r)
v.eaten();
}
You will have to define you binary tree as a range (it must implement begin() and end()). Hopefully trees are kind of graph that can be linearised. All the smart work will then go in the iterator implementation!
If you want it truly polymorphic, then we have to deal with 2 things:
the actual type of the container
The fact that the result of dereferencing a map is a pair containing key and value references.
My view is that the answer to this is not to derive from containers, which is limiting, but to create a polymorphic "value iterator", which models all iterators and extracts their values correctly.
Then we can write code like this:
int main()
{
std::vector<Orange> vo {
Orange(), Orange()
};
std::map<int, Orange> mio {
{ 1, Orange() },
{ 2, Orange() },
{ 3, Orange() }
};
std::cout << "vector:\n";
auto first = makePolymorphicValueIterator(vo.begin());
auto last = makePolymorphicValueIterator(vo.end());
do_orange_things(first, last);
std::cout << "\nmap:\n";
first = makePolymorphicValueIterator(mio.begin());
last = makePolymorphicValueIterator(mio.end());
do_orange_things(first, last);
}
To get this:
vector:
Orange
Orange
map:
Orange
Orange
Orange
Here's a minimal, complete implementation:
#include <typeinfo>
#include <memory>
#include <iostream>
#include <vector>
#include <map>
#include <iterator>
// define an orange
struct Orange {
};
// a meta-function to get the type of the value of some iterated value_type
template<class ValueType> struct type_of_value
{
using type = ValueType;
};
// specialise it for maps and unordered maps
template<class K, class V> struct type_of_value<std::pair<K, V>>
{
using type = V;
};
template<class ValueType> using type_of_value_t = typename type_of_value<ValueType>::type;
// function to extract a value from an instance of a value_type
template<class ValueType> struct value_extractor
{
template<class V>
auto& operator()(V&& v) const {
return v;
}
};
// specialised for maps
template<class K, class V> struct value_extractor<std::pair<K, V>>
{
template<class Arg>
auto& operator()(Arg&& v) const {
return std::get<1>(v);
}
};
template<class Iter>
auto extract_value(Iter const& iter) -> auto&
{
using value_type = typename std::iterator_traits<Iter>::value_type;
auto e = value_extractor<value_type> {};
return e(*iter);
}
// a polymorphic (forward only at the moment) iterator
// which delivers the value (in the case of maps) or the element (every other container)
template<class ValueType>
struct PolymorphicValueIterator {
using value_type = type_of_value_t<ValueType>;
private:
struct iterator_details {
std::type_info const &type;
void *address;
};
struct concept {
virtual std::unique_ptr<concept> clone() const = 0;
virtual value_type& invoke_deref() const = 0;
virtual void invoke_next(std::size_t distance = 1) = 0;
virtual iterator_details get_details() = 0;
virtual bool is_equal(const iterator_details &other) const = 0;
virtual ~concept() = default;
};
template<class Iter>
struct model final : concept {
model(Iter iter)
: iter_(iter)
{}
std::unique_ptr<concept> clone() const override
{
return std::make_unique<model>(iter_);
}
virtual value_type& invoke_deref() const override {
return extract_value(iter_);
}
void invoke_next(std::size_t distance = 1) override
{
iter_ = std::next(iter_, distance);
}
iterator_details get_details() override {
return {
typeid(Iter),
std::addressof(iter_)
};
}
bool is_equal(const iterator_details &other) const override {
if (typeid(Iter) != other.type) {
return false;
}
auto pother = reinterpret_cast<Iter const*>(other.address);
Iter const& iother = *pother;
return iter_ == iother;
}
Iter iter_;
};
std::unique_ptr<concept> concept_ptr_;
public:
bool operator==(PolymorphicValueIterator const &r) const {
return concept_ptr_->is_equal(r.concept_ptr_->get_details());
}
bool operator!=(PolymorphicValueIterator const &r) const {
return not concept_ptr_->is_equal(r.concept_ptr_->get_details());
}
PolymorphicValueIterator &operator++() {
concept_ptr_->invoke_next(1);
return *this;
}
value_type& operator*() const {
return concept_ptr_->invoke_deref();
}
template<class Iter>
PolymorphicValueIterator(Iter iter)
{
concept_ptr_ = std::make_unique<model<Iter>>(iter);
}
PolymorphicValueIterator(PolymorphicValueIterator const& r)
: concept_ptr_(r.concept_ptr_->clone())
{}
PolymorphicValueIterator& operator=(PolymorphicValueIterator const& r)
{
concept_ptr_ = r.concept_ptr_->clone();
return *this;
}
};
template<class Iter>
auto makePolymorphicValueIterator(Iter iter)
{
using iter_value_type = typename std::iterator_traits<Iter>::value_type;
using value_type = type_of_value_t<iter_value_type>;
return PolymorphicValueIterator<value_type>(iter);
}
// a test
void do_orange_things(PolymorphicValueIterator<Orange> first, PolymorphicValueIterator<Orange> last)
{
while(first != last) {
std::cout << "Orange\n";
++first;
}
}
int main()
{
std::vector<Orange> vo {
Orange(), Orange()
};
std::map<int, Orange> mio {
{ 1, Orange() },
{ 2, Orange() },
{ 3, Orange() }
};
std::cout << "vector:\n";
auto first = makePolymorphicValueIterator(vo.begin());
auto last = makePolymorphicValueIterator(vo.end());
do_orange_things(first, last);
std::cout << "\nmap:\n";
first = makePolymorphicValueIterator(mio.begin());
last = makePolymorphicValueIterator(mio.end());
do_orange_things(first, last);
}
In order to work with a simplified example, let's consider some animals eating some items in a food list. The food list has a lot of different iterators for different situations.
class contains_fish
{
public:
bool operator () (const Food& food) const;
};
class is_vegetarian
{
public:
bool operator () (const Food& food) const;
};
class FoodList
{
private:
std::vector<Food> foodItems;
public:
typedef std::vector<Food>::iterator iterator;
typedef std::vector<Food>::const_iterator const_iterator;
typedef std::vector<Food>::reverse_iterator reverse_iterator;
typedef std::vector<Food>::const_reverse_iterator const_reverse_iterator;
typedef boost::filter_iterator<contains_fish,FoodList::iterator> fish_iterator;
typedef boost::filter_iterator<contains_fish,FoodList::const_iterator> fish_const_iterator;
typedef boost::filter_iterator<contains_fish,FoodList::reverse_iterator> fish_reverse_iterator;
typedef boost::filter_iterator<contains_fish,FoodList::const_reverse_iterator> fish_const_reverse_iterator;
typedef boost::filter_iterator<is_vegetarian,FoodList::iterator> vegetarian_iterator;
typedef boost::filter_iterator<is_vegetarian,FoodList::const_iterator> vegetarian_const_iterator;
typedef boost::filter_iterator<is_vegetarian,FoodList::reverse_iterator> vegetarian_reverse_iterator;
typedef boost::filter_iterator<is_vegetarian,FoodList::const_reverse_iterator> vegetarian_const_reverse_iterator;
//...
//... with corresponding begin/end functions :
FoodList::iterator begin() { return this->foodItems.begin(); }
FoodList::const_iterator begin() const { return this->foodItems.begin(); }
//...
FoodList::vegetarian_const_reverse_iterator begin_vegetarian_const_reverse() const { return boost::make_filter_iterator<is_vegetarian>(this->foodItems.rbegin(), this->foodItems.rend()); }
};
Now I want to give food (a virtual function) to each animal with iterators on the food list. Something like this code (not working due to virtual template function) :
class Animal
{
public:
virtual ~Animal() {}
template <typename FoodListIterator>
virtual void eat(FoodListIterator begin, FoodListIterator end) = 0;
};
class Dog : public Animal
{
public:
virtual ~Dog() {}
template <typename FoodListIterator>
virtual void eat(FoodListIterator begin, FoodListIterator end)
{
if(begin == end)
std::cout << "Sad day ! Nothing for me..." << std::endl;
else
{
std::cout << "I'm a dog and I'm going to eat :" << std::endl;
for(FoodListIterator it = begin; it != end; ++it)
std::cout << it->toString() << std::endl;
}
}
};
void give_fish(std::vector<Animal*>& animals, const FoodList& food_list)
{
for(unsigned long int i = 0; i < animals.size(); ++i)
animals[i]->eat(food_list.fish_begin(), food_list.fish_end());
}
I have too many different iterator to implement a virtual function for each signature.
How can I do that elegantly, without C++11 ? If it can help, I know the list of types eligible (the list of iterators described in FoodList).
You could reorganize your interface such that each Animal is fed one single Food virtually and a range of Foods non-virtually:
class Animal
{
public:
virtual ~Animal() {}
virtual void eat(Food& ) = 0; // or Food const&
template <typename Iterator>
void eat(Iterator begin, Iterator end) {
for (; begin != end; ++begin) {
eat(*begin);
}
}
};
Though if you really need the whole range, you could use something like boost::any_range:
class Animal
{
public:
using FoodRange = boost::any_range<Food, boost::forward_traversal_tag,
Food&, std::ptrdiff_t>;
virtual ~Animal() {}
virtual void eat(FoodRange ) = 0;
template <typename Iterator>
void eat(Iterator begin, Iterator end) {
eat(FoodRange{begin, end});
}
};
Background
This is purely for educational purposes. If you don't want to read the whole background, you can skip to the question at the bottom.
I have written a Queue interface (abstract class), and 2 derived implementations based on resizing arrays and linked lists.
template <typename T>
class IQueue {
public:
virtual void enqueue(T item) = 0;
virtual T dequeue() = 0;
virtual bool isEmpty() = 0;
virtual int size() = 0;
}
template <typename T>
class LinkedListQueue : public IQueue<T> {...}
template <typename T>
class ResizingArrayQueue : public IQueue<T> {...}
I wanted to be able to go over a queue's elements with an STL compliant iterator (I know queues should not be iterable), so I can use for (auto e: c) or queue.begin() / queue.end().
Because I use run-time polymorphism I had to add a client iterator class to IQueue and use the Pimpl idiom to instantiate the actual implementation specific iterators in the derived queue classes, to avoid object slicing issue.
So the augmented code looks like:
template <typename T>
class IQueue {
public:
virtual void enqueue(T item) = 0;
virtual T dequeue() = 0;
virtual bool isEmpty() = 0;
virtual int size() = 0;
public:
class IteratorImpl {
public:
virtual void increment () = 0;
virtual bool operator== (const IteratorImpl& other) const = 0;
virtual bool operator!= (const IteratorImpl& other) const = 0;
virtual T& operator* () const = 0;
virtual T& operator-> () const = 0;
virtual void swap (IteratorImpl& other) = 0;
virtual IteratorImpl* clone() = 0;
};
public:
class ClientIterator : public std::iterator<std::forward_iterator_tag, T> {
std::unique_ptr<IteratorImpl> impl;
public:
ClientIterator(const ClientIterator& other) : impl(other.impl->clone()) {}
ClientIterator(std::unique_ptr<IteratorImpl> it) : impl(std::move(it)) {}
void swap(ClientIterator& other) noexcept {
impl->swap(*(other.impl));
}
ClientIterator& operator++ () {
impl->increment();
return *this;
}
ClientIterator operator++ (int) {
ClientIterator tmp(*this);
impl->increment();
return tmp;
}
bool operator== (const ClientIterator& other) const {
return *impl == *other.impl;
}
bool operator!= (const ClientIterator& other) const {
return *impl != *other.impl;
}
T& operator* () const {
return **impl;
}
T& operator-> () const {
return **impl;
}
};
typedef ClientIterator iterator;
virtual iterator begin() = 0;
virtual iterator end() = 0;
};
and one of the derived classes implements the begin() / end() methods and the derived Iterator implementation:
template <typename T>
class LinkedListQueue : public IQueue<T> {
// ... queue implementation details.
public:
class LinkedListForwardIterator : public IQueue<T>::IteratorImpl {
// ... implementation that goes through linked list.
};
typename IQueue<T>::ClientIterator begin() {
std::unique_ptr<LinkedListForwardIterator> impl(new LinkedListForwardIterator(head));
return typename IQueue<T>::iterator(std::move(impl));
}
typename IQueue<T>::ClientIterator end() {
std::unique_ptr<LinkedListForwardIterator> impl(new LinkedListForwardIterator(nullptr));
return typename IQueue<T>::iterator(std::move(impl));
}
};
Now in order to test that the iterators work I have the following 2 functions:
template <typename T>
void testQueueImpl(std::shared_ptr<IQueue<T> > queue) {
queue->enqueue(1);
queue->enqueue(2);
queue->enqueue(3);
queue->enqueue(4);
queue->enqueue(5);
queue->enqueue(6);
std::cout << "Iterator behavior check 1st: ";
for (auto e: *queue) {
std::cout << e << " ";
}
std::cout << std::endl;
std::cout << "Iterator behavior check 2nd: ";
for (auto it = queue->begin(); it != queue->end(); it++) {
std::cout << *it << " ";
}
}
void testQueue() {
auto queue = std::make_shared<LinkedListQueue<int> >();
testQueueImpl<int>(queue);
auto queue2 = std::make_shared<ResizingArrayQueue<int> >();
testQueueImpl<int>(queue2);
}
Question
How can I get rid of the run-time polymorphism (remove IQueue, remove the iterator Pimpl implementations), and rewrite the testQueue() / testQueueImpl() functions so that:
the functions can successfully test the Stack implementations and Stack iterators, without having a base class pointer.
that both LinkedListQueue and ResizingArrayQueue adhere to some kind of a compile-time interface (the enqueue, dequeue, isEmpty, size methods are present, the begin / end methods are present, both classes contain valid iterator classes)?
Possible solution
For 1) it seems that I can simply change the template argument to be the whole container, and the program compiles successfully and runs. But this does not check for the existence of the begin() / end() / enqueue() methods.
For 2) from what I could find on the internet, it seems that the relevant solution would involve Type Traits / SFINAE / or Concepts (Container concept, forward iterator concept). It seems that Boost Concepts library allows annotating a class to conform to a container concept, but I am interested in a self-contained solution (no external libraries except STL) for educational purposes.
template <typename Container>
void testQueueImpl(Container queue) {
queue->enqueue(1);
queue->enqueue(2);
queue->enqueue(3);
queue->enqueue(4);
queue->enqueue(5);
queue->enqueue(6);
std::cout << "Size: " << queue->size() << std::endl;
std::cout << "Iterator behavior check 1st: ";
for (auto e: *queue) {
std::cout << e << " ";
}
std::cout << std::endl;
std::cout << "Iterator behavior check 2nd: ";
for (auto it = queue->begin(); it != queue->end(); it++) {
std::cout << *it << " ";
}
std::cout << std::endl;
}
void testQueue() {
auto queue = std::make_shared<LinkedListQueue<int> >();
testQueueImpl<std::shared_ptr<LinkedListQueue<int> > >(queue);
auto queue2 = std::make_shared<ResizingArrayQueue<int> >();
testQueueImpl<std::shared_ptr<ResizingArrayQueue<int> > >(queue2);
}
Here is a minimum compilable example of how you might want to do it.
Note that at the moment, this example only supports const begin() and const end().
The addition of further methods and a mutable iterator is an exercise for the reader
EDIT: provided working example of both compile time and runtime polymorphic queues that shared the same policy classes.
#include <iostream>
#include <list>
#include <vector>
#include <memory>
#include <typeinfo>
#include <typeindex>
/// COMPILE TIME Polymorphic queue of objects of type Element
template<typename Element, class Policy>
struct queue_concept
{
// Define interface
struct const_iterator;
void push_back(Element e);
const_iterator begin() const;
const_iterator end() const;
// Implementation
private:
Policy _policy;
};
// implement class methods an inner classes
template<typename Element, class Policy>
struct queue_concept<Element, Policy>::const_iterator
{
using iterator_type = typename Policy::container_type::const_iterator;
const_iterator(iterator_type iter = iterator_type {})
: _iter { std::move(iter) }
{}
const Element& operator*() const {
return *_iter;
}
const_iterator& operator++() {
std::advance(_iter, 1);
}
bool operator!=(const const_iterator& other) const {
return _iter != other._iter;
}
iterator_type _iter;
};
template<typename Element, class Policy>
void queue_concept<Element, Policy>::push_back(Element e)
{
_policy._data.push_back(std::move(e));
}
template<typename Element, class Policy>
typename queue_concept<Element, Policy>::const_iterator queue_concept<Element, Policy>::begin() const
{
return const_iterator { _policy._data.begin() };
}
template<typename Element, class Policy>
typename queue_concept<Element, Policy>::const_iterator queue_concept<Element, Policy>::end() const
{
return const_iterator { _policy._data.end() };
}
/// RUNTIME Polymorphic queue of objects of type Element
template<typename Element>
struct IQueue
{
struct const_iterator
{
struct Concept {
// virtual base class so make destructor virtual...
virtual ~Concept() = default;
virtual const Element& get_element() const = 0;
virtual void increment(std::size_t distance) = 0;
bool equal_to(const Concept& rhs)
{
if (this->get_type() == rhs.get_type()) {
return unsafe_is_equal(rhs);
}
return false;
}
virtual bool unsafe_is_equal(const Concept& rhs) const = 0;
virtual std::type_index get_type() const = 0;
// provide copy support
virtual std::unique_ptr<Concept> clone() const = 0;
};
template<class Iter>
struct Model : public Concept {
Model(Iter iter) : _iter { std::move(iter) }
{}
const Element& get_element() const override {
return *_iter;
}
void increment(std::size_t distance) override {
std::advance(_iter, distance);
}
bool unsafe_is_equal(const Concept& rhs) const override {
auto _rhs = static_cast<const Model&>(rhs);
return _iter == _rhs._iter;
}
std::type_index get_type() const override {
return std::type_index(typeid(*this));
}
std::unique_ptr<Concept> clone() const override {
return std::unique_ptr<Concept> { new Model(*this) };
}
private:
Iter _iter;
};
// constructor
template<class Iter>
const_iterator(Iter iter)
: _impl { new Model<Iter> { std::move(iter) } }
{}
// default constructor - constructs an invalid iterator
const_iterator()
{}
// provide copy support since impl is a unique_ptr
const_iterator(const const_iterator& other)
: _impl { other._impl ? other._impl->clone() : std::unique_ptr<Concept>{} }
{}
const_iterator& operator=(const_iterator& other)
{
auto p = other._impl ? other._impl->clone() : std::unique_ptr<Concept>{};
std::swap(_impl, p);
}
// since we provided copy support we must provide move support
const_iterator(const_iterator&& rhs) = default;
const_iterator& operator=(const_iterator&& rhs) = default;
const Element& operator*() const {
return _impl->get_element();
}
const_iterator& operator++() {
_impl->increment(1);
return *this;
}
bool operator!=(const const_iterator& rhs) const
{
return !(_impl->equal_to(*(rhs._impl)));
}
private:
std::unique_ptr<Concept> _impl;
};
virtual void push_back(Element e) = 0;
virtual const_iterator begin() const = 0;
virtual const_iterator end() const = 0;
};
template<class Element, class Policy>
struct QueueImpl : public IQueue<Element>
{
void push_back(Element e) override {
_policy._data.push_back(std::move(e));
}
typename IQueue<Element>::const_iterator begin() const override {
return typename IQueue<Element>::const_iterator { std::begin(_policy._data) };
}
typename IQueue<Element>::const_iterator end() const override {
return typename IQueue<Element>::const_iterator { std::end(_policy._data) };
}
Policy _policy;
};
template<class Element>
struct ResizingArrayPolicy
{
using container_type = std::vector<Element>;
container_type _data;
};
template<class Element>
struct LinkedListPolicy
{
using container_type = std::list<Element>;
container_type _data;
};
template<class Element>
std::unique_ptr<IQueue<Element>> make_poly_resizing_array_queue()
{
return std::unique_ptr<IQueue<Element>> { new QueueImpl<Element, ResizingArrayPolicy<Element>> };
}
template<class Element>
std::unique_ptr<IQueue<Element>> make_poly_linked_list_queue()
{
return std::unique_ptr<IQueue<Element>> { new QueueImpl<Element, LinkedListPolicy<Element>>{} };
}
template<class Element>
queue_concept<Element, ResizingArrayPolicy<Element>> make_static_resizing_array_queue()
{
return queue_concept<Element, ResizingArrayPolicy<Element>>{};
}
template<class Element>
queue_concept<Element, LinkedListPolicy<Element>> make_static_linked_list_queue()
{
return queue_concept<Element, LinkedListPolicy<Element>>{};
}
using namespace std;
int main()
{
// create the queues
auto pq1 = make_poly_resizing_array_queue<int>();
auto pq2 = make_poly_linked_list_queue<int>();
// put data in them
pq1->push_back(10);
pq1->push_back(20);
pq2->push_back(30);
pq2->push_back(40);
// prove that iterators are assignable and moveable
IQueue<int>::const_iterator it;
it = pq1->begin();
cout << *it << endl; // should print 10
auto i2 = pq2->begin();
it = move(i2);
cout << *it << endl; // should print 30
// prove that queues are polymorphic
auto queues = vector<unique_ptr<IQueue<int>>>{};
queues.push_back(move(pq1));
queues.push_back(move(pq2));
// print the vector of queues
for(const auto& queue_ptr : queues) {
for(const auto& item : *queue_ptr) {
cout << item << endl;
}
cout << endl;
}
// now the static versions
auto q1 = make_static_resizing_array_queue<int>();
auto q2 = make_static_linked_list_queue<int>();
q1.push_back(10);
q1.push_back(20);
q2.push_back(30);
q2.push_back(40);
cout << "static queues\n";
for(const auto& item : q1) {
cout << item << endl;
}
cout << endl;
for(const auto& item : q2) {
cout << item << endl;
}
return 0;
}
The question is not clear whether you actual need runtime polymorphism (of what, for example?)
An approach could be similiar to the one used by C++ containers: have a class that will manage the allocation/deallocation and construction/destruction of the objects.
template <typename T, class Allocator>
class Queue
{
Allocator myAllocator;
public:
void enqueue(T item)
{
myAllocator.push(item);
}
// other operations.
};
and then have something like
template <class T, template <typename ...> class Container, class ... Args>
class BasicAllocator
{
Container<T, Args...> M_list;
public:
void push(T element)
{
M_list.push_back(element);
}
auto begin() -> decltype( std::begin(M_list) )
{ return std::begin(M_list); }
auto end() -> decltype( std::end(M_list) )
{ return std::end(M_list); }
};
template<class T>
using LinkedListAllocator = BasicAllocator<T, std::list>;
template<class T>
using LinkedListQueue = Queue<T, LinkedListAllocator<T>>;
Implementing dequeue might be a little trickier.
Live example
I'm trying to implement a simple abstract syntax tree (AST) in C++ using the visitor pattern. Usually a visitor pattern does not handle return value. But in my AST there are expressions nodes which care about the return type and value of its children node. For example, I have a Node structure like this:
class AstNode
{
public:
virtual void accept(AstNodeVisitor&) = 0;
void addChild(AstNode* child);
AstNode* left() { return m_left; }
AstNode* right() { return m_right; }
...
private:
AstNode* m_left;
AstNode* m_right;
};
class CompareNode : public AstNode
{
public:
virtual void accept(AstNodeVisitor& v)
{
v->visitCompareNode(this);
}
bool eval(bool lhs, bool rhs) const
{
return lhs && rhs;
}
};
class SumNode : public AstNode
{
public:
virtual void accept(AstNodeVisitor& v)
{
v->visitSumNode(this);
}
int eval(int lhs, int rhs) const
{
return lhs + rhs;
}
};
class AstNodeVisitor
{
public:
...
bool visitCompareNode(CompareNode& node)
{
// won't work, because accept return void!
bool lhs = node.left()->accept(*this);
bool rhs = node.right()->accept(*this);
return node.eval(lhs, rhs);
}
int visitSumNode(Node& node)
{
// won't work, because accept return void!
int lhs = node.left()->accept(*this);
int rhs = node.right()->accept(*this);
return node.eval(lhs, rhs);
}
};
In this case both CompareNode and SumNode are binary operators but they rely on the return type of their children's visit.
As far as I can see to make it work, there are only 2 options:
accept can still return void, save the return value in a context object which is passed to each accept and visit function, and use them in the visit function, where I know what type to use. This should work but feels like a hack.
make AstNode a template, and accept function a none virtual, but return type depends on template parameter T.But if I do this, I no longer have a common AstNode* class and can't save any AstNode* in the children list.
for example:
template <typename T`>
class AstNode
{
public:
T accept(AstNodeVisitor&);
...
};
So is there a more elegant way to do this? This should be a fairly common problem for people implementing AST walking so I'd like to know what's the best practice.
Thanks.
The Visitor can have member that it can use to store result, something like:
class AstNodeVisitor
{
public:
void visitCompareNode(CompareNode& node)
{
node.left()->accept(*this); // modify b
bool lhs = b;
node.right()->accept(*this); // modify b
bool rhs = b;
b = node.eval(lhs, rhs);
}
void visitSumNode(Node& node)
{
node.left()->accept(*this); // modify n
int lhs = n;
node.right()->accept(*this); // modify n
int rhs = n;
n = node.eval(lhs, rhs);
}
private:
bool b;
int n;
};
You may also want to save the type of last result or use something like boost::variant.
template<class T> struct tag { using type=T; };
template<class...Ts> struct types { using type=types; }
template<class T>
struct AstVisitable {
virtual boost::optional<T> accept( tag<T>, AstNodeVisitor&v ) = 0;
virtual ~AstVisitable() {};
};
template<>
struct AstVisitable<void> {
virtual void accept( tag<void>, AstNodeVisitor&v ) = 0;
virtual ~AstVisitable() {};
};
template<class Types>
struct AstVisitables;
template<>
struct AstVisibables<types<>> {
virtual ~AstVisitables() {};
};
template<class T0, class...Ts>
struct AstVisitables<types<T0, Ts...>>:
virtual AstVisitable<T0>,
AstVisitables<types<Ts...>>
{
using AstVisitable<T0>::accept;
using AstVisitables<types<Ts...>>::accept;
};
using supported_ast_return_types = types<int, bool, std::string, void>;
class AstNode:public AstVisitables<supported_ast_return_types> {
public:
void addChild(AstNode* child);
AstNode* left() { return m_left.get(); }
AstNode* right() { return m_right.get(); }
private:
std::unique_ptr<AstNode> m_left;
std::unique_ptr<AstNode> m_right;
};
template<class types>
struct AstVisiablesFailAll;
template<>
struct AstVisiablesFailAll<> {
virtual ~AstVisiablesFailAll() {};
};
template<class T>
struct AstVisitableFailure : virtual AstVisitable<T> {
boost::optional<T> accept( tag<T>, AstNodeVisitor& ) override {
return {};
}
};
template<>
struct AstVisitableFailure<void> : virtual AstVisitable<void> {
void accept( tag<void>, AstNodeVisitor& ) override {
return;
}
};
template<class T0, class...Ts>
struct AstVisitablesFailAll<types<T0, Ts...>>:
AstVisitableFailure<T0>,
AstVisitableFailAll<types<Ts...>>
{
using AstVisitableFailure<T0>::accept;
using AstVisitableFailAll<types<Ts...>>::accept;
};
So now you can boost::optional<bool> lhs = node.left()->accept( tag<bool>, *this );, and from the state of lhs know if the left node can be evaluated in a bool context.
SumNode looks like this:
class SumNode :
public AstNode,
AstVisiablesFailAll<supported_ast_return_types>
{
public:
void accept(tag<void>, AstNodeVisitor& v) override
{
accept(tag<int>, v );
}
boost::optional<int> accept(tag<int>, AstNodeVisitor& v) override
{
return v->visitSumNode(this);
}
int eval(int lhs, int rhs) const {
return lhs + rhs;
}
};
and visitSumNode:
boost::optional<int> visitSumNode(Node& node) {
// won't work, because accept return void!
boost::optional<int> lhs = node.left()->accept(tag<int>, *this);
boost::optional<int> rhs = node.right()->accept(tag<int>, *this);
if (!lhs || !rhs) return {};
return node.eval(*lhs, *rhs);
}
The above assumes that visiting a+b in a void context is acceptable (like in C/C++). If it isn't, then you need a means for void visit to "fail to produce a void".
In short, accepting requires context, which also determines what type you expect. Failure is an empty optional.
The above uses boost::optional -- std::experimental::optional would also work, or you can roll your own, or you can define a poor man's optional:
template<class T>
struct poor_optional {
bool empty = true;
T t;
explicit operator bool() const { return !empty; }
bool operator!() const { return !*this; }
T& operator*() { return t; }
T const& operator*() const { return t; }
// 9 default special member functions:
poor_optional() = default;
poor_optional(poor_optional const&)=default;
poor_optional(poor_optional const&&)=default;
poor_optional(poor_optional &&)=default;
poor_optional(poor_optional &)=default;
poor_optional& operator=(poor_optional const&)=default;
poor_optional& operator=(poor_optional const&&)=default;
poor_optional& operator=(poor_optional &&)=default;
poor_optional& operator=(poor_optional &)=default;
template<class...Ts>
void emplace(Ts&&...ts) {
t = {std::forward<Ts>(ts)...};
empty = false;
}
template<class...Ts>
poor_optional( Ts&&... ts ):empty(false), t(std::forward<Ts>(ts)...) {}
};
which sucks, because it constructs a T even if not needed, but it should sort of work.
For completion sake I post the template version that is mentioned by the OP
#include <string>
#include <iostream>
namespace bodhi
{
template<typename T> class Beignet;
template<typename T> class Cruller;
template<typename T> class IPastryVisitor
{
public:
virtual T visitBeignet(Beignet<T>& beignet) = 0;
virtual T visitCruller(Cruller<T>& cruller) = 0;
};
template<typename T> class Pastry
{
public:
virtual T accept(IPastryVisitor<T>& visitor) = 0;
};
template<typename T> class Beignet : public Pastry<T>
{
public:
T accept(IPastryVisitor<T>& visitor)
{
return visitor.visitBeignet(*this);
}
std::string name = "Beignet";
};
template<typename T> class Cruller : public Pastry<T>
{
public:
T accept(IPastryVisitor<T>& visitor)
{
return visitor.visitCruller(*this);
}
std::string name = "Cruller";
};
class Confectioner : public IPastryVisitor<std::string>
{
public:
virtual std::string visitBeignet(Beignet<std::string>& beignet) override
{
return "I just visited: " + beignet.name;
}
virtual std::string visitCruller(Cruller<std::string>& cruller) override
{
return "I just visited: " + cruller.name;
}
};
}
int main()
{
bodhi::Confectioner pastryChef;
bodhi::Beignet<std::string> beignet;
std::cout << beignet.accept(pastryChef) << "\n";
bodhi::Cruller<std::string> cruller;
std::cout << cruller.accept(pastryChef) << "\n";
return 0;
}
Every pastry is a node and every visitor can implement its accepted return type. Having multiple visitor could visit the same pastry.