logical const without casts!

Christophe travert at phare.normalesup.org
Fri Sep 30 09:16:03 PDT 2011


"Steven Schveighoffer" , dans le message (digitalmars.D:145821), a
 écrit :
> If const must be a part of the signature, then you have lost the typeless  
> aspect of the context pointer.  If we expose the const (or not const)  
> nature of the pointer, then you lose the interoperability that delegates  
> provide.  The beauty of delegates is you don't *have to* care what the  
> type of the context pointer is.  That's defined by the function the  
> delegate represents.

No, you don't lose the typeless aspect of the context pointer. You lose 
a litle bit of that aspect, but not that much.

In D, const is transitive. That means: everything you touch via a const 
object remains const. non-const delegate context pointer breaks that 
rule.

You have const member functions, and initially, delegates were member 
functions associated with an object, so why could not the delegate be 
const ? A const delegate would just be a delegate that promise it 
doesn't touch to its context. It's not a type-defined delegate.

If I implement a delegate at the library level, when I call the 
function, I have to make a cast to access the context pointer. With cast 
removing implicitely the constness of the context pointer, I get the 
current D implementation of delegates. This is legal, but this is also 
undefined behavior. I believe that langage delegate work that way, that 
they are thus undefined behavior, and this should be corrected.

I agree the langage could stipulate that delegate context pointer are 
exceptions, to allow the valuable trick like you revealed. But, to me, 
that is not the current policy about constness.


Do you see the problem in the following code:

class Foo
{
  this(ref int _i) { i = () { return _i; } }
  ref int delegate() dg;
  ref int i() const { return dg(); }
}

int globalInt;

immutable Foo globalFoo = Foo(globalInt);

int bar(const Foo foo)
{
    return foo.i++;
}

int globalBar()
{
    return bar(globalFoo);
}


Answer:

In multithreaded application, globalFoo, which is immutable, is 
automatically shared. However, globalInt is not. globalBar allows you to 
access to (and modify) globalInt. But globalInt is not protected for 
concurrent access. And globalInt from which thread is accessed ?

Possible solutions:

- do nothing, and let the programmer introducing multi-threading in 
the application deal with this, even if the programmers of Foo, bar and 
buggy did not cared to document the impact of their code for 
multithreaded applications.

- forbid to put/call any delegate into an immutable object: that almost 
means forbiding to put/call a delegate into a const object.

- what I propose: implement the separate kind of const delegates, that 
allows to protect their context pointers, and that you can safely call 
from const/immutable data.

> But in the case of the bug I filed, we are talking about a  
> compiler-sanctioned implicit transformation to immutable.  We *must*  
> guarantee that when we allow an implicit transformation, that there are no  
> existing mutable copies of the data.  An explicit transformation should be  
> allowed, because then the user has accepted responsibility.

With my proposal, you can very easily keep an immutable reference in a 
const delegate. The delegate will just not be callable if its 
function pointer is not const with regard to the context pointer.


In any case, if the langage decides that delegate context pointer should 
escape const protection, great care should be taken to make sure they 
don't escape purity. In my example, there should be a compiler error if 
I tried to declare Foo.i and bar pure (maybe there is already an error, 
I didn't test).

-- 
Christophe


More information about the Digitalmars-d mailing list