Rewriting a c++ template to D (replacing iterator with ranges "properly")

Simen Kjærås simen.kjaras at gmail.com
Fri Jan 26 14:59:09 UTC 2018


On Friday, 26 January 2018 at 14:16:04 UTC, aliak wrote:
> It basically steps through in a stride and sets the checkpoints 
> to false.
>
> C++:
> template <typename /*RandomAccessIterator*/ It, int /*Integer*/ 
> N>
> void mark(It begin, It end, N step) {
>   assert(begin != end)
>   *begin = false;
>   while (end - begin > step) {
>     begin = begin + step;
>     *begin = false;
>   }
> }

what is N here? You're declaring it to be an int value in the 
template<> definition, and then use it as a type in the function 
definition.



> For D this is what I figured would be the way?
>
> void mark(R, N)(auto ref R range, N step)
> if (
>   /* 1 */ isIntegral!N
>   /* 2 */ && isRandomAccessRange!R
>   /* 3 */ && is(ElementType!R == bool)
>   /* 4 */ && hasAssignableElements!R
> ) {
>   range.front = false;
>   while (!range.empty) {
>     range.popFrontN(N);
>     range.front = false;
>   }
> }

Not exactly. range.front will assert after the last popFrontN 
(since the range is empty).

You'll need something like this:

void main(R, N)(R range, N step)
if (isIntegral!N &&
     isRandomAccessRange!R &&
     is(ElementType!R == bool) &&
     hasAssignableElements!R)
{
     if (range.empty) return;
     do
     {
         range.front = false;
         range.popFrontN(N);
     } while (!range.empty);
}


> 1) I've seen some phobos code checking for assignability like 
> this:
>
>   is(typeof(range.front = false))
>
> ... is that an advantage of that over hasAssignableElements? Or 
> is that just basically combining constraints 3 and 4 which I 
> have above?

It's trying to combine 3 and 4 I think, but it fails because this 
is allowed in D:

int a;
a = false;


> 2) Say I wanted to restrict to only lvalue ranges passed in as 
> inputs. Does that mean I use hasLvalueElements as a constraint 
> or is remove the "auto" and just have a ref parameter 
> sufficient?

You'll want to pass the range as ref. hasLvalueElements checks if 
the elements have lvalue semantics, not if the range itself does. 
Here's a range that has lvalue elements, for which passing it as 
ref or non-ref makes a huge difference:

import std.range;

struct R {
     int[3] elements;
     int index;

     ref auto front() {
         return elements[index];
     }

     void popFront() {
         ++index;
     }

     bool empty() {
         return index >= elements.length;
     }
}

unittest {
     assert(hasLvalueElements!(R));
     auto a = R([1,2,3], 0);
     auto b = a;
     b.front = 4;
     assert(a.elements == [1,2,3]);
     assert(b.elements == [4,2,3]);
}

As we can see, b is a complete copy of a, and changing b does not 
change a. The exact same behavior would occur if a was passed by 
value to a function.

--
   Simen


More information about the Digitalmars-d-learn mailing list