Thread overview
Why does this code only work with `std.conv.to` whilst not with `cast`?
Jan 08, 2023
thebluepandabear
Jan 08, 2023
ag0aep6g
Jan 08, 2023
matheus
Jan 08, 2023
thebluepandabear
Jan 08, 2023
matheus
January 08, 2023

I've been writing some code and I have been experimenting with casting. I've found that sometimes only std.conv.to! does the job over casting, and I am unsure why this is the case as I assumed that they are both the same.

I have the following interface:

interface ICustomDrawable {
    void render(sfRenderWindow* renderWindow);
}

And the following class that implements that interface:

class Button : ICustomDrawable {
    ...
    override void render(sfRenderWindow* renderWindow) {
        ...
    }
}

And this class which uses ICustomDrawable:

class StackLayout : ICustomDrawable {
   ...
    void addChild(T)(T child) {
        static assert(is(T : ICustomDrawable), "Invalid type T for child");
        ...
    }
    ...
    ICustomDrawable[] _children;
}

For some reason, when I want to cast the children (property), only the second example works:

foreach (Button button; cast(Button[])(_boardSizeRow.children)) {
    button.update(event, _renderWindow);
}
foreach (Button button; to!(Button[])(_boardSizeRow.children)) {
    button.update(event, _renderWindow);
}

When I run the first code example I get a black screen, whilst with the second I do not. I find this very strange as when I write to the console the 'type', it is the same for both scenarios.

Help would be appreciated as to why this is happening so I can better understand how D works.

January 08, 2023
On 08.01.23 12:29, thebluepandabear wrote:
> ```D
> interface ICustomDrawable {
>      void render(sfRenderWindow* renderWindow);
> }
[...]> class Button : ICustomDrawable {
[...]
> }
[...]
> class StackLayout : ICustomDrawable {
[...]
>      ICustomDrawable[] _children;
> }
> ```
> 
> For some reason, when I want to cast the `children` (property), only the second example works:
> 
> 1.
> 
> ```D
> foreach (Button button; cast(Button[])(_boardSizeRow.children)) {
>      button.update(event, _renderWindow);
> }
> ```
> 
> 2.
> 
> ```D
> foreach (Button button; to!(Button[])(_boardSizeRow.children)) {
>      button.update(event, _renderWindow);
> }
> ```

Two things you need to know:

1. While an interface reference implicitly converts to a compatible class reference, the resulting pointer is slightly different.

----
interface I {}
class C : I {}
void main()
{
    C c = new C;
    I i = c;
    import std.stdio;
    writeln(cast(void*) c); /* e.g. 7F3C140A7000 */
    writeln(cast(void*) i); /* e.g. 7F3C140A7010 */
}
----

2. Casting from one array type to another is done "as a type paint"[1]. The elements of the array are reinterpreted, not converted.

But reinterpreting interface references as class references won't work, because the pointers will be wrong. You need to convert each element of the array (which is what `to` does).


[1] https://dlang.org/spec/expression.html#cast_array
January 08, 2023
On Sunday, 8 January 2023 at 11:29:10 UTC, thebluepandabear wrote:
> ...

There is an explanation here: https://forum.dlang.org/post/tqukutfzeaxedunuvkum@forum.dlang.org

But in any case I'd like to point it out that I think you could do that foreach without casting or std.conv by just omitting the type of the variable you're iterating:

import std.stdio, std.conv;

interface IDog {
    void bark();
}

class Dog: IDog{
    string s;
    this(string _s){s = _s;}
    void bark(){writeln(s);}
}

class List{
    IDog[] dogs;
    void add(T)(T d){ dogs ~= d;  }
}

void main(){
    auto d1 = new Dog("meaw!");
    auto d2 = new Dog("wof!");
    auto l = new List();
    l.add(d1);
    l.add(d2);

    foreach(d; l.dogs){ // No type informed for "d"
        d.bark();
    }
}

Prints:

meaw!
wof!

Matheus.
January 08, 2023
On Sunday, 8 January 2023 at 12:35:38 UTC, matheus wrote:
> On Sunday, 8 January 2023 at 11:29:10 UTC, thebluepandabear wrote:
>> ...
>
> There is an explanation here: https://forum.dlang.org/post/tqukutfzeaxedunuvkum@forum.dlang.org
>
> But in any case I'd like to point it out that I think you could do that foreach without casting or std.conv by just omitting the type of the variable you're iterating:
>
> import std.stdio, std.conv;
>
> interface IDog {
>     void bark();
> }
>
> class Dog: IDog{
>     string s;
>     this(string _s){s = _s;}
>     void bark(){writeln(s);}
> }
>
> class List{
>     IDog[] dogs;
>     void add(T)(T d){ dogs ~= d;  }
> }
>
> void main(){
>     auto d1 = new Dog("meaw!");
>     auto d2 = new Dog("wof!");
>     auto l = new List();
>     l.add(d1);
>     l.add(d2);
>
>     foreach(d; l.dogs){ // No type informed for "d"
>         d.bark();
>     }
> }
>
> Prints:
>
> meaw!
> wof!
>
> Matheus.

The `foreach` worked for that case since `bark` is a method of `IDog`. `update` is not so a conversion to `Button[]` is needed.

I will look at your resource, thanks.
January 08, 2023
On Sunday, 8 January 2023 at 12:39:37 UTC, thebluepandabear wrote:
> ...
> The `foreach` worked for that case since `bark` is a method of `IDog`. `update` is not so a conversion to `Button[]` is needed.
>

In that case, you could do a casting like this:

import std.stdio, std.conv;

interface IDog {
    void bark();
}

class Dog: IDog{
    string s;
    this(string _s){s = _s;}
    void bark(){writeln(s);}
    void update(string _s){ s ~= " - " ~ _s; }
}

class List{
    IDog[] dogs;
    void add(T)(T d){ dogs ~= d;  }
}

void main(){
    auto d1 = new Dog("meaw!");
    auto d2 = new Dog("wof!");
    auto l = new List();
    l.add(d1);
    l.add(d2);

    foreach(i,d; l.dogs){ // I'm using 'i' just to show each update.
        (cast(Dog)d).update(to!string(i));
        d.bark();
    }
}

Prints:

meaw! - 0
wof! - 1

Matheus.