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);
}
Related
I would like to find a clean way to slightly change the way std::vector operates.
Problem background
I need to be able to have index in the vector where the pointer would essentially slip one back, so for example; if my vector contains {0,1,2,3,4,5} and index [3] needs to slip, when iterating through the vector it should return: 0, 1, 2, 3, 3, 4, 5
Problem
Without rewriting the entire vector class and implementing my changes, is it possible to inherit std::vector<T> into a custom class, and override the behavior of the iterators ++ operator as well as the vector::begin()? (to add memory of the fact that it has already slipped one and not to slip again)
What I have tried
I have tried a basically implementation so far, but have gotten stuck when trying to override the begin() function.
#include <vector>
#include <iostream>
#include <iterator>
template <typename T>
class myVector : public std::vector<T>{
public:
class myIterator : public std::vector<T>::iterator
{
// call vector's iterotor constructor
//override
// iterator& operator++(void){}
// iterator operator++(int){}
};
using std::vector<T>::vector; // use the constructor from vector
// extend the begin function, but include original operation
myVector::myIterator begin() const
{
std::cout << "hi\n"; // testing to see if begin has been overriden properly
return std::vector<T>::begin();
}
};
// print out the vector
template <typename T>
std::ostream& operator<<(std::ostream& os, const myVector<T>& v)
{
auto begin=v.begin();
while (begin!=v.end())
{
os << *begin;
++begin;
if(begin!=v.end())
{
os << ',';
}
}
return os;
}
int main() {
myVector<int> vec = {1,2,3,4};
std::cout << vec << '\n';
}
If you have an alternative clean solutions, it is also welcome.
Thanks
I would never inherit from std::vector publicly. It is just too easy to be used wrong and you have no control over misuse. std::vector has no virtual destructor!
Iterators seems to be the appropriate customization point. Much more is needed to get a fully compliant iterator, but this is enough for a working example:
#include <iterator>
#include <vector>
#include <iostream>
template <typename It>
struct slippery_iterator {
bool slipped = false;
It to_be_repeated;
It iterator;
slippery_iterator(It iterator,It to_be_repeated) : iterator(iterator),to_be_repeated(to_be_repeated){}
slippery_iterator& operator++(){
if (!slipped && iterator == to_be_repeated){
slipped = true;
return *this;
}
++iterator;
return *this;
}
typename std::iterator_traits<It>::reference operator*(){
return *iterator;
}
bool operator!=(It other) { return iterator != other;}
};
template <typename It>
slippery_iterator<It> make_slippery_iterator(It it,It slip){
return {it,slip};
}
int main() {
std::vector<int> x{1,2,3,4,5};
auto begin = make_slippery_iterator(x.begin(),x.begin()+2);
for (; begin != x.end(); ++begin){
std::cout << *begin;
}
}
Output:
123345
PS: Note that I am used to C++11 where you need a make_x helper. It isnt needed with more recent standards.
As others already said, just define a custom iterator. If you moreover want the nice range iteration, you can then just define a custom range class returning your custom iterators.
See a working example below (needs --std=c++17):
#include <vector>
#include <iostream>
template<typename T>
class SlipIterator {
private:
using iterator = typename std::vector<T>::const_iterator;
iterator i;
iterator slip;
bool has_slipped;
public:
SlipIterator(iterator i, iterator slip): i(i), slip(slip), has_slipped(false) {}
SlipIterator &operator++() {
if ((!has_slipped) && (i == slip))
has_slipped = true;
else
++i;
return *this;
}
bool operator!=(SlipIterator<T> &the_end) const { return i != the_end.i; }
const T &operator*() const { return *i; }
};
template<typename T>
class SlipperyRange {
private:
const std::vector<T> &v;
size_t slip_index;
public:
SlipperyRange(const std::vector<T> &v, size_t slip_index) : v(v), slip_index(slip_index) {}
SlipIterator<T> begin() const { return SlipIterator<T>(v.cbegin(), v.cbegin() + slip_index); }
SlipIterator<T> end() const { return SlipIterator<T>(v.cend(), v.cend()); }
};
int main() {
std::vector<int> v{1,2,3,4,5};
for(const int i: SlipperyRange{v, 2})
std::cout << i << ' ';
std::cout << '\n';
return 0;
}
I've got this Map in my Entity-Component-System:
std::map<u_int32_t, std::vector<std::shared_ptr<Component>>> _componentMap;
The u_int32_t is the key to a vector of components. There can be multiple instances of the same component. (That's why there's a vector).
Now I would like to have a templated getter-function that returns a Vector of an inherited type:
template<class T> inline const std::vector<std::shared_ptr<T>> & getVector() const
{
u_int32_t key = getKey<T>();
return static_cast<std::vector<std::shared_ptr<T>>>(_componentMap.count(key) ? _componentMap.at(key) : _emptyComponentVec);
}
I know that this doesn't work, since std::vectors of different types are completely unrelated and I cannot cast between them. I would also like to avoid allocating a new vector every time this function is called.
But how I can I get the desired behaviour? When the the components are added I can create an std::vector of the desired derived type.
The question could also be: How can I have an std::map containing different types of std::vector?
For any solutions I can not link against boost, though if absolutely needed, I could integrate single headers of boost.
template<class It>
struct range_view {
It b, e;
It begin() const { return b; }
It end() const { return e; }
using reference = decltype(*std::declval<It const&>());
reference operator[](std::size_t n) const
{
return b[n];
}
bool empty() const { return begin()==end(); }
std::size_t size() const { return end()-begin(); }
reference front() const {
return *begin();
}
reference back() const {
return *std::prev(end());
}
template<class O>
range_view( O&& o ):
b(std::begin(o)), e(std::end(o))
{}
};
this is a quick range view. It can be improved.
Now all you need to do is write a pseudo-random-access iterator that converts its arguments. So it takes a random access iterator over a type T, then does some operation F to return a type U. It forwards all other operations.
The map then stores std::vector<std::shared_ptr<Base>>. The gettor returns a range_view< converting_iterator<spBase2spDerived> >.
Here is a crude implementation of a solution I have in mind for this problem. Of course, there are many rooms to refine the code, but hopefully it conveys my idea.
#include <iostream>
#include <map>
#include <vector>
#include <memory>
using namespace std;
class Base {
public:
virtual void f() const = 0;
};
class A : public Base {
public:
static const int type = 0;
explicit A(int a) : a_(a) {}
void f() const { cout << "calling A::f" << endl;}
int a_;
};
class B : public Base {
public:
static const int type = 1;
explicit B(int a) : a_(a) {}
void f() const { cout << "calling B::f" << endl;}
int a_;
};
class MapWrapper {
public:
template<class T>
void append(int a, vector<T> const& vec) {
types_[a] = T::type;
my_map_[a] = make_shared<vector<T>>(vec);
}
template<class T>
vector<T> const& get(int a) const {
return *static_pointer_cast<vector<T>>( my_map_.at(a) );
}
map<int, shared_ptr<void>> const& get_my_map() const {
return my_map_;
}
vector<shared_ptr<Base>> get_base(int a) const {
vector<shared_ptr<Base>> ret;
switch(types_.at(a)) {
case 0: {
auto const vec = get<A>(a);
for(auto v : vec)
ret.push_back(make_shared<A>(v));
break;
}
case 1: {
auto const vec = get<B>(a);
for(auto v : vec)
ret.push_back(make_shared<B>(v));
break;
}
}
return ret;
}
map<int, shared_ptr<void>> my_map_;
map<int, int> types_;
};
int main() {
MapWrapper map_wrapper;
map_wrapper.append(10, vector<A>{A(2), A(4)});
map_wrapper.append(20, vector<B>{B(5), B(7), B(9)});
for(auto const& w : map_wrapper.get_my_map())
for(auto v : map_wrapper.get_base(w.first))
v->f();
for(auto const& x: map_wrapper.get<A>(10))
cout << x.a_ << " ";
cout << endl;
for(auto const& x: map_wrapper.get<B>(20))
cout << x.a_ << " ";
return 0;
}
The solution was to use reinterpret_cast:
template<class T> inline std::vector<std::shared_ptr<T>> * getVector() const
{
auto key = getKey<T>();
return reinterpret_cast<std::vector<std::shared_ptr<T>> *>( (_componentMap.count(key) ? _componentMap.at(key).get() : const_cast<std::vector<std::shared_ptr<Component>> *>(&_emptyComponentSharedPtrVec)) );
}
It's not very pretty but it does work fine and it fulfills all requirements.
I'm trying to store and manipulate a list of template class objects with different parameter types; the template class has two parametrised methods, one returning the parameter type and a void one accepting it as input.
More specifically, I have a template class defined as follows:
template<typename T>
class Test
{
public:
virtual T a() = 0;
virtual void b(T t) = 0;
};
And different specifications of it, such as:
class TestInt : public Test<int>
{
public:
int a() {
return 1;
}
void b(int t) {
std::cout << t << std::endl;
}
};
class TestString : public Test<std::string>
{
public:
std::string a() {
return "test";
}
void b(std::string t) {
std::cout << t << std::endl;
}
};
I'd like to be able to store in one single list different objects of both TestInt and TestString type and loop through it calling one method as input for the other, as in:
for (auto it = list.begin(); it != list.end(); ++it)
(*it)->b((*it)->a());
I've looked into boost::any but I'm unable to cast the iterator to the specific class, because I don't know the specific parameter type of each stored object. Maybe this cannot be done in a statically typed language as C++, but I was wondering whether there could be a way around it.
Just for the sake of completeness, I'll add that my overall aim is to develop a "parametrised observer", namely being able to define an observer (as with the Observer Pattern) with different parameters: the Test class is the observer class, while the list of different types of observers that I'm trying to properly define is stored within the subject class, which notifies them all through the two methods a() and b().
The virtuals have actually no meaning here, since for each T the signatures are distinct.
So it seems you have Yet Another version of the eternal "how can we emulate virtual functions templates" or "how to create an interface without virtual functions":
Generating an interface without virtual functions?
How to achieve "virtual template function" in C++
The first one basically contains an idea that you could employ here.
Here's an idea of what I'd do:
Live On Coliru
#include <algorithm>
#include <iostream>
namespace mytypes {
template <typename T>
struct Test {
T a() const;
void b(T t) { std::cout << t << std::endl; }
};
template <> int Test<int>::a() const { return 1; }
template <> std::string Test<std::string>::a() const { return "test"; }
using TestInt = Test<int>;
using TestString = Test<std::string>;
}
#include <boost/variant.hpp>
namespace mytypes {
using Value = boost::variant<int, std::string>;
namespace detail {
struct a_f : boost::static_visitor<Value> {
template <typename T>
Value operator()(Test<T> const& o) const { return o.a(); }
};
struct b_f : boost::static_visitor<> {
template <typename T>
void operator()(Test<T>& o, T const& v) const { o.b(v); }
template <typename T, typename V>
void operator()(Test<T>&, V const&) const {
throw std::runtime_error(std::string("type mismatch: ") + __PRETTY_FUNCTION__);
}
};
}
template <typename O>
Value a(O const& obj) {
return boost::apply_visitor(detail::a_f{}, obj);
}
template <typename O, typename V>
void b(O& obj, V const& v) {
boost::apply_visitor(detail::b_f{}, obj, v);
}
}
#include <vector>
int main()
{
using namespace mytypes;
using AnyTest = boost::variant<TestInt, TestString>;
std::vector<AnyTest> list{TestInt(), TestString(), TestInt(), TestString()};
for (auto it = list.begin(); it != list.end(); ++it)
b(*it, a(*it));
}
This prints
1
test
1
test
Bonus Points
If you insist, you can wrap the AnyTest variant into a proper class and have a() and b(...) member functions on that:
Live On Coliru
int main()
{
using namespace mytypes;
std::vector<AnyTest> list{AnyTest(TestInt()), AnyTest(TestString()), AnyTest(TestInt()), AnyTest(TestString())};
for (auto it = list.begin(); it != list.end(); ++it)
it->b(it->a());
}
Expanding on my comment above, the simplest what I can currently think of to achieve what you are trying to do - at least as I understood it from your example code - is the following:
/* Interface for your container, better not forget the destructor! */
struct Test {
virtual void operate(void) = 0;
virtual ~Test() {}
};
/* Implementation hiding actual type */
template<typename T>
struct TestImpl : public T, public Test {
void operate(void) {
T::b(T::a());
}
};
/* Actual code as template policies */
struct IntTest {
int a(void) {
return 42;
}
void b(int value) {
std::cout << value << std::endl;
}
};
struct StringTest {
std::string a(void) {
return "Life? Don't talk to me about life.";
}
void b(std::string value) {
std::cout << value << std::endl;
}
};
You would then need to create a container for objects of class Test and fill it with objects of the respective TestImpl<IntTest>, TestImpl<StringTest>, and so on. To avoid object slicing you need reference or pointer semantics, that is std::vector<std::unique_ptr<Test> > for example.
for (auto it = list.begin(); it != list.end(); ++it) {
(*it)->operate();
}
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 want to specify in a header file that the input to a function will be an iterator_range, and the iterator can be dereferenced to obtain an int. What is the best way to do this? I do not want to say iterator_range<std::vector<int>::iterator> as this binds the implementation to use std::vector<int>.
Would appreciate any ideas to specify the function the best way.
A common way to do this is to make the range a template parameter and then let the usage you make of it be the "concept check":
template<class SinglePassRange>
void func(SinglePassRange const & nums)
{
typedef typename boost::range_iterator<SinglePassRange>::type It;
for(It it = nums.begin(), e = nums.end(); it != e; ++it)
{
int i = *it;
// Do something with your int
}
}
This won't compile if your range does not contain ints (or something convertible to int) so there's no need to add any further constraints to the interface. But if you really want you can add a concept check at the begining of your function (it will provide better error messages to your clients):
BOOST_CONCEPT_ASSERT(
(boost::Convertible<typename boost::range_value<SinglePassRange>::type,
int>));
Finally, if you don't want to make your function a template then I think that you'll have to cope with taking a boost::iterator_range<std::vector<int>::iterator> but in that case I see no advantage with respect to taking a simple std::vector&.
Your question seems a bit unclear and/or the requirements are contradictory.
It seems you want a function to take a boost::iterator_range<Container<int>::iterator> where Container can be anything, yet you don't want a function template.
I don't see how that can be achieved, except by overloading the function for all the iterators you want to support (gets even worse if you don't strictly mean a container of int).
Here's something just for fun, a quick any_iterator that uses type-erasure so that it can indeed wrap any iterator (which dereferences to a particular type). (Actually by Google'ing you might be able to find a more complete class with the same name.)
It is used by a non-template sum function:
#include <numeric>
#include <vector>
#include <list>
#include <iostream>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/range/iterator_range.hpp>
namespace detail
{
template <class T>
class any_iterator_base
{
public:
virtual ~any_iterator_base() {}
virtual void increment() = 0;
virtual bool equal(const any_iterator_base& other) const = 0;
virtual T& dereference() const = 0;
};
template <class T, class Iter>
class any_iterator_impl: public any_iterator_base<T>
{
Iter it;
public:
any_iterator_impl(Iter it): it(it) {}
virtual void increment() { ++it; }
virtual bool equal(const any_iterator_base& other) const
{
//warning: throws if dynamic type of other is not the same as *this (can't compare iterators of different type)
return it == dynamic_cast<const any_iterator_impl<T, Iter>&>(other).it;
}
virtual T& dereference() const { return *it; }
};
} //namespace detail
template <class T>
class any_iterator: public boost::iterator_facade<any_iterator<T>, T, boost::forward_traversal_tag>
{
boost::shared_ptr<detail::any_iterator_base<T> > iter;
public:
template <class Iter>
any_iterator(Iter it): iter(new detail::any_iterator_impl<T, Iter>(it)) {}
private:
friend class boost::iterator_core_access;
void increment() { iter->increment(); }
bool equal(const any_iterator& other) const
{
return iter->equal(*other.iter);
}
T& dereference() const { return iter->dereference(); }
};
int sum(const boost::iterator_range<any_iterator<int> >& range)
{
return std::accumulate(range.begin(), range.end(), 0);
}
int main()
{
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::vector<int> vec(arr, arr + 5);
std::list<int> li(arr + 5, arr + 10);
std::cout << sum(boost::make_iterator_range(any_iterator<int>(vec.begin()), any_iterator<int>(vec.end()))) << '\n';
std::cout << sum(boost::make_iterator_range(any_iterator<int>(li.begin()), any_iterator<int>(li.end()))) << '\n';
std::cout << sum(boost::make_iterator_range(any_iterator<int>(arr), any_iterator<int>(arr + 10))) << '\n';
}
(I'd still doubt, whether it is a practical solution. Templates are made for these things.)