Immutable member functions and private members

simendsjo simendsjo at gmail.com
Wed Aug 3 03:00:18 PDT 2011


On 03.08.2011 11:44, simendsjo wrote:
> 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
> }

Oh, and BCS example didn't work directly.
I had to do this:

struct S {
   private immutable(char)[] _handle2;
   @property immutable(char[]) handle2() immutable {
     auto s = cast(S)this;
     if(!_handle2) {
       s._handle2 = "some value";
     }
     return s._handle2; // just _handle2 is empty, so I have to use 
s._handle2
   }
}

Given that I have the non-immutable reference. Would this hack be safe?


More information about the Digitalmars-d-learn mailing list