Thread overview
Gneric linkedList range adaptor
Feb 10, 2023
Ben Jones
Feb 10, 2023
Ruby The Roobster
Feb 10, 2023
Ruby The Roobster
Feb 10, 2023
Ali Çehreli
Feb 12, 2023
Ben Jones
Feb 11, 2023
Paul Backus
February 10, 2023

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)

February 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.

February 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

February 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.

February 11, 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

February 11, 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:

--- 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);
     }
February 12, 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?