Chapter 3. Implementing iterators with reference-counted objects

Index

Implementing a reference-counted object-based iterator
Accessing the underlying x::ref and x::ptr
Base class for reference-counted output iterators
Reference-counted output iterator tuples

C++ iterators are frequently copied and moved around, and can benefit from a reference-counted approach, where only a reference count gets updated, instead of having to copy a large object here and there.

#include <x/refiterator.H>
#include <x/ref.H>
#include <x/ptr.H>
#include <x/obj.H>

class customiteratorObj : virtual public x::obj {

// ...

};

typedef x::refiterator<customiteratorObj> customiterator;

auto iter=customiterator::create( /* arguments... */ );
      

x::refiterator defines a subclass of x::ref that implements iterator operator facades which invoke the corresponding methods from the referenced object. The template, like x:ref, takes an optional second parameter that specifies a custom base class.

x::ptriterator, x::const_refiterator, and x::const_ptriterator round out the rest of the customary naming convention for references and pointers to a reference-counted object.

Note

The remainder of this chapter refers to x:refiterator. This description equally applies to x::ptriterator and the others, subject to the inherent differenced between the underlying x::ref and x::ptr semantics.

At this time, the reference-counted iterator template implements supports only input and output iterators.

auto iter=x::make_refiterator(x::ref<dummyOutputIterObj>::create());

The make_refiterator() convenience function takes a x::ref parameter, and constructs an x::refiterator from it.

Implementing a reference-counted object-based iterator

class dummyOutputIterObj : virtual public x::obj {
public:

    typedef std::iterator<std::output_iterator_tag, void, void, void,
                  void> iterator_traits;

    std::string buffer;

public:
    dummyOutputIterObj() {}
    ~dummyOutputIterObj() {}

    void operator++() {}

    x::ref<dummyOutputIterObj> before_postoper()
    {
        return x::ref<dummyOutputIterObj>(this);
    }

    dummyOutputIterObj &operator*()
    {
        return *this;
    }

    void operator=(char c)
    {
        buffer.push_back(c);
    }

    dummyOutputIterObj *operator->()
    {
        return this;
    }
};

// ...

auto iter=x::refiterator<dummyOutputIterObj>::create();

std::string s="foobar";

iter=std::copy(s.begin(), s.end(), iter);

*iter++='r';

Like with x::refs and x::ptrs, create() constructs a reference or a pointer to a new reference-counted object. x::refiterator implements the *(), ++(), and ->() operators that are required from an iterator implementation. They get implemented by invoking the following methods and members of the referenced object:

typedef std::iterator<...> iterator_traits

The object must declare iterator_traits to describe what kind of an iterator it implements. x::refiterator inherits from it.

* and ->

x::refiterator's * and -> methods invoke the corresponding method from the object, and return whatever the invoked method returns. The * operator is required, and -> is optional.

As usual, it's expected that an output iterator's *() returns something that implements =() that adds a new element to the output sequence. The usual approach is to return a native reference to the same object, and have the class implement the = operator. Unlike a regular iterator's operator, here it does not need to return a native reference to its own instance, since it's the x::refiterator's * that actually gets invoked, and it returns a native reference to itself.

For input iterators, the required * and the optional -> operators need to return something that's convertible to the current value referenced by the input iterator.

++()

x::refiterator's ++ method invokes the referenced object's ++ operator, but returns a reference to itself, and the return value from the object's ++ operator gets ignored.

The object's ++ operator's semantics should implement the prefix version of the operator.

before_postoper()

x::refiterator's postfix version of ++ method invokes before_postoper(), and passes through whatever value it returns; but before returning, the object's ++() gets invoked. So, the sequence of events is:

  • x::refiterator's postfix ++ gets invoked.

  • The object's before_postoper() gets invoked.

  • The object's ++() operator gets invoked.

  • The return value from before_postoper() becomes the return value of x::refiterator's postfix ++() operator.

It follows that the return value from before_postoper is expected to be dereferenced by *(), in the context of an input or an output iterator.