Polymorphic converting iterator - c++

Consider the following (simplified) scenario:
class edgeOne {
private:
...
public:
int startNode();
int endNode();
};
class containerOne {
private:
std::vector<edgeOne> _edges;
public:
std::vector<edgeOne>::const_iterator edgesBegin(){
return _edges.begin();
};
std::vector<edgeOne>::const_iterator edgesEnd(){
return _edges.end();
};
};
class edgeTwo {
private:
...
public:
int startNode();
int endNode();
};
class containerTwo {
private:
std::vector<edgeTwo> _edges;
public:
std::vector<edgeTwo>::const_iterator edgesBegin(){
return _edges.begin();
};
std::vector<edgeTwo>::const_iterator edgesEnd(){
return _edges.end();
};
};
I.e., I have two mostly identical edge types and two mostly identical container types. I can iterate over each kind individually. So far, so fine.
But now my use case is the following: Based on some criteria, I get either a containerOne or a containerTwo object. I need to iterate over the edges. But because the types are different, I cannot easily do so without code duplication.
So my idea is the following: I want to have an iterator with the following properties:
- Regarding its traversal behavior, it behaves either like a std::vector<edgeOne>::const_iterator or a std::vector<edgeTwo>::const_iterator, depending on how it was initialized.
- Instead of returning a const edgeOne & or const edgeTwo &, operator* should return a std::pair<int,int>, i.e., apply a conversion.
I found the Boost.Iterator Library, in particular:
iterator_facade, which helps to build a standard-conforming iterator and
transform_iterator, which could be used to transform edgeOne and edgeTwo to std::pair<int,int>,
but I am not completely sure how the complete solution should look like. If I build nearly the entire iterator myself, is there any benefit to use transform_iterator, or will it just make the solution more heavy-weight?
I guess the iterator only needs to store the following data:
A flag (bool would be sufficient for the moment, but probably an enum value is easier to extend if necessary) indicating whether the value type is edgeOne or edgeTwo.
A union with entries for both iterator types (where only the one that matches the flag will ever be accessed).
Anything else can be computed on-the-fly.
I wonder if there is an existing solution for this polymorphic behavior, i.e. an iterator implementation combining two (or more) underlying iterator implementation with the same value type. If such a thing exists, I could use it to just combine two transform_iterators.
Dispatching (i.e., deciding whether a containerOne or a containerTwo object needs to be accessed) could easily be done by a freestanding function ...
Any thoughts or suggestions regarding this issue?

How about making your edgeOne & edgeTwo polymorphic ? and using pointers in containers ?
class edge
class edgeOne : public edge
class edgeTwo : public edge
std::vector<edge*>

Based on some criteria, I get either a containerOne or a containerTwo object. I need to iterate over the edges. But because the types are different, I cannot easily do so without code duplication.
By "code duplication", do you mean source code or object code? Is there some reason to go past a simple template? If you're concerned about the two template instantiations constituting "code duplicaton" you can move most of the processing to an out-of-line non-templated do_whatever_with(int, int) support function....
template <typename Container>
void iterator_and_process_edges(const Container& c)
{
for (auto i = c.edgesBegin(); i != c.edgesEnd(); ++i)
do_whatever_with(i->startNode(), i->endNode());
}
if (criteria)
iterate_and_process_edges(getContainerOne());
else
iterate_and_process_edges(getContainerTwo());
My original aim was to hide the dispatching functionality from the code that just needs to access the start and end node. The dispatching is generic, whereas what happens inside the loop is not, so IMO this is a good reason to separate both.
Not sure I follow you there, but I'm trying. So, "code that just needs to access the start and end node". It's not clear whether by access you mean to get startNode and endNode for a container element, or to use those values. I'd already factored out a do_whatever_with function that used them, so by elimination your request appears to want to isolate the code extracting the nodes from an Edge - done below in a functor:
template <typename Edge>
struct Processor
{
void operator()(Edge& edge) const
{
do_whatever_with(edge.startNode(), edge.endNode());
}
};
That functor can then be applied to each node in the Container:
template <typename Container, class Processor>
void visit(const Container& c, const Processor& processor)
{
for (auto i = c.edgesBegin(); i != c.edgesEnd(); ++i)
processor(*i);
}
"hide the dispatching functionality from the code that just needs to access the start and end node" - seems to me there are various levels of dispatching - on the basis of criteria, then on the basis of iteration (every layer of function call is a "dispatch" in one sense) but again by elimination I assume it's the isolation of the iteration as above that you're after.
if (criteria)
visit(getContainerOne(), Process<EdgeOne>());
else
visit(getContainerTwo(), Process<EdgeTwo>());
The dispatching is generic, whereas what happens inside the loop is not, so IMO this is a good reason to separate both.
Can't say I agree with you, but then it depends whether you can see any maintenance issue with my first approach (looks dirt simple to me - a layer less than this latest incarnation and considerably less fiddly), or some potential for reuse. The visit implementation above is intended to be reusable, but reusing a single for-loop (that would simplify further if you have C++11) isn't useful IMHO.
Are you more comfortable with this modularisation, or am I misunderstanding your needs entirely somehow?

template<typename T1, typename T2>
boost::variant<T1*, T2*> get_nth( boost::variant< std::vector<T1>::iterator, std::vector<T2>::iterator > begin, std::size_t n ) {
// get either a T1 or a T2 from whichever vector you actually have a pointer to
}
// implement this using boost::iterator utilities, I think fascade might be the right one
// This takes a function that takes an index, and returns the nth element. It compares for
// equality based on index, and moves position based on index:
template<typename Lambda>
struct generator_iterator {
std::size_t index;
Lambda f;
};
template<typename Lambda>
generator_iterator< typename std::decay<Lambda>::type > make_generator_iterator( Lambda&&, std::size_t index=0 );
boost::variant< it1, it2 > begin; // set this to either one of the begins
auto double_begin = make_generator_iterator( [begin](std::size_t n){return get_nth( begin, n );} );
auto double_end = double_begin + number_of_elements; // where number_of_elements is how big the container we are type-erasing
Now we have an iterator that can iterate over one, or the other, and returns a boost::variant<T1*, T2*>.
We can then write a helper function that uses a visitor to extract the two fields you want from the returned variant, and treat it like an ADT. If you dislike ADTs, you can instead write up a class that wraps the variant and provides methods, or even change the get_nth to be less generic and instead return a struct with your data already produced.
There is going to be the equivalent of a branch on each access, but there is no virtual function overhead in this plan. It does currently requires an auto typed variable, but you can write an explicit functor to replace the lambda [begin](std::size_t n){return get_nth( begin, n );} and even that issue goes away.
Easier solutions:
Write a for_each function that iterates over each of the containers, and passes in the processed data to the passed in function.
struct simple_data { int x,y; };
std::function<std::function<void(simple_data)>> for_each() const {
auto begin = _edges.begin();
auto end = _edges.end();
return [begin,end](std::function<void(simple_data)> f) {
for(auto it = begin; it != end; ++it) {
simple_data d;
d.x = it->getX();
d.y = it->getY();
f(d);
}
};
};
and similar in the other. We now can iterate over the contents without caring about the details by calling foo->foreach()( [&](simple_data d) { /*code*/ } );, and because I stuffed the foreach into a std::function return value instead of doing it directly, we can pass the concept of looping around to another function.
As mentioned in comments, other solutions can include using boost's type-erased iterator wrappers, or writing a python-style generator mutable generator that returns either a simple_data. You could also directly use the boost iterator-creation functions to create an iterator over boost::variant<T1, T2>.

Related

design pattern for links

I have a bunch of functors that take some data and build new ones when operator() is invoked.
class Functor {
void operator()() {
pt = BaseFactory(INPUT);
}
std::shared_ptr<BASE> pt;
};
The input data of the functors may contain some "local" data to the functor, some "global static" data, or the result of another functor.
In the first two cases, the functor knows where data is. The problem comes with the last case, since functors may be deleted, modified and re-run, leading to reallocation or deletion of the actual data.
I was thinking of developing a single interface of a Link, that basically acts as a proxy for the data. Something like:
template<typename T>
class Link {
T & get();
};
Maybe, provide specializations for Persistent and Builded types and, in the last case, also provide methods to query the memory state of the data (broken link) or to ask who is supposed to build it.
How this kind of problems is generally approached?
Bonus question :
how to correctly define operator< to allow the use of associative containers, without risking to lose data? For example to have:
class AnotherFunctor {
void operator()() {
for(auto & iter : map){
iter.second = BaseFactory(iter.first.get());
}
}
std::map<Link<INPUT>,std::shared_ptr<BASE>> map;
}

iterator with special case implementation

Is there a way to build an iterator class that has two implementations : a general implementation for a container containing any number of elements and a special case (very fast) implementation when the container contains a single element wihtout using virtual functions and dynamic polymorphism ?
For the moment, I have :
struct Container {
struct FastIterator;
struct SlowIterator;
void add(...) { ... }
SlowIterator begin_slow() { ... }
FastIterator begin_fast() { ... }
};
instead I would like to have :
struct Container {
struct Iterator;
void add(...) { ... }
Iterator begin() { // select between fast and slow based on the contents of the container }
};
so that :
void f() {
Container c;
c.add(...);
Container::Iterator it = c.begin(); // uses FastIterator hidden by the Iterator type
}
void f2() {
Container c;
c.add(...);
c.add(...);
Container::Iterator it = c.begin(); // use SlowIterator hidden by the iterator type
}
Of course, the obvious way would be to use virtual function or a delegate in the Iterator implementation to switch from one case to the other, however I tested that this slows down a lot the iteration compared to directly using the Slow/Fast iterators.
Since all the information to decide which implementation to use is available during the call to begin(), I would think there is a way to use some kind of compile time polymorphism/trick to avoid any kind of indirection.
Also, I really don't want the user to have to decide if it should call begin_fast() or begin_slow(), this should be automatically handled and hidden by the Iterator class.
Is there a way ?
Thanks
Sure.
Your container becomes a std::variant of two different states, the "single element" state and the "many element" state (and maybe the "zero element" state).
The member function add can convert the zero or single-element container into a single or multi-element function. Similarly, a remove might do the opposite in some cases.
The variant itself doesn't have a begin or end. Instead, users must std::visit it with a function object that can accept either.
template<class T>
struct Container:
std::variant<std::array<T,0>, std::array<T,1>, std::vector<T>>
{
void add(T t) {
std::visit(
overload(
[&](std::array<T,0>& self) {
*this = std::array<T,1>{{std::move(t)}};
},
[&](std::array<T,1>& self) {
std::array<T,1> tmp = std::move(self);
*this = std::vector<T>{tmp[0], std::move(t)};
},
[&](std::vector<T>& self) {
self.push_back( std::move(t) );
}
),
*this
);
}
};
boost has a variant that works similarly. overload is merely
struct tag {};
template<class...Fs>
struct overload_t {overload_t(tag){}};
template<class F0, class F1, class...Fs>
struct overload_t: overload_t<F0>, overload_t<F1, Fs...> {
using overload_t<F0>::operator();
using overload_t<F1, Fs...>::operator();
template<class A0, class A1, class...Args>
overload_t( tag, A0&&a0, A1&&a1, Args&&...args ):
overload_t<F0>( tag{}, std::forward<A0>(a0)),
overload_t<F1, Fs...>(tag{}, std::forward<A1>(a1), std::forward<Args>(args)...)
{}
};
template<class F>
struct overload_t:F {
using F::operator();
template<class A>
overload_t( tag, A&& a ):F(std::forward<A>(a)){}
};
template<class...Fs>
overload_t<std::decay_t<Fs>...> overload(Fs&&...fs) {
return {tag{}, std::forward<Fs>(fs)...};
}
overload is ridiculously easier in c++17:
template<class...Fs>
struct overload:Fs{
using Fs::operator();
};
template<class...Fs>
overload->overload<Fs...>;
and use {} instead of ().
Use of this in c++14 looks like:
Container<int> bob = get_container();
std::visit( [](auto&& bob){
for (int x:bob) {
std::cout << x << "\n";
}
}, bob );
and for the 0 and 1 case, the size of the loop will be known exactly to the compiler.
In c++11 you'll have to write an external template function object instead of an inline lambda.
You could move the variant part out of the Container and into what begin returns (inside the iterator), but that would require a complex branching iterator implementation or for callers to visit on the iterator. And as the begin/end iterator types are probably tied, you'd want to return a range anyhow so the visit makes sense. And that gets you half way back to the Container solution anyhow.
You could also implement this outside of variant, but as a general rule earlier operations on a variable cannot change the later type in the same scope of code. It can be used to dispatch on a callable object passed in "continuation passing style", where both implementations will be compiled but one chosen at runtime (via branch). It may be possible for a compiler to realize which branch the visit will go down and dead-code eliminate the other, but the other branch still needs to be valid code.
If you want fully dynamicly typed objects, you are going to lose a factor of 2 to 10 speed at least (which is what languages who support this do), which is hard to recover by iteration efficiency on one element loops. That would be related to storing the variant-equivalent (maybe a virtual interface or whatever) in the iterator returned and making it complexly handle the branch at runtime. As your goal is performance, this isn't practical.
In theory, C++ could have the ability to change the type of variables based on operations on them. Ie, a theoretical language in which
Container c;
is of type "empty container", then:
c.add(foo);
now c changes static type to "single element container", then
c.add(foo);
and c changes static type to "multi-element container".
But that isn't the C++ type model. You can emulate it like above (at runtime), but it isn't the same.

Partial member function template specialisation and data member access

I have a question regarding partial specialisation of templated member functions.
Background: The goal is to compute descriptive statistics of large datasets which are too large to be hold in memory at once. Therefore I have accumulator classes for the variance and the covariance where I can push in the datasets piece by piece (either one value at a time or in larger chunks). A rather simplified version computing the arithmetic mean only is
class Mean
{
private:
std::size_t _size;
double _mean;
public:
Mean() : _size(0), _mean(0)
{
}
double mean() const
{
return _mean;
}
template <class T> void push(const T value)
{
_mean += (value - _mean) / ++_size;
}
template <class InputIt> void push(InputIt first, InputIt last)
{
for (; first != last; ++first)
{
_mean += (*first - _mean) / ++_size;
}
}
};
One particular advantage of this kind of accumulator class is the possibility to push values of different datatypes into the same accumulator class.
Problem: This works fine for all integral datatypes. However the accumulator classes should be able to handle complex numbers as well by first calculating the absolute value |z| and then pushing it to the accumulator. For pushing single values it's easy to provide an overloaded method
template <class T> void push(const std::complex<T> z)
{
T a = std::real(z);
T b = std::imag(z);
push(std::sqrt(a * a + b * b));
}
For pushing chunks of data via iterators however the case not quite as simple. In order to overload correctly a partial specialisation is required since we need to know the actual (fully specialised) complex number type. The usual way would be to delegate the actual code in an internal struct and specialise it accordingly
// default version for all integral types
template <class InputIt, class T>
struct push_impl
{
static void push(InputIt first, InputIt last)
{
for (; first != last; ++first)
{
_mean += (*first - _mean) / ++_size;
}
}
};
// specialised version for complex numbers of any type
template <class InputIt, class T>
struct push_impl<InputIt, std::complex<T>>
{
static void push(InputIt first, InputIt last)
{
for (; first != last; ++first)
{
T a = std::real(*first);
T b = std::imag(*first);
_mean += (std::sqrt(a * a + b * b) - _mean) / ++_size;
}
}
};
In the accumulator class the templated methods of the delegation struct are then called by
template <class InputIt>
void push(InputIt first, InputIt last)
{
push_impl<InputIt, typename std::iterator_traits<InputIt>::value_type>::push(first, last);
}
However there is one problem with this technique which is how to access the private members of the accumulator class. Since they are different classes no direct access is possible and furthermore the methods of push_impl need to be static and cannot access the non-static members of the accumulator.
I can think of the following four solutions to the problem which all have their own advantages and disadvantages:
Create an instance of push_impl in each call to push with (possible) decrease in performance due to the extra copy.
Have an instance of push_impl as member variable of the accumulator class, which would prevent me from pushing different datatypes into the accumulator since the instance would have to be fully specialised.
Make all members of the accumulator class public and pass *this to push_impl::push() calls. This is a particular bad solution due to the break in encapsulation.
Implement the iterator version in terms of the single value version, i.e. call the push() method for each element with (possible) decrease in performance due to the extra function call.
Note that the mentioned decreases performance are theoretical in their nature and might be no problem at all due to clever inlining by the compiler, however the actual push methods might be much more complex than the example from above.
Is one solution preferable to the others or do I miss something?
Best regards and many thanks.
As commented, you don't need to use partial specialization for this at all, indeed partial specialization is usually pretty easy to avoid, and preferred to avoid.
private:
template <class T>
struct tag{}; // trivial nested struct
template <class I, class T>
void push_impl(I first, I last, tag<T>) { ... } // generic implementation
template <class I, class T>
void push_impl(I first, I last, tag<std::complex<T>>) { ... } // complex implementation
public:
template <class InputIt>
void push(InputIt first, InputIt last)
{
push_impl(first, last,
tag<typename std::iterator_traits<InputIt>::value_type> {});
}
Since push_impl is a (private) member function you don't need to do anything special any more.
Compared to your proposed solutions, this has no extra performance cost. It's the same number of function calls, the only difference is passing a stateless type by value, which is a wholly trivial optimization for the compiler. And there's no sacrifice in encapsulation either. And slightly less boilerplate.
push_impl can be made either an inner class template (if you use c++11) or a friend class template of your accumulator class (this seems like a good case for using friend declarations, since push_impl is essentially an integral part of your accumulator class implementation, separated purely for language reasons). Then you can use your option #3 (passing this to static methods of push_impl), but without making accumulator members public.
Option #4 doesn't seem too bad either (since it avoids code duplication), but as you mentioned the performance impacts would need to be measured.
Personally I'd be likely to choose your option 4, after all the only part of the iterator version that actually varies with type is the logic in the "single value version"
However another option is to write your iterator versions to receive the mean and size by reference, mean and size can then be updated without them having to be made public.
This will also help with testing as it allows push_impl to be tested separately (although with this approach you might consider that's no longer the best name for the function)
As an aside, it would be better for your push_impl to be templated only on iterator type, you can deduce the value type inside push_impl in the same way that you currently so in your calling example, but with only the iterator type as a parameter there's no chance of accidently calling it with the wrong value type (which might not always cause a compilation error if the value type can be converted to the type you pass as "T")

a dictionary with compile time keys, or types as keys

Take the following "type" dictionary -- its keys are supposed to be types, and values an instance of that type:
class TypeDictionary {
public:
template<class T> void insert(T t);
template<class T> T& get();
// implementation be here -- and the question is about this
};
struct Foo;
struct Bar;
void userOfTypeDictionary() {
TypeDictionary td;
td.insert( Foo() );
td.insert( Bar() );
td.insert( double(3.14) );
// and other unknown (to TypeDictionary)
// list of types attached
// later on, in a different scope perhaps
Foo& f = td.get<Foo>() ;
Bar& f = td.get<Bar>();
double pi = tg.get<double>();
// ...
}
This particular TypeDictionary has a growing set of types "registered", but of course, the facility should allow an arbitrary set of types, and potentially a different set for each instance of this class.
As for a realistic motivating use case, think of a plugin manager. At any given time of its life an arbitrary heterogeneous set of objects are attached to it, and the it is the manager's job to manage the life time of the attached objects and return (a reference) to them when queried in a type-safe manner.
Any ideas on whether such a thing is possible? It is fine if a strategy involves using a library, like Boost.Fusion or similar.
A minimal but probably sub-optimal way of doing this is using the type-wise versions of std::get that are only available in C++14. This way your dictionary would simply derive or wrap a tuple:
template<typename... T>
struct dict
{
std::tuple<T...> t;
template<typename E>
void insert(E e) { std::get<E>(t) = e; }
template<typename E>
E& get() { return std::get<E>(t); }
};
struct Foo {};
struct Bar {};
using TypeDictionary = dict<Foo, Bar, double>;
The above gives the desired behavior with clang -std=c++1y but fails with g++ up to 4.8.2 at least.
Anyhow, the idea is that you have a predefined list of type-keys given up-front, and your dictionary behaves like a fixed-size flat set, meaning that it includes size for its elements (at least non-empty ones) even if you don't call insert(), in which case these elements are default-constructed.
I have paid no particular attention here to giving const/non-const versions of methods, or move operations. The above is only to get the idea. Of course, this is so simple that you could just use std::tuple and std::get() directly.
To have the dictionary with a fixed type regardless of its element types is quite harder; it's a matter of type erasure and in this respect it resembles std::function. To get a really dynamic type-safe heterogeneous container (with no casts, virtual functions etc.) is not possible with the given syntax I think. What is possible is to get a new dictionary of a new (enlarged) type after each insert() operation, e.g.
auto new_d = d.insert(3.14);
This would simply concatenate the existing tuple<T...> with the new element to give a new tuple<T...,double>.

C++ : Using different iterator types in subclasses without breaking the inheritance mechanism

I'm trying to achieve the following: Given an abstract class MemoryObject, that every class can inherit from, I have two subclasses: A Buffer and a BigBuffer:
template <typename T>
class MemoryObject
{
public:
typedef typename std::vector<T>::iterator iterator;
typedef typename std::vector<T>::const_iterator const_iterator;
[...] //Lot of stuff
virtual iterator begin() = 0;
virtual iterator end() = 0;
};
A Buffer:
template <typename T>
class Buffer: public MemoryObject<T>
{
public:
typedef typename std::vector<T>::iterator iterator;
iterator begin() { return buffer_.begin(); }
iterator end() { return buffer_.end(); };
[...] //Lot of stuff
private:
std::vector<T> buffer_;
};
And finally:
template <typename T>
class BigBuffer: public MemoryObject<T>
{
public:
[...] //Omitted, for now
private:
std::vector<Buffer<T>*> chunks_;
};
As you can see, a BigBuffer holds a std::vector of Buffer<T>*, so you can view a BigBuffer as an aggregation of Buffer(s). Futhermore, I have a bunch of functions that must work of every MemoryObject, so this is a real signature:
template <class KernelType, typename T>
void fill(CommandQueue<KernelType>& queue, MemoryObject<T>& obj, const T& value)
{
//Do something with obj
}
What's the point? - You may ask. The point is that I must implement iterators over these classes. I've already implemented them for Buffer, and is exactly what I need: be able to iterate over a Buffer, and access to ranges (for example b.begin(), b.begin() + 50).
Obviously I can't do the same for BigBuffer, because the real data (that is inside each Buffer' private variable buffer_) is scattered accross the memory. So I need a new class, let's call it BigBufferIterator, which can overload operator like * or +, allowing me to "jump" from a memory chunk to another without incurring in in segmentation fault.
The problems are two:
The iterator type of MemoryObject is different from the iterator
type of BigBuffer: the former is a std::vector<T>::iterator, the
latter a BigBufferIterator. My compiler obviously complains
I want be able to preserve the genericity of my functions signatures
passing to them only a MemoryObject<T>&, not specializing them for
each class type.
I've tried to solve the first problem adding a template parameter classed Iterator, and giving it a default argument to each class, with a model loosely based to Alexandrescu's policy-based model. This solution solved the first issue, but not the second: my compiled still complains, telling me: "Not known conversion from BigBuffer to MemoryObject", when I try to pass a BigBuffer to a function (for example, the fill() ). This is because Iterator types are different..
I'm really sorry for this poem, but It was the only way to proper present my problem to you. I don't know why someone would even bother in reading all this stuff, but I'll take pot luck.
Thanks in advance, only just for having read till this point.
Humbly,
Alfredo
They way to go is to use the most general definition as the iterator type of the base. That is, you want to treat the data in a Buffer as just one segment while the BigBuffer is a sequence of the corresponding segments. This is a bit unfortunate because it means that you treat your iterator for the single buffer in Buffer as if it may be multiple buffers, i.e. you have a segmented structure with just one segment. However, compared to the alternative (i.e. a hierarchy of iterators with virtual functions wrapped by a handle giving value semantics to this mess) you are actually not paying to bad a cost.
I encountered the same problem in a different context; let me restate it.
You have a Base class (which could be abstract), which is iterable via its BaseIterator.
You have a Child subclass, which differs in implementation slightly, and for which you have a different specialized ChildIterator.
You have custom functions that work with Base, and rely on its iterability.
It is not feasible to generate a template specialization of the custom functions for each subclass of Base. Possible reasons may be:
huge code duplication;
you distribute this code as a library and other developers are going to subclass Base;
other classes will take some reference or pointer to Base and apply those custom functions, or more generically:
Base implements some logic that is going to be uses in contexts where do not know any of the subclasses (and writing completely templated code is too cumbersome).
Edit: Another possibility would be using type erasure. You would hide the real iterator that you're using behind a class of a fixed type. You would have to pay the cost of the virtual functions call though. Here is an implementation of a any_iterator class which implements exactly iterator type erasure, and some more background on it.
The only effective solution I could find was to write a multi-purpose iterator that can iterate over all possible containers, possibly exploiting their internals (to avoid copy-pasting the iterator code for every subclass of Base):
// A bigger unit of memory
struct Buffer;
class Iterator {
// This allows you to know which set of methods you need to call
enum {
small_chunks,
big_chunks
} const _granularity;
// Merge the data into a union to save memory
union {
// Data you need to know to iterate over ints
struct {
std::vector<int> const *v;
std::vector<int>::const_iterator it;
} _small_chunks;
// Data you need to know to iterate over buffer chunks
struct {
std::vector<Buffer *> const *v;
std::vector<Buffer *>::const_iterator it;
} _big_chunks;
};
// Every method will have to choose what to do
void increment() {
switch (_granularity) {
case small_chunks:
_small_chunks.it++;
break;
case big_chunks:
_big_chunks.it++;
break;
}
}
public:
// Cctors are almost identical, but different overloads choose
// different granularity
Iterator(std::vector<int> const &container)
: _granularity(small_chunks) // SMALL
{
_small_chunks.v = &container;
_small_chunks.it = container.begin();
}
Iterator(std::vector<Buffer *> const &container)
: _granularity(big_chunks) // BIG
{
_big_chunks.v = &container;
_big_chunks.it = container.begin();
}
// ... Implement all your methods
};
You can use the same class for both Base and Child, but you need to initialize it differently. At this point you can make begin and end virtual and return an Iterator constructed differently in each subclass:
class Base {
public:
virtual Iterator begin() const = 0;
};
class IntChild : public Base {
std::vector<int> _small_mem;
public:
virtual Iterator begin() const {
// Created with granularity 'small_chunks'
return Iterator(_small_mem);
}
// ...
};
class BufferChild : public Base {
std::vector<Buffer *> _big_mem;
public:
virtual Iterator begin() const {
// Created with granularity 'big_chunks'
return Iterator(_big_mem);
}
// ...
};
You will pay a small price in performance (because of the switch statements) and in code duplication, but you will be able to use any generic algorithm from <algorithm>, to use range-loop, to use Base only in every function, and it's not stretching the inheritance mechanism.
// Anywhere, just by knowing Base:
void count_free_chunks(Base &mem) {
// Thanks to polymorphism, this code will always work
// with the right chunk size
for (auto const &item : mem) {
// ...this works
}
// This also works:
return std::count(mem.begin(), mem.end(), 0);
}
Note that the key point here is that begin() and end() must return the same type. The only exception would be if Child's methods would shadow Base's method (which is in general not a good practice).
One possible idea would be to declare an abstract iterator, but this is not so good. You would have to use all the time a reference to the iterator. Iterator though are supposed to be carried around as lightweight types, to be assignable and easily constructible, so it would make coding a minefield.