www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - typedef behavior

reply Alex <sascha.orlov gmail.com> writes:
A question about Typedef usage:
Say, I have the following circumstances

/// --- code --- ///

import std.typecons;

void main()
{
	MyEA ea;
	MyEB eb;
	ea.tarr.length = 5;
	static assert(!is(MyEA == MyEB));
	static assert(!is(MyEA == E));
	static assert(!is(MyEB == E));
	assert(ea.tarr.length == eb.tarr.length); // line 11
	assert(ea.tarr.length != eb.tarr.length); // line 12
}

struct T
{
	size_t i;
}

struct E
{
	size_t i;
	static T[] tarr;
}

alias MyEA = Typedef!(E, E.init, "A"); // line 26
alias MyEB = Typedef!(E, E.init, "B"); // line 27

/// --- code ends --- ///

Line 12 yields an assertion error, while line 11 does not.
This tells me, that despite the fact the types MyEA and MyEB are 
different they still share the static array, which would 
contradict the definition of static.

I suppose, the tricky thing is to tweak the init property of the 
typedefs in lines 26/27 to avoid this clash. How to manage this?
Feb 09 2018
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 02/09/2018 03:45 PM, Alex wrote:
 A question about Typedef usage:
 Say, I have the following circumstances

 /// --- code --- ///

 import std.typecons;

 void main()
 {
      MyEA ea;
      MyEB eb;
      ea.tarr.length = 5;
      static assert(!is(MyEA == MyEB));
      static assert(!is(MyEA == E));
      static assert(!is(MyEB == E));
      assert(ea.tarr.length == eb.tarr.length); // line 11
      assert(ea.tarr.length != eb.tarr.length); // line 12
You must have meant assert(ea.tarr.ptr != eb.tarr.ptr); // line 12 Indeed, .ptr are unexpectedly the same.
 }

 struct T
 {
      size_t i;
 }

 struct E
 {
      size_t i;
      static T[] tarr;
To save time to others, note that 'tarr' is a static member that ends up being shared by two Typedef instantiations.
 }

 alias MyEA = Typedef!(E, E.init, "A"); // line 26
 alias MyEB = Typedef!(E, E.init, "B"); // line 27

 /// --- code ends --- ///

 Line 12 yields an assertion error, while line 11 does not.
 This tells me, that despite the fact the types MyEA and MyEB are
 different they still share the static array, which would contradict the
 definition of static.

 I suppose, the tricky thing is to tweak the init property of the
 typedefs in lines 26/27 to avoid this clash. How to manage this?
Ali
Feb 09 2018
parent reply Alex <sascha.orlov gmail.com> writes:
On Saturday, 10 February 2018 at 01:01:39 UTC, Ali Çehreli wrote:
 On 02/09/2018 03:45 PM, Alex wrote:
 A question about Typedef usage:
 Say, I have the following circumstances

 /// --- code --- ///

 import std.typecons;

 void main()
 {
      MyEA ea;
      MyEB eb;
      ea.tarr.length = 5;
      static assert(!is(MyEA == MyEB));
      static assert(!is(MyEA == E));
      static assert(!is(MyEB == E));
      assert(ea.tarr.length == eb.tarr.length); // line 11
      assert(ea.tarr.length != eb.tarr.length); // line 12
You must have meant assert(ea.tarr.ptr != eb.tarr.ptr); // line 12 Indeed, .ptr are unexpectedly the same.
Yes... this is a more precise comparison :)
 }

 struct T
 {
      size_t i;
 }

 struct E
 {
      size_t i;
      static T[] tarr;
To save time to others, note that 'tarr' is a static member that ends up being shared by two Typedef instantiations.
Yup. They are shared by two Typedef instantiations with different cookies. So... The question is two-fold: Would it help to alter the init value of the Typedef? If yes, how to alter it? If no, is this a bug?
 }

 alias MyEA = Typedef!(E, E.init, "A"); // line 26
 alias MyEB = Typedef!(E, E.init, "B"); // line 27

 /// --- code ends --- ///

 Line 12 yields an assertion error, while line 11 does not.
 This tells me, that despite the fact the types MyEA and MyEB
are
 different they still share the static array, which would
contradict the
 definition of static.

 I suppose, the tricky thing is to tweak the init property of
the
 typedefs in lines 26/27 to avoid this clash. How to manage
this? Ali
Feb 09 2018
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 02/09/2018 05:14 PM, Alex wrote:

 struct E
 {
      size_t i;
      static T[] tarr;
To save time to others, note that 'tarr' is a static member that ends up being shared by two Typedef instantiations.
Yup. They are shared by two Typedef instantiations with different
cookies.
 So...
 The question is two-fold:
 Would it help to alter the init value of the Typedef?
 If yes, how to alter it?
 If no, is this a bug?
I think this is a limitation of Typedef. I can't see a way out (at least one that can support any type E). It should be possible if you can modify E but I can't work something out now. Ali
Feb 09 2018
parent reply Alex <sascha.orlov gmail.com> writes:
On Saturday, 10 February 2018 at 01:23:20 UTC, Ali Çehreli wrote:
 Yup. They are shared by two Typedef instantiations with
different cookies.
 So...
 The question is two-fold:
 Would it help to alter the init value of the Typedef?
 If yes, how to alter it?
 If no, is this a bug?
I think this is a limitation of Typedef. I can't see a way out (at least one that can support any type E). It should be possible if you can modify E but I can't work something out now. Ali
Ah... From your hint I arrived at the following: Inside of the struct E I define more then one static array. Namely, one for each Typedef I plan to instantiate. The Typedefs have to be known at compile time, so the amount of them has to be known by me :) Then, during the initialization, I work only with one of the arrays of E, depending on the Typedef I use. During the work inside of E, the object will have to check, which array was initialized and cooperate with this array, as this is the only possibility. As a workaround this will work, I think... Thanks! Nevertheless, I would file a bug later on... all ideas are appreciated in the meanwhile.
Feb 09 2018
parent reply Alex <sascha.orlov gmail.com> writes:
On Saturday, 10 February 2018 at 02:08:50 UTC, Alex wrote:
 Inside of the struct E I define more then one static array. 
 Namely, one for each Typedef I plan to instantiate. The 
 Typedefs have to be known at compile time, so the amount of 
 them has to be known by me :)
 Then, during the initialization, I work only with one of the 
 arrays of E, depending on the Typedef I use.
 During the work inside of E, the object will have to check, 
 which array was initialized and cooperate with this array, as 
 this is the only possibility.
Hmm... No. As the arrays are shared all of them will be initialized for every Typedef and the last point (decision during the work inside of a single Typedef) can not be achieved. So... there has to be another indirection, which will be filled during (possibly static) initialization.
Feb 09 2018
parent reply Alex <sascha.orlov gmail.com> writes:
On Saturday, 10 February 2018 at 02:55:26 UTC, Alex wrote:

bug filed

https://issues.dlang.org/show_bug.cgi?id=18416
Feb 10 2018
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Sunday, 11 February 2018 at 01:32:52 UTC, Alex wrote:
 On Saturday, 10 February 2018 at 02:55:26 UTC, Alex wrote:

 bug filed

 https://issues.dlang.org/show_bug.cgi?id=18416
Basically, Typedef looks like this: struct Typedef(T) { T _payload; // Forward method calls, member access, etc, to _payload. } If T looks like this: struct T { static int[3] arr; void foo() { arr[0]++; } } How is Typedef supposed to wrap T.foo in such a way that it uses a different arr depending on whether it's called from the Typedef or from T? -- Simen
Feb 11 2018
parent reply Alex <sascha.orlov gmail.com> writes:
On Sunday, 11 February 2018 at 15:18:11 UTC, Simen Kjærås wrote:
 Basically, Typedef looks like this:

 struct Typedef(T) {
     T _payload;
     // Forward method calls, member access, etc, to _payload.
 }

 If T looks like this:

 struct T {
     static int[3] arr;
     void foo() { arr[0]++; }
 }

 How is Typedef supposed to wrap T.foo in such a way that it 
 uses a different arr depending on whether it's called from the 
 Typedef or from T?

 --
   Simen
In the same way as it is handled by this: /// --- code --- /// void main(){} static assert(T.arr.ptr != S.arr.ptr); struct T { static int[3] arr; void foo() { arr[0]++; } } struct S { static int[3] arr; void foo() { arr[0]++; } } /// --- code ends --- /// I understand that Typedef should be kept simple. But if it defines a type which is recognized as another type in respect to the underlying one, then I should be able to rely on independence of everything, like I would copy/paste the implementation. Or do I misinterpret something?
Feb 11 2018
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Sunday, 11 February 2018 at 19:33:23 UTC, Alex wrote:
 On Sunday, 11 February 2018 at 15:18:11 UTC, Simen Kjærås wrote:
 Basically, Typedef looks like this:

 struct Typedef(T) {
     T _payload;
     // Forward method calls, member access, etc, to _payload.
 }

 If T looks like this:

 struct T {
     static int[3] arr;
     void foo() { arr[0]++; }
 }

 How is Typedef supposed to wrap T.foo in such a way that it 
 uses a different arr depending on whether it's called from the 
 Typedef or from T?

 --
   Simen
In the same way as it is handled by this: /// --- code --- /// void main(){} static assert(T.arr.ptr != S.arr.ptr); struct T { static int[3] arr; void foo() { arr[0]++; } } struct S { static int[3] arr; void foo() { arr[0]++; } } /// --- code ends --- /// I understand that Typedef should be kept simple. But if it defines a type which is recognized as another type in respect to the underlying one, then I should be able to rely on independence of everything, like I would copy/paste the implementation. Or do I misinterpret something?
I agree that'd be nice. Sadly, it's not a reasonable expectation. :( A more extreme example: You have a compiled library, and some .di (header) files. In one of those files is this code: struct S { static int[] arr; void foo(); } Now how should Typedef go about making foo() do the right thing? Even with compiler support, this is impossible - the source of S.foo is simply not available, and it doesn't take the address of the static data as a parameter anywhere. -- Simen
Feb 12 2018
parent reply Alex <sascha.orlov gmail.com> writes:
On Monday, 12 February 2018 at 08:51:14 UTC, Simen Kjærås wrote:
 I agree that'd be nice. Sadly, it's not a reasonable 
 expectation. :(
:)
 A more extreme example: You have a compiled library, and some 
 .di (header) files. In one of those files is this code:

 struct S {
     static int[] arr;
     void foo();
 }

 Now how should Typedef go about making foo() do the right 
 thing? Even with compiler support, this is impossible - the 
 source of S.foo is simply not available, and it doesn't take 
 the address of the static data as a parameter anywhere.

 --
   Simen
But isn't foo scoped somehow? I mean, if foo is part of a type, then either you have to write s.foo, or S.foo. And if the address of a static belongs to a type, shouldn't this resolve the ambiguities?
Feb 12 2018
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Monday, 12 February 2018 at 09:10:52 UTC, Alex wrote:

 A more extreme example: You have a compiled library, and some 
 .di (header) files. In one of those files is this code:

 struct S {
     static int[] arr;
     void foo();
 }

 Now how should Typedef go about making foo() do the right 
 thing? Even with compiler support, this is impossible - the 
 source of S.foo is simply not available, and it doesn't take 
 the address of the static data as a parameter anywhere.
But isn't foo scoped somehow? I mean, if foo is part of a type, then either you have to write s.foo, or S.foo. And if the address of a static belongs to a type, shouldn't this resolve the ambiguities?
Not really, since D doesn't have a concept of an address associated with a type, only with instances of it. So when you use a static array, the address is hard-coded. -- Simen
Feb 12 2018
parent reply Alex <sascha.orlov gmail.com> writes:
On Monday, 12 February 2018 at 09:37:56 UTC, Simen Kjærås wrote:
 Not really, since D doesn't have a concept of an address 
 associated with a type, only with instances of it. So when you 
 use a static array, the address is hard-coded.

 --
   Simen
Ok... so the query on ptr on a static is not to be allowed? And the fact I can perform it is a bug?
Feb 12 2018
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Monday, 12 February 2018 at 09:58:13 UTC, Alex wrote:
 On Monday, 12 February 2018 at 09:37:56 UTC, Simen Kjærås wrote:
 Not really, since D doesn't have a concept of an address 
 associated with a type, only with instances of it. So when you 
 use a static array, the address is hard-coded.

 --
   Simen
Ok... so the query on ptr on a static is not to be allowed? And the fact I can perform it is a bug?
I'm sorry, I was apparently unclear. When I said 'static array' above, I meant 'static member'. Since we've been using arrays in our examples, there could be conflation of ideas there. The fact that you can access (and even modify) the static member's .ptr property (as in S.arr.ptr = [1,2,3].ptr), doesn't mean you can change the address of the array itself (the static data in S). This can be shown by writeln(&S.arr), which will not change. When you call a static method, as the one in this example: struct S { static int[] arr; static void foo() { arr[0]++; } } unittest { S.foo(); } No pointer is being passed to foo - it's exactly equivalent to this code: module test; int[] arr; void foo() { arr[0]++; } unittest { foo(); } Likewise, when a non-static method modifies a static member, it doesn't need to look up the address of the static member - its address is hard-coded. As an example, try this: struct S { static int n1; int n2; } unittest { import std.stdio; S s1; S s2; // These should be equal: writeln(&s1.n1); writeln(&s2.n1); // These should be different: writeln(&s1.n2); writeln(&s2.n2); } -- Simen
Feb 12 2018
parent Alex <sascha.orlov gmail.com> writes:
On Monday, 12 February 2018 at 11:25:40 UTC, Simen Kjærås wrote:
 I'm sorry, I was apparently unclear. When I said 'static array' 
 above, I meant 'static member'.

 Since we've been using arrays in our examples, there could be 
 conflation of ideas there. The fact that you can access (and 
 even modify) the static member's .ptr property (as in S.arr.ptr 
 = [1,2,3].ptr), doesn't mean you can change the address of the 
 array itself (the static data in S). This can be shown by 
 writeln(&S.arr), which will not change.

 When you call a static method, as the one in this example:

 struct S {
     static int[] arr;
     static void foo() { arr[0]++; }
 }

 unittest {
     S.foo();
 }

 No pointer is being passed to foo - it's exactly equivalent to 
 this code:

 module test;
 int[] arr;
/* yeah... just saw it in the AST-output. However, even if this is rewrited, I cannot reference to the arr as .arr I have to write S.arr */
 void foo() { arr[0]++; }

 unittest {
     foo();
 }

 Likewise, when a non-static method modifies a static member, it 
 doesn't need to look up the address of the static member - its 
 address is hard-coded.

 As an example, try this:

 struct S {
    static int n1;
    int n2;
 }

 unittest {
     import std.stdio;
     S s1;
     S s2;
     // These should be equal:
     writeln(&s1.n1);
     writeln(&s2.n1);
// sure, this is because the type of s1 and s2 is the same.
     // These should be different:
     writeln(&s1.n2);
     writeln(&s2.n2);
 }

 --
   Simen
It is even worse, then I thought. Let us simplify our examples a little bit: /// --- code --- /// import std.typecons; struct S { static int i; } alias T = Typedef!S; struct U { static int i; } static assert(is(typeof(S.i) == int)); static assert(is(typeof(T.i) == void)); void main() { S.i = 42; assert(U.i != S.i); } /// --- code ends --- /// So... I'm starting to see your point now, I think. What I expected from the Typedef was an ability to "templatify" a type ad-hoc. I wanted to say, well, I have a dedicated type and some different manifestations of it which I define by a Typedef. After that, I could write some functions for different Typedefs being sure, that only the right types would be addressed by the functions, as specified in the example of Typedef. However, as I can see, the definition of "static" takes advantage and heaves the according member away. Not only away from instances, but also from types (which is yeah... well... lets call it unexpected :) ) However, if I define another type manually, this is not the case. So, static behaves differently on Typedefs, then on different types, defined manually.
Feb 12 2018