I have a class that holds a list containing boost::shared_ptrs to objects of another class.
The class member functions that give access to the elemets in the list return raw pointers. For consistency I'd also like to be able to iterate with raw pointers instead of shared_ptrs. So when I dereference the list iterator, I'd like to get raw pointer, not a shared_ptr.
I assume I need to write a custom iterator for this. Is this correct? If so can someone point me in the right direction - I've never done this before.
Here's an option using Boost transform_iterator:
#include <list>
#include <boost/iterator/transform_iterator.hpp>
#include <tr1/memory>
#include <tr1/functional>
using std::list;
using std::tr1::shared_ptr;
using boost::transform_iterator;
using boost::make_transform_iterator;
using std::tr1::mem_fn;
using std::tr1::function;
struct Foo {};
struct Bar
{
typedef shared_ptr< Foo > Ptr;
typedef list< Ptr > List;
typedef function< Foo* (Ptr) > Functor;
typedef transform_iterator< Functor, List::iterator > Iterator;
Iterator begin()
{
return make_transform_iterator( fooptrs.begin(), mem_fn( &Ptr::get ) );
}
Iterator end()
{
return make_transform_iterator( fooptrs.end(), mem_fn( &Ptr::get ) );
}
List fooptrs;
};
C++11 would make it easy to eliminate the function wrapper but I don't have a compiler handy to test it out. You could also hide the concrete type of Iterator using type-erasure if you saw the need (I think Adobe offers a free any_iterator class template for this purpose.)
I sometimes see people reaching for STL containers of boost::shared_ptr when actually the less obvious and relatively little known boost::ptr_container might be a better choice.
This may or may not be one of those cases, but consider that one of the nice properties of the ptr_container classes is that their iterators have an "extra" indirection which helps keep things clean and safe.
🧟♀️ ... zombie thread ... 🧟
If you can't use Boost (e.g., it's part of your public interface and you don't want to make that a requirement for your users), you can roll your own relatively easily. For a container my_container -- which could be a front for a standard, boost, whatever container but whose actual implementation is hidden in your implementation files and not exposed to your API -- holding any smart pointer to a class my_type, it would be something like:
class iterator
{
public:
~iterator();
iterator( const iterator& other );
iterator& operator=( const iterator& other );
// The standard iterator traits (names must be lower-case)
typedef std::forward_iterator_tag iterator_category;
typedef my_type* value_type;
typedef std::ptrdiff_t difference_type;
typedef value_type* pointer;
typedef value_type reference;
iterator& operator++();
iterator& operator++( int );
reference operator*() const;
friend bool operator==( const iterator& it1, const iterator& it2 );
friend bool operator!=( const iterator& it1, const iterator& it2 );
private:
// Private, type-erased construction
friend class my_container;
explicit iterator( void* );
// Implementation hidden by pimpl
struct impl;
impl* _impl;
};
And in the cpp file, assuming we're using a vector of shared_ptr under the hood:
// Define the Pimpl struct
struct iterator::impl
{
typedef std::vector< std::shared_ptr<my_type> >::iterator iterator;
iterator iter;
};
// Use void* as type erasure to hide the actual types from the user
iterator::iterator( void* wrappedIter )
: _impl( new impl( *reinterpret_cast<impl::iterator*>( wrappedIter ) ) )
{
}
// Copying
iterator::iterator( const iterator& other )
: _impl( new impl( *other._impl ) )
{}
iterator& iterator::operator=( const iterator& other )
{
_impl->iter = other._impl->iter;
return *this;
}
// ... could implement moving too ...
iterator::~iterator()
{
// Destroy the pimpl
delete _impl;
}
iterator& iterator::operator++()
{
++_impl->iter;
return *this;
}
iterator& iterator::operator++( int )
{
++_impl->iter;
return *this;
}
iterator::reference iterator::operator*() const
{
// This is where the magic happens: We convert the shared_ptr to a raw pointer.
return _impl->iter->second.get();
}
bool operator==( const iterator& it1, const iterator& it2 )
{
return *it1 == *it2;
}
bool operator!=( const iterator& it1, const iterator& it2 )
{
return !( it1 == it2 );
}
Then all you need is a way to create these. To that end my_container's begin()/end() functions would look like:
my_container::iterator my_container::begin()
{
// The & allows our type erasure by invoking our void* constructor
auto iter = _data->vector.begin();
return iterator( &iter );
}
my_container::iterator my_container::end()
{
// The & allows our type erasure by invoking our void* constructor
auto iter = _data->vector.end();
return iterator( &iter );
}
That type erasure and taking the address of a member variable is a little dicey looking, but it's okay since we're immediately reinterpreting the void* and dereferencing and copying it.
Related
I am trying to learn C++ and would like to understand iterators better. To that end, I tried to write an STL compatible iterator for a custom class (called StrBlob which holds strings, defined in the wonderful C++ Primer). Eventually, the following iterator definition worked:
class StrBlob::iterator
: public std::iterator<std::input_iterator_tag, // iterator_category
std::string, // value_type
std::string, // difference_type
const std::string*, // pointer
std::string // reference
> {
public:
iterator() : curr(0) {}
iterator(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
iterator& operator++(); // prefix operators
iterator& operator--();
iterator operator++(int); // postfix operators
iterator operator--(int);
std::string& operator*() const;
std::string* operator->() const;
bool operator==(const iterator& b) const { return curr == b.curr; }
bool operator!=(const iterator& b) const { return curr != b.curr; }
private:
std::shared_ptr<std::vector<std::string>> check(std::size_t,
const std::string&) const;
std::weak_ptr<std::vector<std::string>> wptr;
std::size_t curr;
};
Initially I tried for a long time in vain to design the iterator with the right type definitions myself. This didn't work, so I simply provided an inheritance to std::iterator as mentioned in cppreference.
Can I write the class without inheritance at all? If so, how would I have to rewrite the code above?
(I tried to understand Stackoverflow answers to related questions about iterator design but they were a bit to abstract for me.)
Your code is bugged, std::string is not a suitable difference type (difference_type is the difference between two iterators so normally its std::ptrdiff_t), nor is std::string a suitable reference type (should be std::string&).
Here's the correct typedefs, you can use these instead of inheritance.
typedef std::input_iterator_tag iterator_category;
typedef std::string value_type;
typedef std::ptrdiff_t difference_type;
typedef std::string* pointer;
typedef std::string& reference;
Can I write the class without inheritance at all? If so, how would I have to rewrite the code above?
Yes, std::iterator (deprecated in C++17), only provides typedef.
So you can add those typedef yourself instead of using inheritance.
class StrBlob::iterator
{
public:
using iterator_category = std::input_iterator_tag;
using value_type = std::string;
using difference_type = std::string;// should be `std::ptrdiff_t`
using pointer = const std::string*; // const should probably be dropped, unless you do const_iterator
using reference = std::string; // should be std::string&
// ...
};
I want to know how to have a c++ class to be iterable (stl compatible) without exposing the implementation ?
The structure of the project is like :
Stream
class Stream
{
public:
Stream();
[...]
StreamIterator iter()
{
return StreamIterator(this);
}
private:
class impl;
std::unique_ptr<impl> pimpl;
};
StreamFilter
class StreamFilter
{
public:
StreamFilter();
[...]
private:
class impl;
std::shared_ptr<impl> pimpl;
};
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
StreamFilter is a base class for differents filtering strategies.
For simplicity in the sample code, I used raw memory pointers, of course in a real exploitation code I use intelligents pointers : shared, weak ...
I want to allow the StreamIterator become iterable in a STL way, doing :
StreamIterator iter = stream.iter();
iter.addFilter(new FilterByOffset([...with parameters...]));
for (auto item : iter)
{
[...doing something with filtered items...]
}
The basic way is to add some accessors to allow range-based for loop.
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
iterator begin();
iterator end();
const_iterator cbegin() const;
const_iterator cend() const;
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
Where iterator and const_iterator are basically typedef's to the internal container iterators. And this is the problem.
First, I don't want to expose private implementation in the StreamIterator header. And so, iterator and const_iterator are not allowed here.
Second, because of the stream filtering strategy, the iterators returned are not just alias to some internal stl containers. In the internal implementation, I need to call the filters in a functor way to check if the item need to be exclude or not.
The only type allowed in the StreamIterator header is the type of the item object returned.
Is there a way to do that?
Thank you very much!
Additional Information:
Maybe this declaration is a way to allow a private implementation, I need to investigate more :
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
struct iterator
{
Object::Ptr operator*();
iterator& operator++();
bool operator!= (const iterator& it) const;
};
typedef typename StreamIterator::iterator iterator;
iterator begin();
iterator end();
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
First, don't call it StreamIterator; an iterator is a pointer-like object for which 2 of them can specify a range. Your StreamIterator doesn't have this. Nothing good can come of reusing the well defined iterator term here.
Now, your StreamIterator is some kind of range of iterators. So we'll call it a StreamRange.
In order to hide how StreamRange can be iterated over, you have to hide how the iterators it uses work.
And this -- hiding the implementation detalis of a stream iterator -- has a substantial cost to it.
When iterating over a loop, each step in the loop involves ++ and * and an ==. Throwing a pointer indirection and a vtable lookup (or equivalent) on each of those will make your loops much slower.
But here is how to do it.
template<class Value>
struct any_input_iterator {
using difference_type = std::ptrdiff_t;
using value_type=Value;
using pointer = value_type*;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
private:
struct vtable_t {
bool(*equal)( std::any const& lhs, std::any const& rhs ) = 0;
Value(*get)( std::any& ) = 0;
void(*inc)( std::any& ) = 0;
};
vtable_t const* vtable = 0;
std::any state;
template<class U>
static vtable_t make_vtable() {
return {
[](std::any const& lhs, std::any const& rhs)->bool {
return std::any_cast<U const&>(lhs) == std::any_cast<U const&>(rhs);
},
[](std::any& src)->Value {
return *std::any_cast<U&>(src);
},
[](std::any& src) {
++std::any_cast<U&>(src);
}
};
}
template<class U>
static vtable_t const* get_vtable() {
static const auto vtable = make_vtable<U>();
return &vtable;
}
public:
template<class U,
std::enable_if_t<!std::is_same<std::decay_t<U>, any_input_iterator>{}, bool> = true
>
any_input_iterator(U&& u):
vtable(get_vtable<std::decay_t<U>>()),
state(std::forward<U>(u))
{}
any_input_iterator& operator++() { vtable->inc(state); return *this; }
any_input_iterator operator++(int) { auto tmp = *this; ++*this; return tmp; }
reference operator*() { return vtable->get(state); }
friend bool operator==( any_input_iterator const& lhs, any_input_iterator const& rhs ) {
if (lhs.vtable != rhs.vtable) return false;
if (!lhs.vtable) return true;
return lhs.vtable->equal( lhs.state, rhs.state );
}
friend bool operator!=( any_input_iterator const& lhs, any_input_iterator const& rhs ) {
return !(lhs==rhs);
}
struct fake_ptr {
Value t;
Value* operator->()&&{ return std::addressof(t); }
};
fake_ptr operator->()const { return {**this}; }
};
there are probably some typoes, but this is basic type erasure. Boost does a better job at this.
I only supported input iterators. If you want to support forward iterators, you have to change up some typedefs and return references and the like.
any_input_iterator<int> begin();
any_input_iterator<int> end();
that is, however, enough to let someone iterate over your range in question.
It will be slow, but it will work.
I have a legacy C API to a container-like object (specifically, the Python C API to tuples) which I would like to wrap in a nice C++14 API, so that I can use an iterator. How should I go about implementing this?
Here's some more details. We have the following existing C API which cannot be changed:
Py_ssize_t PyTuple_GET_SIZE(PyObject *p);
PyObject* PyTuple_GET_ITEM(PyObject *p, Py_ssize_t pos);
void PyTuple_SET_ITEM(PyObject *p, Py_ssize_t pos, PyObject *o)
We want to create a class which allows you to get access to a read/write iterator on the elements in the tuple.
The forward read-only iterator is not too difficult to define. Here is what I have:
class PyTuple {
private:
PyObject* tuple;
public:
PyTuple(PyObject* tuple) : tuple(tuple) {}
class iterator {
// iterator traits
PyObject* tuple;
Py_ssize_t index;
public:
iterator(PyObject *tuple, Py_ssize_t index) : tuple(tuple), index(index) {}
iterator& operator++() { index++; return *this; }
iterator operator++(int) { auto r = *this; ++(*this); return r; }
bool operator==(iterator other) const { return tuple == other.tuple && index == other.index; }
bool operator!=(iterator other) const { return !(*this == other); }
PyObject* operator*() { return PyTuple_GET_ITEM(tuple, index); }
// iterator traits
using difference_type = Py_ssize_t;
using value_type = PyObject*;
using pointer = PyObject**;
using reference = PyObject*&;
using iterator_category = std::forward_iterator_tag;
};
iterator begin() {
return iterator(tuple, 0);
}
iterator end() {
return iterator(tuple, PyTuple_GET_SIZE(tuple));
}
}
However, I am not too sure how to support writes. I have to somehow make *it = pyobj_ptr work. Conventionally, this would be done by changing the type to PyObject*& operator*() (so that it gives an lvalue) but I can't do this because the tuple "write" needs to go through PyTuple_SET_ITEM. I have heard that you can use operator= to solve this case but I am not sure if I should use a universal reference (Why no emplacement iterators in C++11 or C++14?) or a proxy class (What is Proxy Class in C++), and am not exactly sure what the code should look like exactly.
What you're looking for is basically a proxy reference. You don't want to dereference into PyObject*, you want to dereference into something that itself can give you a PyObject*. This is similar to how the iterators for types like vector<bool> behave.
Basically, you want operator*() to give you something like:
class PyObjectProxy {
public:
// constructors, etc.
// read access
operator PyObject*() const { return PyTuple_GET_ITEM(tuple, index); }
// write access
void operator=(PyObject* o) {
PyTuple_SET_ITEM(tuple, index, o); // I'm guessing here
}
private:
PyObject* tuple;
Py_ssize_t index;
};
I've never worked with iterators before, and I'm having trouble designing a custom iterator for a custom container class that I wrote.
Background: Using Google Tests API, these are the two test cases that I have:
TEST(RandomArray, End) {
RandomArray r(17);
int *b = r.begin();
int *e = r.end();
EXPECT_EQ(b + 17, e);
}
TEST(RandomArray, IteratorTypedef) {
RandomArray r(7);
for (RandomArray::iterator it = r.begin(); it != r.end(); ++it) {
*it = 89;
EXPECT_EQ(89, *it);
}
}
Here's my header file and the code for the iterators:
class RandomArray
{
friend ostream& operator<<(ostream&, const RandomArray&);
public:
class iterator
{
public:
typedef iterator self_type;
typedef int* pointer;
typedef int& reference;
self_type operator++() { self_type i = *this; ptr++; return i;}
reference operator*() {return *ptr;}
bool operator!=(const self_type& rhs) {return ptr != rhs.ptr;}
private:
pointer ptr;
};
class const_iterator
{
public:
typedef const_iterator self_type;
typedef int* pointer;
typedef int& reference;
self_type operator++() { self_type i = *this; ptr++; return i;}
const reference operator*() { return *ptr; }
bool operator!=(const self_type& rhs) {return ptr != rhs.ptr;}
private:
pointer ptr;
};
RandomArray();
RandomArray(size_t);
size_t size() const;
int* begin();
iterator begin();
const int* begin() const;
const iterator begin() const;
int* end();
iterator end();
const int* end() const;
const iterator end() const;
private:
size_t capacity;
int* data;
};
The error I'm getting on begin and end is the following: Error: Cannot overload functions distinguished by return type alone.
I know you're not allowed to have the same function name and same parameters with different return types, so I'm wondering if there's a better way to do this? Am I making the iterator right? Would a template help fix this? I need begin() and end() to return both an int* and an iterator so I can pass both test cases. Is there a better way to accomplish this?
I need begin() and end() to return both an int* and an iterator so I can pass both test cases.
No, you don't. The test case expecting a pointer is wrong. Containers give you back iterators. In your case, your iterator could be a pointer, but that's an implementation detail. You definitely just want:
iterator begin();
const_iterator begin() const; // NB: const_iterator, not const iterator
And then fix your unit test to expect RandomArray::iterator instead of int*. Or, even better, auto.
Note: Your operator++() does postfix increment instead of prefix increment. Also const reference is the wrong type, that woudl be int& const, and references are inherently const. You want to change your typedef for reference to itself be int const&.
If you can't change the test case with int *b = r.begin(); (though if this is a new class, why not?) then you need to either make your iterator type be the pointer type, or be convertible to the pointer type.
In the first case, get rid of the iterator classes and write using iterator = int*; using const_iterator = int const*;.
In the second case, add conversion functions operator pointer() const { return ptr; }.
The second case is better, as this allows you to deprecate the conversion function in future. However it would be even better to fix the test case to use the iterator type.
I figured out what I was doing wrong. All I had to do was declare a typedef iterator as an int*.
Updated Code:
class MyClass
{
public:
int* begin();
const int* begin() const;
int* end();
const int* end() const;
//HERE
typedef int* iterator;
typedef const int* const_iterator;
private:
size_t capacity;
int* data;
};
Then, in the function body I changed it to this:
int* MyClass::begin()
{
return iterator(&data[0]);
}
const int* MyClass::begin() const
{
return const_iterator(&data[0]);
}
int* MyClass::end()
{
return iterator(&data[capacity]);
}
const int* MyClass::end() const
{
return const_iterator(&data[capacity]);
}
I've created a DynamicArray class and implemented an iterator for it. One of my .cpp files takes a DynamicArray of n length filled with random values, and is supposed to sort it with STL sort. However, I've tinkered for a few hours and have always gotten this error whenever trying to run sort with begin() and end() as the parameters:
no type named 'value_type' in 'struct std::iterator_traits<DynamicArray<double>::iterator>'
I'll give my DynamicArray prototypes for reference:
template<class T>
class DynamicArray {
public:
static T dummy;
class iterator { // iterator for begin() and end() functions
private:
T* ptr;
public:
iterator() { ptr = NULL; } // constructor, sets pointer to NULL
iterator( T* p ) { ptr = p; } // parameterized constructor, sets pointer to data in DynamicArray
const T& operator*() const { return *ptr; } // pointer operator, returns pointer
void operator++() { if( ptr ) ptr++; } // pre-increment operator, increments pointer
void operator++( int ) { if( ptr ) ptr++; } // post-increment operator, increments pointer
bool operator!=( const iterator & other ) { return ptr != other.ptr; } // does not equal operator
};
private:
T* data;
bool* inUse;
unsigned int size;
unsigned int capacity;
public:
DynamicArray();
DynamicArray( const DynamicArray<T> & );
virtual ~DynamicArray();
DynamicArray<T> & operator=( const DynamicArray<T> & );
T operator[]( unsigned int index ) const;
T& operator[]( unsigned int index );
unsigned int getSize() const;
unsigned int getCapacity() const;
bool containsKey( unsigned int index ) const;
void deleteKey( unsigned int index );
iterator begin() const { return iterator( data ); }
iterator end() const { return iterator( data + size ); }
vector<unsigned int> keys() const;
void clear();
private:
void copy( const DynamicArray<T> & );
void deleteIt();
void setCapacity( unsigned int newCap = 10 );
};
What does this error mean? How can I fix it? Thanks in advance.
Your iterator does not meet the C++ library requirements for an iterator. It's not as easy as simply naming something an iterator, in order to create an iterator. The requirements for an iterator span over 30 pages, in the C++ standard.
In nearly all cases, the easiest way to implement a custom iterator is to inherit from the std::iterator template. The template takes two required parameters, and three optional parameters, in order to synthesize all the required attributes of the custom iterator.
Briefly looking over your iterator's code, it looks to me like you can almost support the requirements of forward iterators, so try inheriting your iterator class from std::iterator<std::forward_iterator_tag,T>.
The reason I say "almost" is because your operator++ operators are broken. Their return value is wrong, and the post-increment version is also wrong. This may preclude your iterator from working correctly, with some algorithms in the C++ library, until you fix this. But your biggest problem is lack of the implemenation of the required iterator traits.