www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - cas and interfaces

reply Johannes Loher <johannes.loher fg4f.de> writes:
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
parent reply Johan Engelen <j j.nl> writes:
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
parent reply Johannes Loher <johannes.loher fg4f.de> writes:
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:
 [...]
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
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?
Dec 25 2018
parent reply Rene Zwanenburg <renezwanenburg gmail.com> writes:
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
parent Johan Engelen <j j.nl> writes:
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:
 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.
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.). -Johan
Dec 27 2018