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