Thread overview | |||||||
---|---|---|---|---|---|---|---|
|
December 23, 2018 cas and interfaces | ||||
---|---|---|---|---|
| ||||
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? |
December 24, 2018 Re: cas and interfaces | ||||
---|---|---|---|---|
| ||||
Posted in reply to Johannes Loher | 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
|
December 25, 2018 Re: cas and interfaces | ||||
---|---|---|---|---|
| ||||
Posted in reply to Johan Engelen | 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?
|
December 27, 2018 Re: cas and interfaces | ||||
---|---|---|---|---|
| ||||
Posted in reply to Johannes Loher | 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 |
December 27, 2018 Re: cas and interfaces | ||||
---|---|---|---|---|
| ||||
Posted in reply to Rene Zwanenburg | 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 |
Copyright © 1999-2021 by the D Language Foundation