Thread overview
Passing a derived class where base class is defined as ref parameter
Dec 13, 2021
chopchop
Dec 13, 2021
Adam D Ruppe
Dec 14, 2021
Tejas
Dec 14, 2021
chopchop
Dec 14, 2021
chopchop
Dec 14, 2021
Stanislav Blinov
Dec 15, 2021
chopchop
Dec 14, 2021
Adam D Ruppe
Dec 15, 2021
chopchop
Dec 14, 2021
Mike Parker
December 13, 2021

Hi guys,

below a small piece of code, which does not compile because "b" of type B can not be passed as ref parameter to incr(ref A a).

If I remove the ref, it works as expected, that is to say I can give a derived class as parameter. I have an idea why it does not work, but I think a c++ reference would work, ie incr(A& console) would accept a B as parameter. What the logic here?

Thanks

void main()
{
    import std.stdio: write, writeln, writef, writefln;

    class A
    {
    	int i = 2;
    }

    class B : A
    {
    	int j= 3;
    }

    void incr(ref A a)
    {
    	writeln(a.i);
    }

    B b = new B();
    incr(b);

}
December 13, 2021
On Monday, 13 December 2021 at 22:06:45 UTC, chopchop wrote:
> If I remove the ref, it works as expected, that is to say I can give a derived class as parameter.

Why are you using the ref to begin with?

> What the logic here?

Consider this:


class C : A {}

void incr(ref A a) {
   a = new C;
}

B b = new B;
incr(b);
// oops b now got rebound to a C instead of to a B, which breaks everything
December 14, 2021

On Monday, 13 December 2021 at 22:30:59 UTC, Adam D Ruppe wrote:

>

On Monday, 13 December 2021 at 22:06:45 UTC, chopchop wrote:

>

If I remove the ref, it works as expected, that is to say I can give a derived class as parameter.

Why are you using the ref to begin with?

>

What the logic here?

Consider this:

class C : A {}

void incr(ref A a) {
a = new C;
}

B b = new B;
incr(b);
// oops b now got rebound to a C instead of to a B, which breaks everything

But B is not a child of A, why should it be accepted in a function that accepts A as a parameter? It's not implicitly convertible to A

December 14, 2021

On Monday, 13 December 2021 at 22:06:45 UTC, chopchop wrote:

>

If I remove the ref, it works as expected, that is to say I can give a derived class as parameter. I have an idea why it does not work, but I think a c++ reference would work, ie incr(A& console) would accept a B as parameter. What the logic here?

TL:DR it's because there are two levels of indirection.

What's happening here is that ref A in D is not equivalent to A& in C++. That's because D classes are reference types like Java classes, not value types like C++ classes. Your b is a handle to an instance, not an instance itself. It's more akin to B* in C++. So that means that your ref A is like A** in C++. And I believe you'll find that B** in C++ is not implicitly convertible to A**.

Since ref in D is just a pointer under the hood, we can be more explicit like so:

void incr(A* a)
{
    writeln(a.i);
}

B b = new B();
incr(&b);

In this case, compilation also fails. B* is not implicitly convertible to A*. Again, this is equivalent to B** and A** in C++.

In this case, you can explicitly do the conversion with a cast: incr(cast(A*)&b);

But consider what happens in this case:

void incr(A* a)
{
    *a = new A;
}

B b = new B();
incr(cast(A*)&b);
writeln(b.j);

Your b is no longer a B, but still thinks it is.

This is my understanding of why implicit conversion is disallowed when multiple levels of indirection are involved.

December 14, 2021

On Tuesday, 14 December 2021 at 05:38:17 UTC, Tejas wrote:

>

On Monday, 13 December 2021 at 22:30:59 UTC, Adam D Ruppe wrote:

>

On Monday, 13 December 2021 at 22:06:45 UTC, chopchop wrote:

>

If I remove the ref, it works as expected, that is to say I can give a derived class as parameter.

Why are you using the ref to begin with?

>

What the logic here?

Consider this:

class C : A {}

void incr(ref A a) {
a = new C;
}

B b = new B;
incr(b);
// oops b now got rebound to a C instead of to a B, which breaks everything

But B is not a child of A, why should it be accepted in a function that accepts A as a parameter? It's not implicitly convertible to A

Tejas, I think you should not read Adam's example as standalone, obviously he is implicitly reusing the definition of B in my first post, so B is indeed a child of A.

December 14, 2021
On Monday, 13 December 2021 at 22:30:59 UTC, Adam D Ruppe wrote:
> On Monday, 13 December 2021 at 22:06:45 UTC, chopchop wrote:
>> If I remove the ref, it works as expected, that is to say I can give a derived class as parameter.
>
> Why are you using the ref to begin with?
>
>> What the logic here?
>
> Consider this:
>
>
> class C : A {}
>
> void incr(ref A a) {
>    a = new C;
> }
>
> B b = new B;
> incr(b);
> // oops b now got rebound to a C instead of to a B, which breaks everything

Hi Adam,

I am using the "ref" here (I put tinyurl to avoid over-referencing the post instead of the github page itself):
https://tinyurl.com/bdddkmub

I would like to be able to pass any kind of console to updateFoodToken ( Console c ), ie either a winconsole or a nixconsole, which are derived from Console.

I mean I probably have a cognitive bias of being a c++ dev. Let me explain. If I was coding in C++ I would pass "A&". Not "const A&", because I do two function calls with console - for example console.gotoxy(...) - but those 2 members functions are not suffixed "const". Since it is work in progress, I dont even know if I am going to modify those functions at the end or do something else with console...


But well, what do you guys think? The documentation does not even say if an "in" param is passed by value or reference ("may be passed by reference" it says). I don't really see how a copy of a console would work... Kind of eery in my minde.


Adam, I am actually using your code :) You and anyone else are welcome to review / send some Pull Request, deal with the console code, comment, etc


December 14, 2021
On Tuesday, 14 December 2021 at 17:20:18 UTC, chopchop wrote:

> I am using the "ref" here (I put tinyurl to avoid over-referencing the post instead of the github page itself):
> https://tinyurl.com/bdddkmub
>
> I would like to be able to pass any kind of console to updateFoodToken ( Console c ), ie either a winconsole or a nixconsole, which are derived from Console.
>
> I mean I probably have a cognitive bias of being a c++ dev. Let me explain. If I was coding in C++ I would pass "A&". Not "const A&", because I do two function calls with console - for example console.gotoxy(...) - but those 2 members functions are not suffixed "const". Since it is work in progress, I dont even know if I am going to modify those functions at the end or do something else with console...

Simple. Don't take a `ref`. Just take a `Console`. Classes in D are reference types, you're not making a copy as you would in C++ if you were to write `updateFoodToken(Console c)`.
December 14, 2021
On Tuesday, 14 December 2021 at 17:20:18 UTC, chopchop wrote:
> I am using the "ref" here (I put tinyurl to avoid over-referencing the post instead of the github page itself):
> https://tinyurl.com/bdddkmub

yeah D classes are automatically ref unlike c++ so you don't need the second level of it most often.

Using plain `Object` is what you want most the time, maybe `in Object` sometimes. But `ref Object` or `Object*` both very rare in D.


> Adam, I am actually using your code :)

nice. You might want to look at my terminal.d (arsd-official:terminal on dub) too which has pretty comprehensive functionality for tons of things. Prolly more than you need looking at your interface tho but it includes the output functions then mouse and real time key inputs, get line input, even scrollable areas and more.
December 15, 2021
On Tuesday, 14 December 2021 at 17:27:13 UTC, Stanislav Blinov wrote:
> Simple. Don't take a `ref`. Just take a `Console`. Classes in D are reference types, you're not making a copy as you would in C++ if you were to write `updateFoodToken(Console c)`.

Ah, ok, Reference types! That's great. Definitely my C++ bias working against me, good you read my mind :)


December 15, 2021
On Tuesday, 14 December 2021 at 20:58:21 UTC, Adam D Ruppe wrote:
> nice. You might want to look at my terminal.d (arsd-official:terminal on dub) too which has pretty comprehensive functionality for tons of things. Prolly more than you need looking at your interface tho but it includes the output functions then mouse and real time key inputs, get line input, even scrollable areas and more.

Always a pleasure to check your great libs, I will have a look and learn probably a lot. But I wont use an external lib because it's a demo where all the code must be under contract / covered, to meet some strict coding standard. So in worst case I copy the code and add unittest / contract / invariant, etc. But original authors are always quoted and licences respected ;)