Adapting foreign iterators to D ranges

Chloé chloekek at use.startmail.com
Mon Apr 22 11:36:43 UTC 2024


Assume a third-party API of the following signature:

     T* next(I iter);

which advances an iterator of sorts and returns the next element, or 
null when iteration is done. No other information about the state of the 
iterator is available.

I wish to adapt this interface to a forward range for use with foreach 
and Phobos' range utilities. This amounts to implementing empty, front, 
and popFront, in terms of next and some state. But there is a choice to 
be made regarding the first call to next.

One could call next during range construction:

     struct Range
     {
         private I iter;
         private T* current;
         this(I iter) { this.iter = iter; current = next(iter); }
         bool empty() const => current is null;
         inout(T)* front() inout => current;
         void popFront() { current = next(iter); }
     }

Or do not call it until the first call to empty:

     struct Range
     {
         private bool initialized;
         private I iter;
         private T* current;
         this(I iter) { this.iter = iter; }
         bool empty()
         {
             if (!initialized) {
                 current = next(iter);
                 initialized = true;
             }
             return current is null;
         }
         inout(T)* front() inout => current;
         void popFront() { current = next(iter); }
     }

The first implementation has the advantage is being simpler and empty 
being const, but has the downside that next is called even if the range 
ends up not being used. Is either approach used consistently across the 
D ecosystem?


More information about the Digitalmars-d-learn mailing list