How to break const

Christophe Travert travert at phare.normalesup.org
Tue Jun 19 10:18:36 PDT 2012


Timon Gehr , dans le message (digitalmars.D:170178), a écrit :
> That is completely unrelated.
> It is impossible to justify transitivity of const for delegate context
> pointers using this argument. It is far too general and the
> justification for the general concept comes from a specific example
> that is different from the one at hand.
> 
> The question is, what the meaning of 'const' references should be:
> 
> 1. data cannot be changed transitively through the reference
> 
> 2. the reference can reference both 'const' and 'immutable' data and
>     'immutable' data can transitively not be changed through the
>     reference.
> 
> 
> 1. requires transitive const for delegate context pointers, 2. does not.

A const reference can contain

I don't understand the difference.


struct Delegate(C, F, Args)
{
    C* ptr; // points to some structure containing all referenced data
    R function(C*, Args) fun;
    R opCall(Args) { return ptr.fun(Args); }
}

The signature of opCall determines the type of the delegate. In reality, 
the delegate is opaque, and C is not typed. ptr is a pointer to void*, 
and fun knows how to use that pointer. But that does not prevent the 
pointer to be const or immutable.

Note that calling opCall is not possible if the Delegate is const, or 
part of a const structure, because opCall does not have the const 
attribute.

But the signature of opCall could have any kind of attributes: const, 
immutable, pure, nothrow, inout..., which can be reflected by the 
delegates type.

A delegate of type "R delegate(Args) const" would be like this:

struct DelegateConst(C, R, Args...)
{
  const C* ptr;
  R function(const C*, Args) fun;
  R opCall(Args) const { return ptr.fun(Args); }
}

Now it is possible to call opCall if the DelegateConst is const. 
However, it is possible to build this delegate only if fun is const with 
regard to its context argument.

The same holds if you replace const by immutable.

Now, the context pointer can point to all type of data. C is a like a 
structure, and can contain any kind of data (mutable, const, immutable, 
shared...). However transitivity rules must be preserved. If the data is 
immutable, the delegate context pointer must be. If the data is a mix of 
mutable, const, and immutable data, there is no problem, has long has 
the function mutates only the mutable data (but then, the delegate's 
frame pointer type must be mutable, and the delegate is not callable if 
it is const).

However, it must respect transitivity: if the delegate is immutable, all 
data contained in the context must be immutable. If the context pointer 
is const, the data can be mutable, const, or immutable.


And where does all this comes from ?
delegates are primarily methods applied to a struct or class instance.

class S
{
  data d;
  void method(arg);
}

S s = new S;
void delegate(arg) dg = &s.method;

The delegate is constructed directly from the object's method. (1)

That is why it must have the same signature has objects method. If you 
want to fully represent methods, delegates must have all methods 
attributes: pure, nothrow, (which is a problem to introduce in 
toString methods, for example), etc... but also const, immutable, and 
maybe one day inout.

Currently, delegates does not support all this. It makes life easier, 
because we do not have a zillion types of delegates, and it gives a 
little bit of air on const virality until we have proper systems to 
simplify all this. But this is a gap in the langage. It is up to the 
programmer to respect const transitivity, and not exploit this gap and 
break the langage.

(1) In my first example, a langage delegate can be obtained from my 
artificial Delegate template by taking the adresse of opApply:

Delegate!(C, R, Args) s;
R delegate(Args) dg = &s.opApply

(2) Note that it is however possible to obtain a 'C delegate(Args) 
const' but taking the adress of a const method.

class S
{
  data d;
  void method(Arg) const;
}

S s = new S;
auto dg = &s.method; 

dg is infered as 'void delegate(Arg) const' by the compiler

-- 
Christophe


More information about the Digitalmars-d mailing list