Thread overview
Casting lvalues
Apr 02, 2021
z
Apr 02, 2021
z
Apr 02, 2021
tsbockman
April 02, 2021
shared TT[] a;
T processArray(T)(ref T[] p){/*...*/} //function calls .reserve on its parameter
a.processArray;
>

Template name cannot deduce function from argument types !()(shared(T[]))...

Even if the function is changed to only accept shared parameters, .reserve does not appear to support shared so the function is impossible to use without somehow changing its type or using __gshared.
Other than using pointers and casting(processArray(*(cast(Unqual!TT*)&a)), not ideal), is there a better way to transform an lvalue's type without it transforming into an rvalue?(direct casting seems to create an rvalue because compilation fails.)
Thanks

April 02, 2021

On Friday, 2 April 2021 at 12:47:35 UTC, z wrote:

>
T processArray(T)(ref T[] p){/*...*/} //function calls .reserve

i meant void for the function return type.

April 02, 2021

On Friday, 2 April 2021 at 12:47:35 UTC, z wrote:

>

Even if the function is changed to only accept shared parameters, .reserve does not appear to support shared so the function is impossible to use without somehow changing its type or using __gshared.

There is no way that .reserve can correctly support shared, because it cannot know how to correctly synchronize the slice and its contents in the larger context of the whole program.

In general, just casting away shared to make the compiler stop complaining completely defeats the purpose of shared, and makes it just as unsafe as __gshared.

Instead, you must do any necessary synchronization yourself using, for example, core.sync.mutex. Only within properly synchronized regions should you cast away shared. This is the purpose of shared, and the only difference from __gshared: to remind you to synchronize by requiring the use of a cast to do much of anything.

Typically, you should synchronize an entire region of code across which the shared data under protection will begin and end with all invariants satisfied. Synchronizing individual operations (like just .reserve by itself) is usually wrong - or rather, insufficient.

The synchronization must guarantee that whenever a thread is writing to the data, it has exclusive access. But, it is safe and fast to have multiple threads read data simultaneously, as long as it is not written to during that time. core.sync.rwmutex can be used to implement this optimization.

(It is possible to design algorithms that support multiple simultaneous writers using lock-free algorithms (see core.atomic) or other more complicated schemes, but this is much harder to do correctly and usually not necessary.)

Finally, if you only need to write the data once, after which it will only be read, then you can skip all of this confusion and complexity by just preparing the data from a single thread in a non-shared container and then using cast(immutable) (if there is only one extant reference to the data) or .idup (otherwise). No synchronization is necessary for immutable data.

>

*(cast(Unqual!TT*)&a), not ideal

That is the correct way to perform an lvalue reinterpret cast (provided that the resulting type is actually compatible with the source type).

>

(direct casting seems to create an rvalue because compilation fails.)

Yes, direct casting should result in an rvalue. (Although, I think it is possible to subvert this with a custom opCast.)