www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Gneric linkedList range adaptor

reply Ben Jones <fake fake.fake> writes:
I'm trying to write a range adaptor for linked list types.  The 
range type seems to work OK, but my helper function to deduce the 
node type has a compiler error.  My hunch is that `nextField` 
loses its association with T when I'm trying to pass it as a 
template parameter inside the helper method, but I can't use 
__traits(child) there to fix it.

Any idea how to fix the helper method?

Is this something that would be useful to add to std.range 
somewhere?  Or is something like it already in Phobos?

Thanks

```
import std.stdio;
import std.range;

struct LinkedListAdaptor(alias nextField, T){
  	T current;
      safe:
    	nothrow:

     this(T head){
      	current = head;
     }

     bool empty() const {
     	return current == null;
     }	

     T front() {
      	return current;
     }

     void popFront() {
		current = __traits(child, current, nextField);
     }
}

auto linkedListAdaptor(alias nextField, T)(T head) if(is(T == U*, 
U)){
  	return LinkedListAdaptor!(nextField, T)(head);

     //fails with:
     //onlineapp.d(27): Error: symbol or expression expected as 
first argument of __traits `child` instead of `Node*`
     // return LinkedListAdaptor!(__traits(child, T, nextField), 
T)(head);
}

struct Node{
  	int data;
     Node* next;
}

void main(){
	Node* head = new Node(10, new Node(20, null));

     auto rng1 = LinkedListAdaptor!(Node.next, Node*)(head);
     auto rng = linkedListAdaptor!(Node.next)(head);
     foreach(const x; rng){
         writeln(x.data);
     }
}
```

This fails with:

Error: need `this` for `linkedListAdaptor` of type `pure nothrow 
 nogc  safe LinkedListAdaptor!(next, Node*)(Node* head)`
Feb 10 2023
next sibling parent reply Ruby The Roobster <rubytheroobster yandex.com> writes:
On Friday, 10 February 2023 at 22:10:20 UTC, Ben Jones wrote:
 I'm trying to write a range adaptor for linked list types.  The 
 range type seems to work OK, but my helper function to deduce 
 the node type has a compiler error.  My hunch is that 
 `nextField` loses its association with T when I'm trying to 
 pass it as a template parameter inside the helper method, but I 
 can't use __traits(child) there to fix it.

 Any idea how to fix the helper method?

 Is this something that would be useful to add to std.range 
 somewhere?  Or is something like it already in Phobos?

 Thanks

 ```
 import std.stdio;
 import std.range;

 struct LinkedListAdaptor(alias nextField, T){
  	T current;
      safe:
    	nothrow:

     this(T head){
      	current = head;
     }

     bool empty() const {
     	return current == null;
     }	

     T front() {
      	return current;
     }

     void popFront() {
 		current = __traits(child, current, nextField);
     }
 }

 auto linkedListAdaptor(alias nextField, T)(T head) if(is(T == 
 U*, U)){
  	return LinkedListAdaptor!(nextField, T)(head);

     //fails with:
     //onlineapp.d(27): Error: symbol or expression expected as 
 first argument of __traits `child` instead of `Node*`
     // return LinkedListAdaptor!(__traits(child, T, nextField), 
 T)(head);
 }

 struct Node{
  	int data;
     Node* next;
 }

 void main(){
 	Node* head = new Node(10, new Node(20, null));

     auto rng1 = LinkedListAdaptor!(Node.next, Node*)(head);
     auto rng = linkedListAdaptor!(Node.next)(head);
     foreach(const x; rng){
         writeln(x.data);
     }
 }
 ```

 This fails with:

 Error: need `this` for `linkedListAdaptor` of type `pure 
 nothrow  nogc  safe LinkedListAdaptor!(next, Node*)(Node* head)`
The problem is that Node.next is not, and cannot be `static`. Thus, there is no way for you to pass it as an alias template parameter, and this should be considered a compiler bug that this doesn't error out.
Feb 10 2023
parent Ruby The Roobster <rubytheroobster yandex.com> writes:
On Friday, 10 February 2023 at 23:39:11 UTC, Ruby The Roobster 
wrote:

 The problem is that Node.next is not, and cannot be `static`.  
 Thus, there is no way for you to pass it as an alias template 
 parameter, and this should be considered a compiler bug that 
 this doesn't error out.
Nevermind, I'm an absolute idiot. The first works, it uses Node.next in the context of this.current. The second should also work, T is of type Node*. This seems to be a bug with the compiler, it automatically infers function attributes by default before evaluating whether the function actually works with said inferred attributes. Your __traits(child) solution doesn't work, because T is a type, not a variable.
Feb 10 2023
prev sibling next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 2/10/23 14:10, Ben Jones wrote:

 Any idea how to fix the helper method?
I came up with the following solution:
 struct LinkedListAdaptor(alias nextField, T){
In this case nextField will be a callable (a lambda below).
      void popFront() {
          current = __traits(child, current, nextField);
I changed that to current = nextField(current);
      auto rng1 = LinkedListAdaptor!(Node.next, Node*)(head);
Commented that out.
      auto rng = linkedListAdaptor!(Node.next)(head);
Replaced that with auto rng = linkedListAdaptor!(node => node.next)(head); Now it works but explicitly creating a lambda feels less than ideal. Perhaps it can be improved. Ali
Feb 10 2023
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/10/23 5:10 PM, Ben Jones wrote:
 I'm trying to write a range adaptor for linked list types.  The range 
 type seems to work OK, but my helper function to deduce the node type 
 has a compiler error.  My hunch is that `nextField` loses its 
 association with T when I'm trying to pass it as a template parameter 
 inside the helper method, but I can't use __traits(child) there to fix it.
 
This seems like a bug in the compiler. I did try simplifying, whereas, instead of `T` being a pointer, I just used `T*` everywhere, and things got a lot simpler. But it still fails in the same way. The fact that explicitly specifying the `T` instead of using IFTI works suggests for sure that the compiler should accept it. -Steve
Feb 10 2023
parent Ben Jones <fake fake.fake> writes:
On Saturday, 11 February 2023 at 05:02:49 UTC, Steven 
Schveighoffer wrote:

Reported

https://issues.dlang.org/show_bug.cgi?id=23687


Assuming this is a compiler bug and it gets fixed, does this seem 
like something worth trying to add to std.range?
Feb 12 2023
prev sibling parent Paul Backus <snarwin gmail.com> writes:
On Friday, 10 February 2023 at 22:10:20 UTC, Ben Jones wrote:
 I'm trying to write a range adaptor for linked list types.  The 
 range type seems to work OK, but my helper function to deduce 
 the node type has a compiler error.  My hunch is that 
 `nextField` loses its association with T when I'm trying to 
 pass it as a template parameter inside the helper method, but I 
 can't use __traits(child) there to fix it.

 Any idea how to fix the helper method?
As a workaround, you can use a `string` instead of an `alias`: ```diff --- before.d 2023-02-11 01:48:08.922945736 -0500 +++ after.d 2023-02-11 01:47:42.062922019 -0500 -1,7 +1,7 import std.stdio; import std.range; -struct LinkedListAdaptor(alias nextField, T){ +struct LinkedListAdaptor(string nextField, T){ T current; safe: nothrow: -19,11 +19,11 } void popFront() { - current = __traits(child, current, nextField); + current = __traits(getMember, current, nextField); } } -auto linkedListAdaptor(alias nextField, T)(T head) if(is(T == U*, U)){ +auto linkedListAdaptor(string nextField, T)(T head) if(is(T == U*, U)){ return LinkedListAdaptor!(nextField, T)(head); //fails with: -39,8 +39,8 void main(){ Node* head = new Node(10, new Node(20, null)); - auto rng1 = LinkedListAdaptor!(Node.next, Node*)(head); - auto rng = linkedListAdaptor!(Node.next)(head); + auto rng1 = LinkedListAdaptor!("next", Node*)(head); + auto rng = linkedListAdaptor!("next")(head); foreach(const x; rng){ writeln(x.data); } ```
Feb 10 2023