Range over a container r-value with disabled postblit

Nordlöw per.nordlow at gmail.com
Sun Jan 14 01:38:17 UTC 2018


Given my combined hashmap and hashset container `HashMapOrSet` 
defined at

https://github.com/nordlow/phobos-next/blob/master/src/hashmap_or_hashset.d

with deterministic memory management and disabled copy 
constructions and a member byElement() defined as

         @property auto byElement()() inout
         {
             dln("entering byElement");
             alias This = ConstThis;
             // TODO is the use of `&this` incorrect when `this` 
is an r-value?
             auto result = 
ByElement!This((ElementRef!This(cast(This*)&this)));
             result.initFirstNonEmptyBin();
             dln("leaving byElement");
             return result;
         }

         scope auto opSlice()() inout return
         {
             return byElement();
         }

where

         static private struct ByElement(HashMapOrSetType)
         {
             static if (is(ElementType == class))
             {
                 /// Get reference to front element (key and 
value).
                 @property scope auto front()() return @trusted
                 {
                     /* cast away const from `HashMapOrSetType` 
for classes
                      * because class elements are currently 
hashed and compared
                      * compared using their identity (pointer 
value) `is`
                      */
                     return 
cast(ElementType)table.binElementsAt(binIx)[elementOffset];
                 }
             }
             else
             {
                 /// Get reference to front element (key and 
value).
                 @property scope auto front()()
                 {
                     return 
table.binElementsAt(binIx)[elementOffset];
                 }
             }

             public ElementRef!HashMapOrSetType _elementRef;

             alias _elementRef this;
         }

iteration via opSlice works fine when `X` is fed as `this` to 
`byElement` as an l-value as in

     import digestx.fnv : FNV;
     alias X = HashMapOrSet!(uint, void, null, FNV!(64, true));
     const x = X.withElements([11].s);
     foreach (e; x.byElement) {}

but when `X` is fed as an r-value `this` to `byElement` as in

     import digestx.fnv : FNV;
     alias X = HashMapOrSet!(uint, void, null, FNV!(64, true));
     foreach (e; X.withElements([11].s).byElement) {}

I get a behaviour that seems to indicate that the instance of `X` 
is freed after `byValue()` (opSlice) returns but before the 
`ByValue`-range is consumed, resulting in incorrect (undefind) 
behaviour.

Is it incorrect to use `&this` as follows

             auto result = 
ByElement!This((ElementRef!This(cast(This*)&this)));

in the `byElement`?

If so, is there a way around this problem except for making my 
container be RC-allocated?

My current proposal for a solution is to make `byElement` a free 
unary function

     byElement(auto ref X x)

which statically checks via

     static if (__traits(isRef, x))

whether the `X`-instance is passed as either an

- l-value, where my current solution works (ByLvalueElement), or
- r-value, in which the X-instance instead is moved into range 
(ByRvalueElement).

byValue can be used via UFCS, but this solution removes the 
possibility for using the opSlice-overload which I can live with.

Further, does all EMSI-container-style containers (with disabled 
postblit) have the same issue with ranges over r-value instances 
of its containers?


More information about the Digitalmars-d-learn mailing list