digitalmars.D.learn - forward tuple arg to local variable + dtor
- vit (48/48) Jan 22 2022 Hello,
- Adam Ruppe (5/5) Jan 22 2022 You can't forward to a local variable. Local variables will be a
- vit (34/39) Jan 22 2022 Thanks,
- =?UTF-8?Q?Ali_=c3=87ehreli?= (12/14) Jan 22 2022 I don't even understand where the local variable comes from. If you want...
- vit (78/95) Jan 22 2022 I want implement something like this:
- Stanislav Blinov (37/39) Jan 22 2022 Take by value and make a copy without forwarding:
- vit (4/11) Jan 22 2022 Thanks, second options is what I need. (In my case forwarding is
- Stanislav Blinov (16/17) Jan 22 2022 Scratch the previous reply, 'twas a brain fart... Simply take by
Hello, Why is tuple variable `params` immediately destructed after its construction? Why is `check(-2)` after `dtor(2)`? Code: ```d import std.stdio : writeln; import core.lifetime : forward, move; struct Foo{ int i; this(int i){ this.i = i; writeln("ctor(", i, "): ", cast(void*)&this); } ~this(){ writeln("dtor(", i, "): ", cast(void*)&this); i *= -1; } } void seq(Args...)(auto ref Args args){ Args params = forward!args; writeln("params initialized"); alias foo = params[0]; writeln("check(", foo.i, "): ", cast(void*)&foo); } void main(){ writeln("Foo(1):"); { auto foo = Foo(1); writeln("check(", foo.i, "): ", cast(void*)&foo); } writeln("\nFoo(2):"); seq(Foo(2)); } ``` Output: ``` Foo(1): ctor(1): 7FFEBCAF0538 check(1): 7FFEBCAF0538 dtor(1): 7FFEBCAF0538 Foo(2): ctor(2): 7FFEBCAF0548 dtor(2): 7FFEBCAF04F0 params initialized check(-2): 7FFEBCAF04F0 dtor(0): 7FFEBCAF0558 ```
Jan 22 2022
You can't forward to a local variable. Local variables will be a copy of the tuple. forward only actually works if sent *directly* to another function call. There's a bunch of things in D that only work in function parameter lists and not local variables. This is one of them.
Jan 22 2022
On Saturday, 22 January 2022 at 14:23:32 UTC, Adam Ruppe wrote:You can't forward to a local variable. Local variables will be a copy of the tuple. forward only actually works if sent *directly* to another function call. There's a bunch of things in D that only work in function parameter lists and not local variables. This is one of them.Thanks, Why local variable of type tuple call destructors immediately after initialization? ```d import std.stdio : writeln; import std.meta : AliasSeq; struct Foo{ int i; this(int i){ this.i = i; writeln("ctor(", i, "): ", cast(void*)&this); } ~this(){ writeln("dtor(", i, "): ", cast(void*)&this); i *= -1; } } void main(){ { AliasSeq!(Foo, Foo) tup; static foreach(alias x; tup) writeln("check(", x.i, "): ", cast(void*)&x); //x is destructed } } ``` Print: ``` dtor(0): 7FFF30D76868 dtor(0): 7FFF30D76858 check(0): 7FFF30D76858 //dangling? check(0): 7FFF30D76868 //dangling? ```
Jan 22 2022
On 1/22/22 07:17, vit wrote:Why local variable of type tuple call destructors immediately after initialization?I don't even understand where the local variable comes from. If you want a pair of Foo objects, I would use std.typeconst.Tuple. Otherwise I would use AliasSeq as I understand it like the following: alias tup = AliasSeq!(Foo, Foo); static foreach(Type; tup) {{ Type x; writeln("check(", x.i, "): ", cast(void*)&x); }} So, to me, AliasSeq!(Foo, Foo) is just a pair of types. I instantiate an object for each type individually and use it inside. Ali
Jan 22 2022
On Saturday, 22 January 2022 at 17:23:12 UTC, Ali Çehreli wrote:On 1/22/22 07:17, vit wrote:I want implement something like this: ```d import std.stdio : writeln; import std.meta : AliasSeq, staticMap; import core.memory : pureMalloc, pureFree; import core.lifetime : emplace, forward; void main() safe{ auto rc1 = RcPtr!int.make(1); //ref counted pointer long result = 0; //apply can be safe apply!((ref int a, ref long b){ rc1 = null; //apply has copy of rc1, a is not dangling reference result = a + b; })(rc1, RcPtr!long.make(2)); assert(result == 3); } // safe access to data of multiple ref counted objects: public auto apply(alias fn, Args...)(scope auto ref Args args){ Args params = forward!args; //copy lvalue and move rvalue args property auto ref elm(alias param)() trusted{ return param.get(); } return fn(staticMap!(elm, params)); } //simple implementation of ref counted pointer struct RcPtr(T){ private Payload* payload; private this(Payload* payload) safe{ this.payload = payload; } //copy ctor public this(ref typeof(this) rhs){ this.payload = rhs.payload; if(payload) payload.count += 1; } //make data public static auto make(Args...)(auto ref Args args){ Payload* payload = () trusted{ return cast(Payload*)pureMalloc(Payload.sizeof); }(); emplace(payload, forward!args); return RcPtr(payload); } public ~this(){ this.opAssign(null); } //release payload void opAssign(typeof(null) nil){ if(payload){ payload.count -= 1; if(payload.count == 0){ destroy(*payload); () trusted{ pureFree(payload); }(); payload = null; } } } // ref T get() system{ assert(payload); return payload.data; } private struct Payload{ int count = 1; T data; this(Args...)(auto ref Args args){ data = T(forward!args); } } } ```Why local variable of type tuple call destructors immediatelyafterinitialization?I don't even understand where the local variable comes from. If you want a pair of Foo objects, I would use std.typeconst.Tuple. Otherwise I would use AliasSeq as I understand it like the following: alias tup = AliasSeq!(Foo, Foo); static foreach(Type; tup) {{ Type x; writeln("check(", x.i, "): ", cast(void*)&x); }} So, to me, AliasSeq!(Foo, Foo) is just a pair of types. I instantiate an object for each type individually and use it inside. Ali
Jan 22 2022
On Saturday, 22 January 2022 at 18:00:58 UTC, vit wrote:I want implement something like this: ...Take by value and make a copy without forwarding: ```d import std.typecons : Tuple; import std.meta : allSatisfy; enum bool isRcPtr(T) = is(T == RcPtr!U, U); // safe access to data of multiple ref counted objects: public auto apply(alias fn, Args...)(Args args) if (allSatisfy!(isRcPtr, Args)) { Tuple!Args params = args; // borrow all property auto ref elm(alias param)() trusted{ return param.get(); } return fn(staticMap!(elm, args)); // staticMap over original args tuple } ``` Or make a helper function that takes by value and forward to that: ```d template apply(alias fn) { private auto impl(Args...)(Args args) { property auto ref elm(alias param)() trusted{ return param.get(); } return fn(staticMap!(elm, args)); } auto apply(Args...)(auto ref Args args) if (allSatisfy!(isRcPtr, Args)) { import core.lifetime : forward; return impl(forward!args); // impl takes by value - RcPtrs passed by reference will be copied } } ```
Jan 22 2022
On Saturday, 22 January 2022 at 19:01:09 UTC, Stanislav Blinov wrote:On Saturday, 22 January 2022 at 18:00:58 UTC, vit wrote:Thanks, second options is what I need. (In my case forwarding is more complex because weak ptrs need lock()).[...]Take by value and make a copy without forwarding: ```d import std.typecons : Tuple; import std.meta : allSatisfy; [...]
Jan 22 2022
On Saturday, 22 January 2022 at 18:00:58 UTC, vit wrote:I want implement something like this:Scratch the previous reply, 'twas a brain fart... Simply take by value, no need for extra copies at all in that case. Arguments themselves will become those copies as needed. ```d import std.meta : allSatisfy; enum bool isRcPtr(T) = is(T == RcPtr!U, U); // safe access to data of multiple ref counted objects: public auto apply(alias fn, Args...)(Args args) if (allSatisfy!(isRcPtr, Args)) { property auto ref elm(alias param)() trusted{ return param.get(); } return fn(staticMap!(elm, args)); } ```
Jan 22 2022