what exactly does cast(shared) and cast away shared do?

Simen Kjærås simen.kjaras at gmail.com
Tue Jun 16 06:53:22 UTC 2020


On Tuesday, 16 June 2020 at 06:15:11 UTC, mw wrote:
> I'm trying this program:
>
> -----------------------------------------------------------------------------
> class Foo {
>   int attr;
>
>   this() {
>     writeln("Foo.ctor:", &attr);
>   }
> }
>
> void threadInc(shared Foo foo) {
>   writeln("threadInc:", &foo);
>   writeln("threadInc:", &(foo.attr));
>   Foo f = cast(Foo)foo;
>   writeln("threadInc:", &f);
>   foo.attr = 123;
> }
>
> void main() {
>   Foo foo = new Foo();
>   writeln("main:\t", &foo);
>   writeln("main:\t", &(foo.attr));
>
>   spawn(&threadInc, cast(shared)foo);  // cannot pass foo: 
> Error: static assert:  "Aliases to mutable thread-local data 
> not allowed." Have to cast to shared
>   thread_joinAll();
>
>   writeln("main:", foo.attr);
>   writeln("main:\t", &foo);
>   writeln("main:\t", &(foo.attr));
>
>   shared sf1 = cast(shared)foo;
>   shared sf2 = cast(shared)foo;
>   writeln("sf1:\t", &sf1, "\t", &(sf1.attr));
>   writeln("sf2:\t", &sf2, "\t", &(sf2.attr));
>
>   foo.attr = 456;
>   writeln("main:", foo.attr);
>   writeln("main:", sf1.attr);
>   writeln("main:", sf2.attr);
> }
> -----------------------------------------------------------------------------
>
> here is the result:
>
> -----------------------------------------------------------------------------
> Foo.ctor:7FB83F4B0010              <- &attr is always the same
> main:   7FFFD6D081F0               <- &foo
> main:   7FB83F4B0010
> threadInc:7FB83E1EFC08             <- cast(shared)foo
> threadInc:7FB83F4B0010
> threadInc:7FB83E1EFBF8             <- cast(Foo)foo, but != the 
> original &foo
> main:123
> main:   7FFFD6D081F0               <- foo
> main:   7FB83F4B0010
> sf1:    7FFFD6D08200    7FB83F4B0010   <- each time 
> cast(shared)foo will have a new addr
> sf2:    7FFFD6D08208    7FB83F4B0010   <- but &(x.attr) is 
> always the same
> main:456
> main:456
> main:456
> -----------------------------------------------------------------------------
>
> In all the 3 cases, &(foo.attr) are the same; this is what I 
> wanted, since the object is allocated on the heap.
>
> But the memory address of the original foo, cast(shared) foo, 
> and cast(Foo) foo are all different.
>
> And for sf1, sf2, each time cast(shared)foo will have a new 
> addr.
>
> So every time cast(shared) / cast away shared will create a new 
> wrapper to the original object, but all these wrappers' actual 
> fields (the physical addr) stay the same?
>
> What's the point of doing these wrappers every time?
>
> Esp. for sf1, and sf2?  `shared` means it's globally *shared*, 
> everywhere it's the same object, then why we have &(sf1) != 
> &(sf2)?
>
>
> What's the exact semantics of cast(shared) and cast away shared?

The only thing cast(shared) does is tell the type system 'this 
object is shared'. The reason you're getting different addresses 
is you're taking the address of the reference on the stack. As 
you've noticed, &foo.attr, &(sf1.attr), &(sf2.attr) is exactly 
the same, so the actual instances are the same.

So in D, unlike C++, you can't really refer to a class instance 
directly - you always have a reference to the instance instead. 
When you have 'Foo instance = new Foo();', 'instance' is, behind 
the scenes, a pointer, for instance 7FB83F4B0000. When you 
further add 'Foo instance2 = instance;', they both refer to the 
same object. However, 'instance' and 'instance2' are themselves 
separate variables, placed on the stack, and they will have their 
own addresses, which is what you get with &instance and 
&instance2. If you look at the addresses you get for &foo vs 
&(foo.attr), you will notice they are significantly different, 
which you would not expect for a pointer to the instance vs a 
pointer to a field inside that instance, unless the instance was 
humongous.

Since I've told you class references are pointers behind the 
scenes, you can print their values by casting them to to some 
kind of pointer. Try replacing &foo in the code with 
cast(int*)foo, and you'll see they all point to the same memory 
(8 or 16 bytes before the address of attr, since class instances 
have some hidden fields - vtable and monitor).

--
   Simen


More information about the Digitalmars-d mailing list