www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Rewriting a c++ template to D (replacing iterator with ranges

reply aliak <something something.com> writes:
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;
   }
}

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;
   }
}

I have a more specific question too:

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?

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?

Cheers
Jan 26
next sibling parent aliak <something something.com> writes:
On Friday, 26 January 2018 at 14:16:04 UTC, aliak wrote:
   range.front = false;
   while (!range.empty) {
     range.popFrontN(N);
     range.front = false;
   }
 }
Oops, this should be: while (!range.empty) { range.front = false; range.popFrontN(N); }
Jan 26
prev sibling next sibling parent reply Meta <jared771 gmail.com> writes:
On Friday, 26 January 2018 at 14:16:04 UTC, aliak wrote:
 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?
Where did you see this? That's got to be some very old code; I can't think of any instance where you would not want to use `hasAssignableElements` instead.
Jan 26
parent reply aliak <something something.com> writes:
On Friday, 26 January 2018 at 14:35:25 UTC, Meta wrote:
 On Friday, 26 January 2018 at 14:16:04 UTC, aliak wrote:
 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?
Where did you see this? That's got to be some very old code; I can't think of any instance where you would not want to use `hasAssignableElements` instead.
Seems to occur in https://github.com/dlang/phobos/blob/v2.078.1/std/algorithm/mutation.d eg: https://github.com/dlang/phobos/blob/v2.078.1/std/algorithm/mutation.d#L555
Jan 26
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Friday, 26 January 2018 at 15:33:03 UTC, aliak wrote:
 On Friday, 26 January 2018 at 14:35:25 UTC, Meta wrote:
 On Friday, 26 January 2018 at 14:16:04 UTC, aliak wrote:
 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?
Where did you see this? That's got to be some very old code; I can't think of any instance where you would not want to use `hasAssignableElements` instead.
Seems to occur in https://github.com/dlang/phobos/blob/v2.078.1/std/algorithm/mutation.d eg: https://github.com/dlang/phobos/blob/v2.078.1/std/algorithm/mutation.d#L555
The function is called fill, and assigns a value to every element in the range. If a[0] = false compiles, we also want a.fill(false) to compile. It's simply testing that, rather than caring about the exact type of the elements. -- Simen
Jan 26
parent aliak <something something.com> writes:
On Friday, 26 January 2018 at 23:15:41 UTC, Simen Kjærås wrote:
 The function is called fill, and assigns a value to every 
 element in the range. If a[0] = false compiles, we also want 
 a.fill(false) to compile. It's simply testing that, rather than 
 caring about the exact type of the elements.

 --
   Simen
I see. Yes that makes sense. Thank you.
Jan 27
prev sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
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
Jan 26
parent aliak <something something.com> writes:
On Friday, 26 January 2018 at 14:59:09 UTC, Simen Kjærås wrote:
 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.
Oops again :) Should've been typename N (where N is some integral type).
 Not exactly. range.front will assert after the last popFrontN 
 (since the range is empty).
Ya, sorry, realized this a bit after I posted.
 It's trying to combine 3 and 4 I think, but it fails because 
 this is allowed in D:

 int a;
 a = false;
Ah true, so it's more of a is(ElementType!R : bool) check?
 You'll want to pass the range as ref. hasLvalueElements checks 
 if the elements have lvalue semantics
Doh, of course. It's in the name even!
 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
Thanks for the input!
Jan 26