Jump to page: 1 2
Thread overview
How to get an inout constructor working with a template wrapper
Jul 22, 2018
aliak
Jul 22, 2018
Ali Çehreli
Jul 23, 2018
aliak
Jul 23, 2018
aliak
Jul 23, 2018
Timoses
Jul 27, 2018
aliak
Jul 28, 2018
aliak
Jul 29, 2018
aliak
Jul 31, 2018
aliak
Jul 31, 2018
aliak
Jul 28, 2018
aliak
July 22, 2018
Hi,

In the code below:

struct W(T) {
    T val;
    this(T val) inout {
        this.val = val;
    }
}

class C {}

void main() {
   W!C a = new C;
   immutable W!C b = new C;
}

W!C a = new C results in: "Error: cannot implicitly convert expression val of type C to inout(C)."

If I remove the inout on the constructor then the error is on the other line and is: "Error: mutable method W!(C).W.this is not callable using a immutable object"

If the class is changed to a struct through,  then the constructor with inout works on both lines in main above.

So I guess this has something to do with reference types (As the same behaviour is exhibited if T == int*) What's the recommended way to handle this?

Cheers,
- Ali
July 22, 2018
On 07/22/2018 03:51 PM, aliak wrote:
> Hi,
>
> In the code below:
>
> struct W(T) {
>      T val;
>      this(T val) inout {
>          this.val = val;
>      }
> }
>
> class C {}
>
> void main() {
>     W!C a = new C;
>     immutable W!C b = new C;
> }
>
> W!C a = new C results in: "Error: cannot implicitly convert expression
> val of type C to inout(C)."
>
> If I remove the inout on the constructor then the error is on the other
> line and is: "Error: mutable method W!(C).W.this is not callable using a
> immutable object"
>
> If the class is changed to a struct through,  then the constructor with
> inout works on both lines in main above.
>
> So I guess this has something to do with reference types (As the same
> behaviour is exhibited if T == int*) What's the recommended way to
> handle this?
>
> Cheers,
> - Ali

Without much confidence on my side, first, I think you need to make the constructor parameter inout(T) as well. Otherwise, you may be making a const(W!T) initialized with a non-const T.

After that, I like the "type constructor" syntax in main_alt() below (which works) but a better approach is to use a convenience function like wrap() below:

struct W(T) {
    T val;
    this(inout(T) val) inout {
        this.val = val;
    }
}

class C {}

void main_alt() {
    auto a = W!C(new C);
    auto b = immutable W!(immutable C)(new C);
}

auto wrap(T)(inout T t) {
    return inout(W!T)(t);
}

void main() {
    auto a = wrap(new C);
    auto b = wrap(new immutable(C));
}

Ali
"taklitlerinden sakınınız" :o)

July 23, 2018
On Sunday, 22 July 2018 at 23:11:09 UTC, Ali Çehreli wrote:
> Without much confidence on my side, first, I think you need to make the constructor parameter inout(T) as well. Otherwise, you may be making a const(W!T) initialized with a non-const T.
>
> After that, I like the "type constructor" syntax in main_alt() below (which works) but a better approach is to use a convenience function like wrap() below:
>
> struct W(T) {
>     T val;
>     this(inout(T) val) inout {
>         this.val = val;
>     }
> }
>
> class C {}
>
> void main_alt() {
>     auto a = W!C(new C);
>     auto b = immutable W!(immutable C)(new C);
> }
>
> auto wrap(T)(inout T t) {
>     return inout(W!T)(t);
> }
>
> void main() {
>     auto a = wrap(new C);
>     auto b = wrap(new immutable(C));
> }
>
> Ali
> "taklitlerinden sakınınız" :o)

Thank you Ali! That helped :) I've gotten most of it sorted out now, and the factory wrap is definitely the way to go, it also turned out that inout(T) and inout T (so inout without parens) was surprisingly different (maybe it's a bug? - to test you can remove the parens around U on line 3 in this sample: https://run.dlang.io/is/gd5oxW

Also over there, line 24:

auto si = wrap!(immutable int)(3);

seems to be giving problems. Any ideas there? Error is:

onlineapp.d(8): Error: inout on return means inout must be on a parameter as well for pure nothrow @nogc @safe inout(W!(immutable(int)))(immutable(int) t)
onlineapp.d(23): Error: template instance `onlineapp.wrap!(immutable(int))` error instantiating

To make it compile successfully you can either:

1) Chance immutable to const, then it works for some reason.
2) Change the line to: "auto si = wrap(cast(immutable int)3);" - i.e. do not explicitly provide type information.

Cheers,
- Ali

July 23, 2018
On Monday, 23 July 2018 at 12:02:58 UTC, aliak wrote:
> https://run.dlang.io/is/gd5oxW

Sorry wrong link!

This one is correct -> https://run.dlang.io/is/azxmGN
July 23, 2018
On Monday, 23 July 2018 at 12:02:58 UTC, aliak wrote:
>
> Thank you Ali! That helped :) I've gotten most of it sorted out now, and the factory wrap is definitely the way to go, it also turned out that inout(T) and inout T (so inout without parens) was surprisingly different (maybe it's a bug? - to test you can remove the parens around U on line 3 in this sample: https://run.dlang.io/is/gd5oxW
>
> Also over there, line 24:
>
> auto si = wrap!(immutable int)(3);

Both of these seem to work (as you pointed out)

    // immutable(W!int)
    auto si = wrap!(int)(cast(immutable)3); // or wrap(cast(immutable)3);
    // W!(immutable(int))
    auto si2 = W!(immutable int)(3);

>
> seems to be giving problems. Any ideas there? Error is:
>
> onlineapp.d(8): Error: inout on return means inout must be on a parameter as well for pure nothrow @nogc @safe inout(W!(immutable(int)))(immutable(int) t)
> onlineapp.d(23): Error: template instance `onlineapp.wrap!(immutable(int))` error instantiating
>
> To make it compile successfully you can either:
>
> 1) Chance immutable to const, then it works for some reason.
> 2) Change the line to: "auto si = wrap(cast(immutable int)3);" - i.e. do not explicitly provide type information.

I don't know why

    wrap!(immutable int)(3);

is not working. The error message

    "Error: inout on return means inout must be on a parameter as well for pure nothrow @nogc @safe inout(W!(immutable(int)))(return immutable(int) t)"

sounds very odd and not at all helpful, at least regarding that removing immutable from the template argument works.

>
> Cheers,
> - Ali

The depths of D. Why does the following only work with "return ref"?

    struct W(T) {
        T val;
        this(U : T)(auto ref inout U val) inout {
            pragma(msg, typeof(val));
            this.val = val;
        }
    }

    // Fails without "return ref" (escaping t warning...)
    auto wrap(T)(return ref inout T t) {
        return inout W!T(t);
    }

    class C {}

    void main() {
        immutable C ci = new immutable C;
        auto i = wrap(im);
        pragma(msg, typeof(i));
    }
July 27, 2018
On Monday, 23 July 2018 at 14:46:32 UTC, Timoses wrote:
> On Monday, 23 July 2018 at 12:02:58 UTC, aliak wrote:
>> [...]
>
> Both of these seem to work (as you pointed out)
>
>     // immutable(W!int)
>     auto si = wrap!(int)(cast(immutable)3); // or wrap(cast(immutable)3);
>     // W!(immutable(int))
>     auto si2 = W!(immutable int)(3);
>
>> [...]
>
> I don't know why
>
>     wrap!(immutable int)(3);
>
> is not working. The error message
>
>     "Error: inout on return means inout must be on a parameter as well for pure nothrow @nogc @safe inout(W!(immutable(int)))(return immutable(int) t)"
>
> sounds very odd and not at all helpful, at least regarding that removing immutable from the template argument works.
>
>> [...]
>
> The depths of D. Why does the following only work with "return ref"?
>
>     struct W(T) {
>         T val;
>         this(U : T)(auto ref inout U val) inout {
>             pragma(msg, typeof(val));
>             this.val = val;
>         }
>     }
>
>     // Fails without "return ref" (escaping t warning...)
>     auto wrap(T)(return ref inout T t) {
>         return inout W!T(t);
>     }
>
>     class C {}
>
>     void main() {
>         immutable C ci = new immutable C;
>         auto i = wrap(im);
>         pragma(msg, typeof(i));
>     }

Ok, thanks to Simen from another post [0], I just figured out what the correct constructor and factory method for a template wrapper should be:

https://run.dlang.io/is/S4vHzL

struct W(T) {
    T val;
    this(U : T, this This)(auto ref U val) {
        this.val = val;
    }
}

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

Seems to catch all cases!

[0]: https://forum.dlang.org/thread/hxbeektmpnmfdbvjrtcf@forum.dlang.org
July 27, 2018
On 7/23/18 8:02 AM, aliak wrote:
> On Sunday, 22 July 2018 at 23:11:09 UTC, Ali Çehreli wrote:
>> Without much confidence on my side, first, I think you need to make the constructor parameter inout(T) as well. Otherwise, you may be making a const(W!T) initialized with a non-const T.
>>
>> After that, I like the "type constructor" syntax in main_alt() below (which works) but a better approach is to use a convenience function like wrap() below:
>>
>> struct W(T) {
>>     T val;
>>     this(inout(T) val) inout {
>>         this.val = val;
>>     }
>> }
>>
>> class C {}
>>
>> void main_alt() {
>>     auto a = W!C(new C);
>>     auto b = immutable W!(immutable C)(new C);
>> }
>>
>> auto wrap(T)(inout T t) {
>>     return inout(W!T)(t);
>> }
>>
>> void main() {
>>     auto a = wrap(new C);
>>     auto b = wrap(new immutable(C));
>> }
>>
>> Ali
>> "taklitlerinden sakınınız" :o)
> 
> Thank you Ali! That helped :) I've gotten most of it sorted out now, and the factory wrap is definitely the way to go, it also turned out that inout(T) and inout T (so inout without parens) was surprisingly different (maybe it's a bug? - to test you can remove the parens around U on line 3 in this sample: https://run.dlang.io/is/gd5oxW

This seems like a bug to me.

The semantic difference is that inout(U) means the TYPE inout(U), whereas inout U means the variable U has the STORAGE CLASS inout, which also happens to make it an inout(U) type. As far as I know, the storage class inout shouldn't have any other effect on the semantic meaning.

I can't imagine any difference there, but it appears to not recognize that return should be inferred? I don't know.

> Also over there, line 24:
> 
> auto si = wrap!(immutable int)(3);
> 
> seems to be giving problems. Any ideas there? Error is:
> 
> onlineapp.d(8): Error: inout on return means inout must be on a parameter as well for pure nothrow @nogc @safe inout(W!(immutable(int)))(immutable(int) t)
> onlineapp.d(23): Error: template instance `onlineapp.wrap!(immutable(int))` error instantiating

The problem here is that inout(immutable(int)) is equivalent to immutable(int).

That is, all flavors of mutability are equivalent to immutable(int):

/*mutable*/(immutable(int)) => immutable(int)
      const(immutable(int)) => immutable(int)
  immutable(immutable(int)) => immutable(int)

So the compiler really looks at your wrap instantiation like this;

inout(W!(immutable(int))) wrap(immutable(int) t)

which triggers the (really bad) message.

I'd ask, why are you even worrying about explicit instantiation? Why not just wrap(3)?

or (if you really want to test it) wrap(immutable(int)(3))?

> 
> To make it compile successfully you can either:
> 
> 1) Chance immutable to const, then it works for some reason.

Because immutable(const(int)) => immutable(int), so the compiler can't remove the inout behind your back.

> 2) Change the line to: "auto si = wrap(cast(immutable int)3);" - i.e. do not explicitly provide type information.

Yep, do this :)

Note that the point of inout is 2-fold:

1. reduce template instantiations. In fact, wrap!int works for const, mutable and immutable int.
2. ENSURE that the data isn't modified, even in the case of mutable parameters.

-Steve
July 27, 2018
On 7/27/18 9:29 AM, aliak wrote:
> Ok, thanks to Simen from another post [0], I just figured out what the correct constructor and factory method for a template wrapper should be:
> 
> https://run.dlang.io/is/S4vHzL
> 
> struct W(T) {
>      T val;
>      this(U : T, this This)(auto ref U val) {
>          this.val = val;
>      }
> }
> 
> auto wrap(T)(auto ref T t) {
>      return W!T(t);
> }
> 
> Seems to catch all cases!

And instantiate a new template for all mutabilities. Whereas inout would only instantiate one (and disallows modification of val if not const or immutable).

-Steve
July 28, 2018
On Friday, 27 July 2018 at 14:38:27 UTC, Steven Schveighoffer wrote:
> On 7/27/18 9:29 AM, aliak wrote:
>> Ok, thanks to Simen from another post [0], I just figured out what the correct constructor and factory method for a template wrapper should be:
>> 
>> https://run.dlang.io/is/S4vHzL
>> 
>> struct W(T) {
>>      T val;
>>      this(U : T, this This)(auto ref U val) {
>>          this.val = val;
>>      }
>> }
>> 
>> auto wrap(T)(auto ref T t) {
>>      return W!T(t);
>> }
>> 
>> Seems to catch all cases!
>
> And instantiate a new template for all mutabilities. Whereas inout would only instantiate one (and disallows modification of val if not const or immutable).
>
> -Steve

If you change the ctor to be inout then you get (from the link above):

onlineapp.d(4): Error: cannot implicitly convert expression val of type onlineapp.C to inout(C)
onlineapp.d(28): Error: template instance `onlineapp.W!(C).W.__ctor!(C)` error instantiating
onlineapp.d(4): Error: cannot implicitly convert expression val of type S1 to inout(S1)
onlineapp.d(44): Error: template instance `onlineapp.W!(S1).W.__ctor!(S1)` error instantiating
onlineapp.d(4): Error: cannot implicitly convert expression val of type onlineapp.C to inout(C)
onlineapp.d(9): Error: template instance `onlineapp.W!(C).W.__ctor!(C)` error instantiating
onlineapp.d(52):        instantiated from here: wrap!(C)
onlineapp.d(4): Error: cannot implicitly convert expression val of type const(C) to inout(const(C))
onlineapp.d(9): Error: template instance `onlineapp.W!(const(C)).W.__ctor!(const(C))` error instantiating
onlineapp.d(53):        instantiated from here: wrap!(const(C))

Am I applying inout incorrectly?
July 28, 2018
On Friday, 27 July 2018 at 14:34:54 UTC, Steven Schveighoffer wrote:
> The problem here is that inout(immutable(int)) is equivalent to immutable(int).
>
> That is, all flavors of mutability are equivalent to immutable(int):
>
> /*mutable*/(immutable(int)) => immutable(int)
>       const(immutable(int)) => immutable(int)
>   immutable(immutable(int)) => immutable(int)
>
> So the compiler really looks at your wrap instantiation like this;
>
> inout(W!(immutable(int))) wrap(immutable(int) t)

Ah ok, so the compiler remove inout behind me back here? (And then tells me it needs to be there? :p)

>
> which triggers the (really bad) message.
>
> I'd ask, why are you even worrying about explicit instantiation? Why not just wrap(3)?

Just because I don't see why it should not work really. Why not allow wrap!(immutable int)(3)?

>
> or (if you really want to test it) wrap(immutable(int)(3))?
>
>> 
>> To make it compile successfully you can either:
>> 
>> 1) Chance immutable to const, then it works for some reason.
>
> Because immutable(const(int)) => immutable(int), so the compiler can't remove the inout behind your back.
>
>> 2) Change the line to: "auto si = wrap(cast(immutable int)3);" - i.e. do not explicitly provide type information.
>
> Yep, do this :)
>
> Note that the point of inout is 2-fold:
>
> 1. reduce template instantiations. In fact, wrap!int works for const, mutable and immutable int.
> 2. ENSURE that the data isn't modified, even in the case of mutable parameters.

Thanks for the explanations! For some reason it's hard to get it all to *just work* right now without the template this. But it's probably some minor detail I'm just overlooking...

>
> -Steve


« First   ‹ Prev
1 2