Thread overview
Any way to move in @disabled this(this) type in to a wrapping template?
Jul 25, 2019
aliak
Jul 25, 2019
aliak
Jul 25, 2019
Paul Backus
Jul 25, 2019
aliak
Jul 25, 2019
Paul Backus
July 25, 2019
Basically, can template W be made to handle an S that can't be copied?

import std;

static struct S {
    int i;
    @disable this(this);
    this(int i) { this.i = i; }
}

struct W(T) {
    T value;
    this(T value) {
        this.value = value;
    }
}

auto wrap(T)(T value) {
    return W!T(value);
}

void main() {
    auto a = wrap(S(3));
}

I tried doing something like:

W!T construct(Args...)(auto ref Args args) {
  import std.algorithm: move;
  auto value = T(args);
  W!T w;
  w.value = move(value);
  return move(opt);
}
July 25, 2019
On Thursday, 25 July 2019 at 19:35:36 UTC, aliak wrote:
> Basically, can template W be made to handle an S that can't be copied?
>
> import std;
>
> static struct S {
>     int i;
>     @disable this(this);
>     this(int i) { this.i = i; }
> }
>
> struct W(T) {
>     T value;
>     this(T value) {
>         this.value = value;
>     }
> }
>
> auto wrap(T)(T value) {
>     return W!T(value);
> }
>
> void main() {
>     auto a = wrap(S(3));
> }
>
> I tried doing something like:
>
> W!T construct(Args...)(auto ref Args args) {
>   import std.algorithm: move;
>   auto value = T(args);
>   W!T w;
>   w.value = move(value);
>   return move(opt);
> }

So this works - are there any problems with it?

struct W(T) {
    T value;
    this(T value) {
        static if (isMutable!T)
            this.value = value.move;
        else
            this.value = value;
    }
}

auto wrap(T)(T value) {
    static if (isMutable!T)
        return W!T(value.move);
    else
        return W!T(value);
}

Shouldn't this be happening by default? When would you not want that to happen?
July 25, 2019
On Thursday, 25 July 2019 at 20:38:59 UTC, aliak wrote:
> On Thursday, 25 July 2019 at 19:35:36 UTC, aliak wrote:
>> Basically, can template W be made to handle an S that can't be copied?
>>
>> import std;
>>
>> static struct S {
>>     int i;
>>     @disable this(this);
>>     this(int i) { this.i = i; }
>> }
>>
>> [...]
>
> So this works - are there any problems with it?
>
> struct W(T) {
>     T value;
>     this(T value) {
>         static if (isMutable!T)
>             this.value = value.move;
>         else
>             this.value = value;
>     }
> }
>
> auto wrap(T)(T value) {
>     static if (isMutable!T)
>         return W!T(value.move);
>     else
>         return W!T(value);
> }
>
> Shouldn't this be happening by default? When would you not want that to happen?

The way I handle this is with `auto ref` and `core.lifetime.forward`:

import core.lifetime: forward;

struct W(T)
{
    T value;
    this()(auto ref T value)
    {
        this.value = forward!value;
    }
}

auto wrap(T)(auto ref T value)
{
    return W!T(forward!value);
}

@safe unittest
{
    static struct NoCopy { @disable this(this); }
    assert(__traits(compiles, {
        auto test = wrap(NoCopy());
    }));
    assert(!__traits(compiles, {
        auto lval = NoCopy(); auto test = lval;
    }));
}

Interactive: https://run.dlang.io/is/kDJyYC

It's not very well documented, but `forward` does essentially the same thing as your `static if` + `move` combination.

Note that with both your version and mine, you will run into the same problem I did of `move` making it impossible to use instances of `W` in static initializers and CTFE. [1] The best compromise I was able to come up with was to only call move if `isCopyable!T == false`, which doesn't really solve the problem, but at least contains it.

[1] https://github.com/pbackus/sumtype/issues/22
July 25, 2019
On Thursday, 25 July 2019 at 21:23:33 UTC, Paul Backus wrote:
> On Thursday, 25 July 2019 at 20:38:59 UTC, aliak wrote:
>> On Thursday, 25 July 2019 at 19:35:36 UTC, aliak wrote:
>>> Basically, can template W be made to handle an S that can't be copied?
>>>
>>> import std;
>>>
>>> static struct S {
>>>     int i;
>>>     @disable this(this);
>>>     this(int i) { this.i = i; }
>>> }
>>>
>>> [...]
>>
>> So this works - are there any problems with it?
>>
>> struct W(T) {
>>     T value;
>>     this(T value) {
>>         static if (isMutable!T)
>>             this.value = value.move;
>>         else
>>             this.value = value;
>>     }
>> }
>>
>> auto wrap(T)(T value) {
>>     static if (isMutable!T)
>>         return W!T(value.move);
>>     else
>>         return W!T(value);
>> }
>>
>> Shouldn't this be happening by default? When would you not want that to happen?
>
> The way I handle this is with `auto ref` and `core.lifetime.forward`:
>
> import core.lifetime: forward;
>
> struct W(T)
> {
>     T value;
>     this()(auto ref T value)
>     {
>         this.value = forward!value;
>     }
> }
>
> auto wrap(T)(auto ref T value)
> {
>     return W!T(forward!value);
> }
>
> @safe unittest
> {
>     static struct NoCopy { @disable this(this); }
>     assert(__traits(compiles, {
>         auto test = wrap(NoCopy());
>     }));
>     assert(!__traits(compiles, {
>         auto lval = NoCopy(); auto test = lval;
>     }));
> }
>
> Interactive: https://run.dlang.io/is/kDJyYC
>
> It's not very well documented, but `forward` does essentially the same thing as your `static if` + `move` combination.
>
> Note that with both your version and mine, you will run into the same problem I did of `move` making it impossible to use instances of `W` in static initializers and CTFE. [1] The best compromise I was able to come up with was to only call move if `isCopyable!T == false`, which doesn't really solve the problem, but at least contains it.
>
> [1] https://github.com/pbackus/sumtype/issues/22

Haha. Ironic. Thanks, again :)

Though, if you use auto ref, and you check if it's mutable and not copyable and then move, then that means you could potentially be applying move to an object on behalf of the clients

auto a = MyUnmovableType()
auto b = LibraryType(a);
writeln(a); // ??

If this is a problem, I guess a __traits(isRef, parameter) check along with mutable and copyable could help. Then if client want it moved they could call move explicitly.
July 25, 2019
On Thursday, 25 July 2019 at 21:58:06 UTC, aliak wrote:
> Haha. Ironic. Thanks, again :)
>
> Though, if you use auto ref, and you check if it's mutable and not copyable and then move, then that means you could potentially be applying move to an object on behalf of the clients
>
> auto a = MyUnmovableType()
> auto b = LibraryType(a);
> writeln(a); // ??
>
> If this is a problem, I guess a __traits(isRef, parameter) check along with mutable and copyable could help. Then if client want it moved they could call move explicitly.

Yeah, that's why you use core.lifetime.forward instead of directly calling move--it checks all of this stuff for you.