digitalmars.D.learn - Passing a derived class where base class is defined as ref parameter
- chopchop (28/28) Dec 13 2021 Hi guys,
- Adam D Ruppe (11/14) Dec 13 2021 Consider this:
- Tejas (4/18) Dec 13 2021 But `B` is not a child of `A`, why should it be accepted in a
- chopchop (4/29) Dec 14 2021 Tejas, I think you should not read Adam's example as standalone,
- chopchop (22/36) Dec 14 2021 Hi Adam,
- Stanislav Blinov (4/17) Dec 14 2021 Simple. Don't take a `ref`. Just take a `Console`. Classes in D
- chopchop (4/7) Dec 14 2021 Ah, ok, Reference types! That's great. Definitely my C++ bias
- Adam D Ruppe (12/16) Dec 14 2021 yeah D classes are automatically ref unlike c++ so you don't need
- chopchop (7/13) Dec 14 2021 Always a pleasure to check your great libs, I will have a look
- Mike Parker (37/41) Dec 13 2021 TL:DR it's because there are two levels of indirection.
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); } ```
Dec 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
Dec 13 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: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`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
Dec 13 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: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.On Monday, 13 December 2021 at 22:06:45 UTC, chopchop wrote: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`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
Dec 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: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, etcIf 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
Dec 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)`.
Dec 14 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 :)
Dec 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/bdddkmubyeah 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.
Dec 14 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 ;)
Dec 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: ```d 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: ```d 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.
Dec 13 2021