patterncppMinor
Exposing Iterators while Truly Hiding Their Implementation
Viewed 0 times
exposingwhilehidingtrulyimplementationiteratorstheir
Problem
While implementing classes composed of various collections, I've found that it's pretty hard to completely hide the type of collection used, while still allowing C++ style operations on iterators.
For example, the following two classes allow the client to easily iterate through the collection, but require that all the client code be recompiled, should the collection change from vector to another type.
So I've designed a generic iterator wrapper that uses delegates to invoke the actual iterators. Currently, for simplicity, I've only implemented an input iterator.
```
template
class InputIteratorDelegate final : public std::iterator
{
public:
typedef void* DelegateType;
typedef std::function CopyFuncType;
typedef std::function DestructorFuncType;
typedef std::function ComparerFuncType;
typedef std::function PrefixIncrementFuncType;
typedef std::function DereferenceFuncType;
InputIteratorDelegate(DelegateType delegateTo, CopyFuncType copy, DestructorFuncType destructor,
ComparerFuncType comparer, PrefixIncrementFuncType prefixIncrement, DereferenceFuncType dereference) :
_delegateTo(delegateTo), _copy(copy), _destructor(destructor), _comparer(comparer),
_prefixIncrement(prefixIncrement), _dereference(dereference)
{
}
InputIteratorDelegate(const InputIteratorDelegate& other) :
_delegateTo(other._copy(other._delegateTo)), _copy(other._copy), _destructor(other._destructor),
_comparer(other._comparer), _prefixIncrement(other._prefixIncrement), _dereference(other._dereference)
{
}
InputIteratorDelegate(InputIteratorDelegate&& other) :
_delegateTo(other._delegateTo), _copy(std::move(
For example, the following two classes allow the client to easily iterate through the collection, but require that all the client code be recompiled, should the collection change from vector to another type.
class FooV1
{
public:
FooV1();
const std::vector& getBars() const;
private:
std::vector _bars;
};
class FooV2
{
public:
FooV2();
std::vector::const_iterator beginBars() const;
std::vector::const_iterator endBars() const;
private:
std::vector _bars;
};So I've designed a generic iterator wrapper that uses delegates to invoke the actual iterators. Currently, for simplicity, I've only implemented an input iterator.
```
template
class InputIteratorDelegate final : public std::iterator
{
public:
typedef void* DelegateType;
typedef std::function CopyFuncType;
typedef std::function DestructorFuncType;
typedef std::function ComparerFuncType;
typedef std::function PrefixIncrementFuncType;
typedef std::function DereferenceFuncType;
InputIteratorDelegate(DelegateType delegateTo, CopyFuncType copy, DestructorFuncType destructor,
ComparerFuncType comparer, PrefixIncrementFuncType prefixIncrement, DereferenceFuncType dereference) :
_delegateTo(delegateTo), _copy(copy), _destructor(destructor), _comparer(comparer),
_prefixIncrement(prefixIncrement), _dereference(dereference)
{
}
InputIteratorDelegate(const InputIteratorDelegate& other) :
_delegateTo(other._copy(other._delegateTo)), _copy(other._copy), _destructor(other._destructor),
_comparer(other._comparer), _prefixIncrement(other._prefixIncrement), _dereference(other._dereference)
{
}
InputIteratorDelegate(InputIteratorDelegate&& other) :
_delegateTo(other._delegateTo), _copy(std::move(
Solution
I guess you're already aware that you'll pay a performance cost for the benefit of hiding the implementation, as each method will be dispatched via a
It's no longer considered good practice to inherit from
Wrapping the other iterator categories is going to involve more work (you can probably use inheritance for each category to build on its parent category, so you shouldn't have to repeat too much).
It's probably worth implementing
std::function object, and there will be no opportunity for inlining.It's no longer considered good practice to inherit from
std::iterator (and it's deprecated since C++17). Instead, simply declare the types directly in your class:using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;Wrapping the other iterator categories is going to involve more work (you can probably use inheritance for each category to build on its parent category, so you shouldn't have to repeat too much).
InputIteratorDelegate doesn't need two template arguments, as T should always be typename TIterator::value_type.It's probably worth implementing
InputIteratorDelegate::swap(), which will reduce duplication in your constructors and assignment operators.Code Snippets
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;Context
StackExchange Code Review Q#120619, answer score: 2
Revisions (0)
No revisions yet.