std.concurrency & immutable classes...

Tomek Sowiński just at ask.me
Fri Feb 11 14:08:26 PST 2011


Steven Schveighoffer napisał:

> > It would be much easier if he provided the specific case(s) which broke  
> > his teeth. Then we'll all know where's the problem. If it's soluble,  
> > it'll open the door to tail type modifiers in general, not just in  
> > classes. It's a burning issue e.g. with ranges (mostly struct).
> >
> > http://d.puremagic.com/issues/show_bug.cgi?id=5377
> >
> > Look at the attachment to get a feel of what hoops we'll have to jump  
> > through to side-step lack of tail X.  
> 
> I've worked through this very same problem (a few months back), thinking  
> that we need a general solution to tail-const.  The large issue with  
> tail-const for structs in the general case is that you cannot control the  
> type of 'this'.  It's always ref.  This might seem like a very  
> inconsequential detail, but I realized that a ref to X does not implicitly  
> convert to a ref to a tail-const X.  This violates a rule of two  
> indirections, in which case you are not able to implicitly convert the  
> indirect type, even if the indirect type would implicitly convert outside  
> the reference.
>
> A simple example, you cannot convert an int** to a const(int)**.  Reason  
> being, then you could change the indirect pointer to point to something  
> that's immutable, and the original int ** now points to immutable data.

I tried to understand this on an example and now I'm even more confused. :)

    int* p;
    int** pp = &p;
    const(int)** cpp = pp;  // compiles fine
    immutable int i = 7;
    *cpp = &i;
    **pp = 5;  // mutate the immutable
    writeln(cpp, ' ', pp);
    writeln(*cpp, ' ', *pp, ' ', &i);
    writeln(**cpp, ' ', **pp, ' ', i);

The output is interesting:

12FE08 12FE08
12FE14 12FE14 12FE14
5 5 7

So even they all point to i at the end, it remains unchanged. What gives? Register caching? It doesn't matter as the int** to a const(int)** conversion should fail in the first place, but I'm curious...

> The same is for tail-const structs, because you go through one ref via  
> 'this' and the other ref via the referring member.
> 
> What does this all mean?  It basically means that you have to define  
> *separate* functions for tail-const and const, and separate functions for  
> tail-immutable and immutable.  This is untenable.

I, from the very first discussions, assumed tail-const functions are inevitable. You define empty() as const but popFront() as tail-const. Feels natural.

> You might ask "why doesn't this problem occur with tail-const arrays?",  
> well because you *don't pass them by ref*.  With structs we have no choice.
> 
> I think what we need is a way to define two different structs as being the  
> tail-const version of the other, with some compiler help, and then we do  
> not need to define a new flavor of const functions.  We still need to  
> define these "tail-const" functions, but it comes in a more understandable  
> form.  But importantly, the implicit cast makes a *temporary* copy of the  
> struct, allowing the cast to work.

I'd like to understand it better. How would you define with this scheme, say, a range on a const collection, to which ranges on an (im)mutable collection are implicitly convertible? 

-- 
Tomek



More information about the Digitalmars-d-learn mailing list