Thread overview
Troubles creating templated inout objects
Jul 10, 2018
Timoses
Jul 11, 2018
Timoses
Jul 12, 2018
Timoses
Jul 12, 2018
Timoses
Jul 12, 2018
Timoses
July 10, 2018
How do I create an inout object with template parameters?

Take following code:

	import std.stdio;
	import std.traits;

	struct S
	{
		int[] arr;
	}

	interface I
	{
		inout(I) opIndex(size_t idx) inout;
	}

	class Test(T) : I
	{
                // Error: variable `onlineapp.Test!(inout(int)[]).Test.member` only parameters or stack based variables can be inout
		T member;

		this(inout T mem) inout
		{
			this.member = mem;
		}

		inout(Test!T) get() inout
		{
			return new inout Test!(Unqual!(typeof(member)))(member);
		}

		inout(I) opIndex(size_t idx) inout
		{
			switch (idx)
			static foreach (index, t; T.tupleof)
			{
				case index:
                                        // Error: template instance `onlineapp.Test!(inout(int)[])` error instantiating
					return new inout
						Test!(Unqual!(typeof(this.member.tupleof[index])))
									(this.member.tupleof[index]);
                default:
                	return null;
			}
		}
	}


	unittest
	{
		auto s = S([1,2,3]);
		auto t = new const Test!S(s);
	}


`Unqual` in this case just turns `inout(int[])` into `inout(int)[]`, which is why it complains. That's a side effect of this example, however the main question is how one would go about achieving something like this idiomatically?

I would like to return a new object and that object should have the same mutability as the one creating it.
July 10, 2018
On 7/10/18 10:34 AM, Timoses wrote:
> How do I create an inout object with template parameters?
> 
> Take following code:
> 
>      import std.stdio;
>      import std.traits;
> 
>      struct S
>      {
>          int[] arr;
>      }
> 
>      interface I
>      {
>          inout(I) opIndex(size_t idx) inout;
>      }
> 
>      class Test(T) : I
>      {
>                  // Error: variable `onlineapp.Test!(inout(int)[]).Test.member` only parameters or stack based variables can be inout
>          T member;
> 
>          this(inout T mem) inout
>          {
>              this.member = mem;
>          }
> 
>          inout(Test!T) get() inout
>          {
>              return new inout Test!(Unqual!(typeof(member)))(member);
>          }
> 
>          inout(I) opIndex(size_t idx) inout
>          {
>              switch (idx)
>              static foreach (index, t; T.tupleof)
>              {
>                  case index:
>                                          // Error: template instance `onlineapp.Test!(inout(int)[])` error instantiating
>                      return new inout
>                          Test!(Unqual!(typeof(this.member.tupleof[index])))
>                                      (this.member.tupleof[index]);
>                  default:
>                      return null;
>              }
>          }
>      }
> 
> 
>      unittest
>      {
>          auto s = S([1,2,3]);
>          auto t = new const Test!S(s);
>      }
> 
> 
> `Unqual` in this case just turns `inout(int[])` into `inout(int)[]`, which is why it complains. That's a side effect of this example, however the main question is how one would go about achieving something like this idiomatically?
> 
> I would like to return a new object and that object should have the same mutability as the one creating it.

You are overthinking :) inout typically is much easier than you expect, until you need to create temporary structs or types with inout members, then it becomes problematic.

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

I had to put in a static if, because your function doesn't work once you get down to the array type. See the // fixme comment.

-Steve
July 11, 2018
On Tuesday, 10 July 2018 at 18:01:59 UTC, Steven Schveighoffer wrote:
> You are overthinking :) inout typically is much easier than you expect, until you need to create temporary structs or types with inout members, then it becomes problematic.
>
> https://run.dlang.io/is/kosYuC
>
> I had to put in a static if, because your function doesn't work once you get down to the array type. See the // fixme comment.

Ok, well that helped a tiny bit for the example.

I'm trying to reproduce the errors from my project. It's starting to get out of control : D. inout is on a rampage!

https://run.dlang.io/is/5TN7XX

I guess it's the same as for immutable initialization of arrays. I can't seem to find a proper response to this one..

	import std.traits;

	struct S
	{
		int[3] arr;
	}
	struct SS
	{
		S s;
	}

	interface I
	{
		inout(I) opIndex(size_t idx) inout;
	}

	class Test(T) : I
	{
		T member;

		this(inout T mem) inout
		{
			this.member = mem;
		}

		inout(Test!T) get() inout
		{
			return new inout Test!(Unqual!(typeof(member)))(member);
		}

		inout(I) opIndex(size_t idx) inout
		{
			static if (is(T == struct))
			{
				switch (idx)
				static foreach (index, t; T.tupleof)
				{
					case index:
						return new inout
							Test!(Unqual!(typeof(this.member.tupleof[index])))
										(this.member.tupleof[index]);
					default:
						return null;
				}
			}
			else
				return null;
		}
	}

	auto test(T)(inout T t)
	{
		return new inout Test!(Unqual!T)(t);
	}

	class TestA(T : T[])
	{
		Test!T[] arr;

                // ERROR: Can't initialize inout variable in a for loop...
		this(inout(T[]) arr) inout
		{
			// 1: Nope
			foreach (mem; arr)
			    this.arr ~= test(mem);

			// 2: Nope
			//Test!T[] a;
			//foreach (mem; arr)
			//   a ~= test(mem);

			import std.algorithm : map;
			// 3: Nope
			// this.arr = arr.map!((e) => test(e)).array;
		}
	}


	void main()
	{
		auto ss = SS(S([1,2,3]));
		auto t = new const Test!SS(ss);
		auto ta = new const TestA!(Test!SS[])([t]);
	}

July 12, 2018
On Wednesday, 11 July 2018 at 12:55:35 UTC, Timoses wrote:
> On Tuesday, 10 July 2018 at 18:01:59 UTC, Steven Schveighoffer wrote:
>> You are overthinking :) inout typically is much easier than you expect, until you need to create temporary structs or types with inout members, then it becomes problematic.
>>
>> https://run.dlang.io/is/kosYuC
>>
>> I had to put in a static if, because your function doesn't work once you get down to the array type. See the // fixme comment.
>
> Ok, well that helped a tiny bit for the example.
>
> I'm trying to reproduce the errors from my project. It's starting to get out of control : D. inout is on a rampage!
>
> https://run.dlang.io/is/5TN7XX
>
> I guess it's the same as for immutable initialization of arrays. I can't seem to find a proper response to this one..
>
> [...]
> 	class TestA(T : T[])
> 	{
> 		Test!T[] arr;
>
>                 // ERROR: Can't initialize inout variable in a for loop...
> 		this(inout(T[]) arr) inout
> 		{
> 			// 1: Nope
> 			foreach (mem; arr)
> 			    this.arr ~= test(mem);
>
> 			// 2: Nope
> 			//Test!T[] a;
> 			//foreach (mem; arr)
> 			//   a ~= test(mem);
>
> 			import std.algorithm : map;
> 			// 3: Nope
> 			// this.arr = arr.map!((e) => test(e)).array;
> 		}
> 	}
>
> [...]


I guess the problem here is focused around the problem that the incoming type in the constructor is inout and that the constructed object itself is inout.

I can't seem to find another way, I'm just blatantly casting now...

class TestA(T : T[])
{
    Test!T[] arr;
    this(inout(T[]) arr) inout
    {
        import std.algorithm : map;
        import std.range: array;
        // should be okay to cast to const, won't change anything
        auto ts = cast(const T[])arr;
        // should be okay as well, as `test(t)` creates a new object
        this.arr = cast(inout(Test!T[]))(ts.map!(t => test(t)).array);
    }
}

I also found `assumeUnique` in std.exception, or std.experimental.allocator.makeArray. I'm not to sure how they might be able to circumvent the casting though, since I need an inout array of the objects...

Am I missing something or is `inout` simply not that well "implemented" yet?


July 12, 2018
On Tuesday, 10 July 2018 at 14:34:55 UTC, Timoses wrote:
> `Unqual` in this case just turns `inout(int[])` into `inout(int)[]`, which is why it complains. That's a side effect of this example [...]


See also:
https://issues.dlang.org/show_bug.cgi?id=3567
July 12, 2018
On 7/11/18 8:55 AM, Timoses wrote:
> On Tuesday, 10 July 2018 at 18:01:59 UTC, Steven Schveighoffer wrote:
>> You are overthinking :) inout typically is much easier than you expect, until you need to create temporary structs or types with inout members, then it becomes problematic.
>>
>> https://run.dlang.io/is/kosYuC
>>
>> I had to put in a static if, because your function doesn't work once you get down to the array type. See the // fixme comment.
> 
> Ok, well that helped a tiny bit for the example.
> 
> I'm trying to reproduce the errors from my project. It's starting to get out of control : D. inout is on a rampage!
> 
> https://run.dlang.io/is/5TN7XX
> 
> I guess it's the same as for immutable initialization of arrays. I can't seem to find a proper response to this one..
> 
>      import std.traits;
> 
>      struct S
>      {
>          int[3] arr;
>      }
>      struct SS
>      {
>          S s;
>      }
> 
>      interface I
>      {
>          inout(I) opIndex(size_t idx) inout;
>      }
> 
>      class Test(T) : I
>      {
>          T member;
> 
>          this(inout T mem) inout
>          {
>              this.member = mem;
>          }
> 
>          inout(Test!T) get() inout
>          {
>              return new inout Test!(Unqual!(typeof(member)))(member);
>          }
> 
>          inout(I) opIndex(size_t idx) inout
>          {
>              static if (is(T == struct))
>              {
>                  switch (idx)
>                  static foreach (index, t; T.tupleof)
>                  {
>                      case index:
>                          return new inout
>                              Test!(Unqual!(typeof(this.member.tupleof[index])))
>                                          (this.member.tupleof[index]);
>                      default:
>                          return null;
>                  }
>              }
>              else
>                  return null;
>          }
>      }
> 
>      auto test(T)(inout T t)
>      {
>          return new inout Test!(Unqual!T)(t);
>      }
> 
>      class TestA(T : T[])
>      {
>          Test!T[] arr;
> 
>                  // ERROR: Can't initialize inout variable in a for loop...
>          this(inout(T[]) arr) inout
>          {
>              // 1: Nope
>              foreach (mem; arr)
>                  this.arr ~= test(mem);
> 
>              // 2: Nope
>              //Test!T[] a;
>              //foreach (mem; arr)
>              //   a ~= test(mem);
>

On the right track, but inside inout (or const or immutable) constructors, the members can only be initialized once. So you have to initialize a local, and then set the member once.

The issue is, your input is *also* inout (a necessary condition), so you didn't declare a properly:

inout(Test!T)[] a;
foreach (mem; arr) a ~= test(mem);
this.arr = a;

-Steve
July 12, 2018
On 7/12/18 4:58 AM, Timoses wrote:
> On Tuesday, 10 July 2018 at 14:34:55 UTC, Timoses wrote:
>> `Unqual` in this case just turns `inout(int[])` into `inout(int)[]`, which is why it complains. That's a side effect of this example [...]
> 
> 
> See also:
> https://issues.dlang.org/show_bug.cgi?id=3567

Responded there, but really this is a misunderstanding of Unqual. Unqual should shallowly strip as much off as it can, but shouldn't allow you to break const.

The problem is that for things other than pointers or arrays, it can't provide a tail-const version, so it goes too far. Fixing this will probably break a lot of code, which means we need to find another way.

-Steve
July 12, 2018
On Thursday, 12 July 2018 at 12:22:34 UTC, Steven Schveighoffer wrote:
> On 7/11/18 8:55 AM, Timoses wrote:
>>      class TestA(T : T[])
>>      {
>>          Test!T[] arr;
>> 
>>                  // ERROR: Can't initialize inout variable in a for loop...
>>          this(inout(T[]) arr) inout
>>          {
>>              // 1: Nope
>>              foreach (mem; arr)
>>                  this.arr ~= test(mem);
>> 
>>              // 2: Nope
>>              //Test!T[] a;
>>              //foreach (mem; arr)
>>              //   a ~= test(mem);
>>
>
> On the right track, but inside inout (or const or immutable) constructors, the members can only be initialized once. So you have to initialize a local, and then set the member once.
>
> The issue is, your input is *also* inout (a necessary condition), so you didn't declare a properly:
>
> inout(Test!T)[] a;
> foreach (mem; arr) a ~= test(mem);
> this.arr = a;
>
> -Steve

Aw, thanks! This is much nicer than casting...