Immutable member functions and private members

simendsjo simendsjo at gmail.com
Wed Aug 3 02:44:27 PDT 2011


On 03.08.2011 10:52, Jonathan M Davis wrote:
> On Wednesday 03 August 2011 10:37:58 simendsjo wrote:
>> I have a struct with a private member that is only ever accessed through
>> a single property method - even from within the struct.
>> As this property fills the value on the first access, it cannot be
>> immutable, and as such, none of the many methods accessing this property
>> can be immutable methods.
>>
>> This is according to specification, but I thought that since the single
>> write to the property is done at one, and only one, access point, that
>> it would be safe?
>>
>> I could fill this value in the constructor, but it's a bit slow, so I'd
>> rather do it only if needed.
>>
>> And is there any potential performance optimizations done by the
>> compiler, or is it "only" for safety?
>> Is there a way to hack around this, and more importantly, is it safe to
>> do so, or will I open Pandora's box?
>>
>>
>> Small example:
>>
>> int len(const char[] c) {
>>       return c.length;
>> }
>>
>> struct S {
>>       private immutable(char)[] _v;
>>       @property immutable(char[]) v() { // Cannot be immutable method
>>           if(!_v)
>>               _v = "init"; /* or from external function */
>>           return _v;
>>       }
>>
>>       @property int a() { // and so this cannot be immutable method
>>           return len(v); /* notice the property function v that might
>> modify _v */
>>       }
>> }
>>
>> void main() {
>>       S s;
>>       s.a;
>> }
>
> You're basically looking for logical const - albeit a subset which would be
> much easier to implement were we to implement it (that is, a lazy initialized
> const or immutable member variable). D has no support for logical const. Even
> worse, you're looking for logical immutable (which makes no sense at all
> beyond perhaps lazy initialization and probably doesn't even make sense
> there).
>
> The thing is that immutable methods are pointless unless you make the struct
> immutable (if you want to be able to call them with both a mutable and
> immutable instance of the struct, then you need the functions to be const, not
> immutable). And if you make the struct immutable, the compiler is free to put
> it in read-only memory if it so chooses, at which point setting _anything_ in
> the struct after the constructor has run is likely to blow up. So, even if you
> can get around the issue via casts and get both lazy initialization and
> immutable methods, there's a good chance that it'll blow up at some point (as
> in segfault or worse).
>
> If you were trying to do this with const, you might get away with it (though
> you'd be stepping outside of the type lsystem by casting away const and then
> altering anything - it's undefined behavior). But with immutable, there's no
> way that this is a good idea.
>
> Lazy initialization with const or immutable member variables just is _not_ a
> good idea in D. D provides no type-safe way to do this. You must break the
> type system by casting away const or immutable to even attempt it.
> Convievably, in the case of const, the language could be extended to allow for
> lazy initialization of member variables, but there's no way that it could do
> that with immutable (because the variable could conceivably be put in read-
> only memory), and even if it were done, it would likely have to be a D3
> feature. Syntactically, it would probably be something like this:
>
> lazy int v = initFunc();
>
> and then when v was first accessed, initFunc would be called and v set to that
> value. But that could be ugly and inefficient to implement even if it's
> theoretically possible, so I wouldn't bet on anything like that making it into
> the language. Regardless, it wouldn't be until D3. For now, D doesn't support
> any kind of logical const.
>
> http://stackoverflow.com/questions/4219600/logical-const-in-d
>
> - Jonathan M Davis

Thanks!

I'm not really sure the compiler could put my struct in ROM.
My lazy parameter is immutable(char)[], so the compiler should see that 
I have a non-immutable reference.

The entire struct is immutable without this lazy variable though.
It only has two handles for passing to external functions.
I really would like to always use it only as immutable s = S(123). It 
makes no sense for it to be mutable at all.

Below is an exact example of what I want to do.
If I move the handle2 calculation to the ctor and use const methods, I 
still cannot call the methods using a const variable though.. Bug?
   const s = S(100);
   s.a; // function t.S.a () immutable is not callable using argument type
It says immutable when it should say const..?

   immutable s = S(100);
   s.a; // works on both const and immutable

----

import std.conv, std.exception;

// external expensive function
extern(System) char[] getHandle2(const int handle) {
     return to!(char[])(handle);
}

// other external functions taking string handle instead of int
extern(System) int extFunc1(string handle2) {
     return to!int(handle2);
}

struct S {
     immutable int handle;
     private immutable(char)[] _handle2;

     this(int handle) {
         this.handle = handle;
     }

     @property immutable(char[]) handle2() {
         if(!_handle2) {
             auto buf = getHandle2(handle);
             _handle2 = assumeUnique(buf);
         }
         return _handle2;
     }

     @property int a() {
         return extFunc1(handle2);
     }

     // many more properties like this
}

void main() {
     auto s = S(100);
     assert(s.a == 100);

     immutable s2 = S(100);
     //assert(s2.a == 100); // oops, a not immutable
}


More information about the Digitalmars-d-learn mailing list