digitalmars.D.learn - Range over a container r-value with disabled postblit
- =?UTF-8?B?Tm9yZGzDtnc=?= (88/88) Jan 13 2018 Given my combined hashmap and hashset container `HashMapOrSet`
- kinke (11/20) Jan 14 2018 That sounds reasonable. For something like `foreach (e;
- =?UTF-8?B?Tm9yZGzDtnc=?= (12/22) Jan 14 2018 Thanks for confirmation, kinke.
- =?UTF-8?B?Tm9yZGzDtnc=?= (3/6) Jan 14 2018 Shall be
- Andrei Alexandrescu (3/10) Jan 14 2018 That would be difficult because lval/rval is known on he callee side. --...
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?
Jan 13 2018
On Sunday, 14 January 2018 at 01:38:17 UTC, Nordlöw wrote: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).That sounds reasonable. For something like `foreach (e; makeRange().wrapRangeByRef())`, referencing the makeRange() struct rvalue by pointer in the wrapped range won't work, as the underlying range lifetime ends with the foreach range expression, while the wrapped range's lifetime extends to the end of the foreach loop. So making the wrapped range take ownership of a range rvalue by moving it into a member (of ByRvalueElement) makes sense IMO. Lvalues aren't moved implicitly by the compiler, so referencing them by pointer (&this) is safe.
Jan 14 2018
On Sunday, 14 January 2018 at 14:04:46 UTC, kinke wrote:That sounds reasonable. For something like `foreach (e; makeRange().wrapRangeByRef())`, referencing the makeRange() struct rvalue by pointer in the wrapped range won't work, as the underlying range lifetime ends with the foreach range expression, while the wrapped range's lifetime extends to the end of the foreach loop. So making the wrapped range take ownership of a range rvalue by moving it into a member (of ByRvalueElement) makes sense IMO. Lvalues aren't moved implicitly by the compiler, so referencing them by pointer (&this) is safe.Thanks for confirmation, kinke. Further, if we add the new trait(s) __trait(isLvalue, symbol) __trait(isRvalue, symbol) or perhaps simply just __trait(isRvalue, symbol) that can be applied with symbol being `this` to make this static code branching work inside member functions such as opSlice aswell. What do you think of such an addition? Note that __trait(isLvalue, this) cannot be used to detect whether `this` is an l-value or an r-value, which I find strange.
Jan 14 2018
On Sunday, 14 January 2018 at 21:57:37 UTC, Nordlöw wrote:Note that __trait(isLvalue, this) cannot be used to detect whether `this` is an l-value or an r-value, which I find strange.Shall be __traits(isRef, this)
Jan 14 2018
On 1/14/18 4:59 PM, Nordlöw wrote:On Sunday, 14 January 2018 at 21:57:37 UTC, Nordlöw wrote:That would be difficult because lval/rval is known on he callee side. -- AndreiNote that __trait(isLvalue, this) cannot be used to detect whether `this` is an l-value or an r-value, which I find strange.Shall be __traits(isRef, this)
Jan 14 2018