digitalmars.D.learn - Gneric linkedList range adaptor
- Ben Jones (55/55) Feb 10 2023 I'm trying to write a range adaptor for linked list types. The
- Ruby The Roobster (5/60) Feb 10 2023 The problem is that Node.next is not, and cannot be `static`.
- Ruby The Roobster (10/14) Feb 10 2023 Nevermind, I'm an absolute idiot. The first works, it uses
- =?UTF-8?Q?Ali_=c3=87ehreli?= (11/17) Feb 10 2023 In this case nextField will be a callable (a lambda below).
- Steven Schveighoffer (8/14) Feb 10 2023 This seems like a bug in the compiler.
- Ben Jones (6/6) Feb 12 2023 On Saturday, 11 February 2023 at 05:02:49 UTC, Steven
- Paul Backus (37/44) Feb 10 2023 As a workaround, you can use a `string` instead of an `alias`:
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
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
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
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
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
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
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