digitalmars.D.learn - cas and interfaces
- Johannes Loher (36/36) Dec 23 2018 I recently played around with atomic operations. While doing so,
- Johan Engelen (20/41) Dec 24 2018 The types of the 2nd and 3rd arguments of `cas` do not have to be
- Johannes Loher (5/27) Dec 25 2018 Thanks a lot for the info, that clarifies things a bit. But it
- Rene Zwanenburg (18/25) Dec 27 2018 Unlike interfaces, base class references don't need adjustment.
- Johan Engelen (8/15) Dec 27 2018 Yeah. You shouldn't need to know these details but if you are
I recently played around with atomic operations. While doing so, I noticed a problem with the interaction of interfaces and cas. Consider the following program: ``` import core.atomic; import std.stdio; interface TestInterface { } class TestClass : TestInterface { } void main() { shared TestInterface testInterface = new shared TestClass; cas(&testInterface, testInterface, new shared TestClass).writeln; writeln(typeid(testInterface)); } ``` (https://run.dlang.io/is/9P7PAb) This program compiles successfully and outputs ``` true Error: program killed by signal 11 ``` i.e. a segmentation fault happens. When replacing "interface" with "abstract class" (https://run.dlang.io/is/sFaO1k), everything works as expected and the program outputs ``` true onlineapp.TestClass ``` Is this actually a bug or a fundamental limitation of cas and interfaces? Did I do anything wrong?
Dec 23 2018
On Sunday, 23 December 2018 at 14:07:04 UTC, Johannes Loher wrote:I recently played around with atomic operations. While doing so, I noticed a problem with the interaction of interfaces and cas. Consider the following program: ``` import core.atomic; import std.stdio; interface TestInterface { } class TestClass : TestInterface { } void main() { shared TestInterface testInterface = new shared TestClass; cas(&testInterface, testInterface, new shared TestClass).writeln; writeln(typeid(testInterface)); } ``` (https://run.dlang.io/is/9P7PAb)The types of the 2nd and 3rd arguments of `cas` do not have to be the same, and aren't in your case. I think what's happening is that you are overwriting `testInterface` with a pointer to a TestClass which is not a valid TestInterface pointer. And then the program does something invalid because, well, you enter UB land. Fixed by: ``` cas(&testInterface, testInterface, cast(shared(TestInterface)) new shared TestClass).writeln; ``` Note the cast! Whether this is a bug in `cas` or not, I don't know. The `cas` template checks whether the 3rd can be assigned to the 1st argument (`*here = writeThis;`) which indeed compiles _with_ an automatic conversion. But then the implementation of `cas` does not do any automatic conversions (casts to `void*`). Hence the problem you are seeing. -Johan
Dec 24 2018
On Monday, 24 December 2018 at 11:23:32 UTC, Johan Engelen wrote:On Sunday, 23 December 2018 at 14:07:04 UTC, Johannes Loher wrote:Thanks a lot for the info, that clarifies things a bit. But it still leaves the question, why it works correctly when inheriting from an abstract class instead of implementing an interface... Any idea about why that?[...]The types of the 2nd and 3rd arguments of `cas` do not have to be the same, and aren't in your case. I think what's happening is that you are overwriting `testInterface` with a pointer to a TestClass which is not a valid TestInterface pointer. And then the program does something invalid because, well, you enter UB land. Fixed by: ``` cas(&testInterface, testInterface, cast(shared(TestInterface)) new shared TestClass).writeln; ``` Note the cast! Whether this is a bug in `cas` or not, I don't know. The `cas` template checks whether the 3rd can be assigned to the 1st argument (`*here = writeThis;`) which indeed compiles _with_ an automatic conversion. But then the implementation of `cas` does not do any automatic conversions (casts to `void*`). Hence the problem you are seeing. -Johan
Dec 25 2018
On Tuesday, 25 December 2018 at 22:07:07 UTC, Johannes Loher wrote:Thanks a lot for the info, that clarifies things a bit. But it still leaves the question, why it works correctly when inheriting from an abstract class instead of implementing an interface... Any idea about why that?Unlike interfaces, base class references don't need adjustment. You can see this in action by printing addresses: https://run.dlang.io/is/m6icVr import std.stdio; interface I {} class Base : I {} class Derived : Base { } void main() { auto derived = new Derived; Base base = derived; I i = derived; writeln(cast(void*)derived); writeln(cast(void*)base); writeln(cast(void*)i); }7FC1F96EE000 7FC1F96EE000 7FC1F96EE010
Dec 27 2018
On Thursday, 27 December 2018 at 12:07:48 UTC, Rene Zwanenburg wrote:On Tuesday, 25 December 2018 at 22:07:07 UTC, Johannes Loher wrote:Yeah. You shouldn't need to know these details but if you are interested, the details are here: https://dlang.org/spec/abi.html#classes (it's meant for tech reference, not for explanation. If you need more explanation, go search for vtables, multiple inheritance, etc.). -JohanThanks a lot for the info, that clarifies things a bit. But it still leaves the question, why it works correctly when inheriting from an abstract class instead of implementing an interface... Any idea about why that?Unlike interfaces, base class references don't need adjustment.
Dec 27 2018