Thread overview | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
April 02, 2013 Implicit dereferencing | ||||
---|---|---|---|---|
| ||||
Consider this (non-portable) code: { int[2] a; int[2] b; int[2] *c; c = &a; c[0] = 7; assert(a[0] == 7); // OK, as expected c[1] = 42; assert(b[0] == 42); // do we really want this semantics? } Because indexing c automatically dereferences the pointer, "c[0] = 7" assigns to a[0], as expected. This is the same as "(*c)[0] = 7", which is useful. But "c[1] = 42" is equivalent to "(*(c+1)) = 42", which might not be obvious. In this case it ends writing to b[0]. Why not make the semantics be ((*c)+1) = 42, instead? Wouldn't that be more useful? Also, why don't we have pointer dereferencing for AAs? { struct Foo { int f; } // given that this works... Foo x; Foo *y = &x; x.f = 1; y.f = 2; // implicit dereferencing // coudln't this be made to work?... int[string] c; int[string] *d; d = &c; c["foo"] = 1; d["foo"] = 2; // fails, dereferencing must be explicit (*d)["foo"] = 2; // works, of course } |
April 02, 2013 Re: Implicit dereferencing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Luís Marques | On Mon, 01 Apr 2013 20:07:30 -0400, Luís Marques <luismarques@gmail.com> wrote:
> Consider this (non-portable) code:
>
> {
> int[2] a;
> int[2] b;
> int[2] *c;
> c = &a;
>
> c[0] = 7;
> assert(a[0] == 7); // OK, as expected
>
> c[1] = 42;
> assert(b[0] == 42); // do we really want this semantics?
> }
>
> Because indexing c automatically dereferences the pointer, "c[0] = 7" assigns to a[0], as expected. This is the same as "(*c)[0] = 7", which is useful.
This is not what is happening. If you add:
writeln(a);
you will see:
7, 7
You see, indexing does NOT dereference the pointer, it's an index for that pointer. c[0] means *(c + 0). A pointer is essentially an unchecked slice, with undefined length. This is how it works in C also.
c[1] is the same as *(c + 1), completely consistent (and also sets b to 42, 42)
-Steve
|
April 02, 2013 Re: Implicit dereferencing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Tuesday, 2 April 2013 at 02:52:48 UTC, Steven Schveighoffer wrote:
> You see, indexing does NOT dereference the pointer, it's an index for that pointer. c[0] means *(c + 0). A pointer is essentially an unchecked slice, with undefined length. This is how it works in C also.
>
> c[1] is the same as *(c + 1), completely consistent (and also sets b to 42, 42)
OK, I think I see where I went astray. I was a case of bad induction from a few tests :-)
So, I guess what is happening is the following, right?
int[2] a;
int[2] *c;
c = &a;
c[0] = 7; // same thing as below
a = 7; // same thing above
(cast(int*) c)[0] = 7; // but different from this
I verified that c is a pointer to a.ptr, I guess what I didn't consider is that because c points to int[2], the assignment becomes the same as a = 7, and not a[0] = 7.
Still, what do you think of the struct vs AA automatic pointer dereferencing?
|
April 02, 2013 Re: Implicit dereferencing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Luís Marques | On Mon, 01 Apr 2013 23:23:49 -0400, Luís Marques <luismarques@gmail.com> wrote:
> On Tuesday, 2 April 2013 at 02:52:48 UTC, Steven Schveighoffer wrote:
>> You see, indexing does NOT dereference the pointer, it's an index for that pointer. c[0] means *(c + 0). A pointer is essentially an unchecked slice, with undefined length. This is how it works in C also.
>>
>> c[1] is the same as *(c + 1), completely consistent (and also sets b to 42, 42)
>
> OK, I think I see where I went astray. I was a case of bad induction from a few tests :-)
> So, I guess what is happening is the following, right?
>
> int[2] a;
> int[2] *c;
> c = &a;
>
> c[0] = 7; // same thing as below
> a = 7; // same thing above
>
> (cast(int*) c)[0] = 7; // but different from this
>
> I verified that c is a pointer to a.ptr, I guess what I didn't consider is that because c points to int[2], the assignment becomes the same as a = 7, and not a[0] = 7.
>
> Still, what do you think of the struct vs AA automatic pointer dereferencing?
a pointer defines indexing. To have implicit dereferencing for indexing on any pointer type would not be good.
For example:
string[int][2] aas;
string[int] *aaptr = aas.ptr;
auto x = aaptr[1];
If x is not a pointer to aas[1], then you would have to access it using *(aaptr + 1), which would suck. Plus it would make pointers to indexed types inconsistent with all pointers to types that don't define indexing.
-Steve
|
April 02, 2013 Re: Implicit dereferencing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Tuesday, 2 April 2013 at 03:36:04 UTC, Steven Schveighoffer wrote:
> Plus it would make pointers to indexed types inconsistent with all pointers to types that don't define indexing.
Yeah, of course you're right...
Taking a step back, I think the problem here is that I wanted to have multiple references to the contents of an associative array. With a regular array, while you can't *store* a reference to the array "header" (ptr, length) without using pointers, you can have another array pointing to the same content. I guess you can't do something like that with an associative array, right? (mm... that kinda breaks the ideia of an AA as a more general array)
|
April 02, 2013 Re: Implicit dereferencing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Luís Marques | On Tue, 02 Apr 2013 00:12:37 -0400, Luís Marques <luismarques@gmail.com> wrote:
> On Tuesday, 2 April 2013 at 03:36:04 UTC, Steven Schveighoffer wrote:
>> Plus it would make pointers to indexed types inconsistent with all pointers to types that don't define indexing.
>
> Yeah, of course you're right...
>
> Taking a step back, I think the problem here is that I wanted to have multiple references to the contents of an associative array. With a regular array, while you can't *store* a reference to the array "header" (ptr, length) without using pointers, you can have another array pointing to the same content. I guess you can't do something like that with an associative array, right? (mm... that kinda breaks the ideia of an AA as a more general array)
An AA is a pImpl, meaning a pointer to implementation. A simple copy is an alias.
HOWEVER, there is one horrible caveat. You must have assigned an element at least once in order to alias:
int[int] aa1; // pImpl initialized to null
int[int] aa2; // pImpl initialized to null
aa2 = aa1; // just assigns null to aa2
aa1[1] = 1; // allocates and initializes
aa2[1] = 2; // allocates and initializes separate instance
assert(aa1[1] == 1 && aa2[1] == 2);
aa2 = aa1; // NOW they are aliased
assert(aa2[1] == 1);
aa2[1] = 2;
assert(aa1[1] == 2);
-Steve
|
April 02, 2013 Re: Implicit dereferencing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Luís Marques Attachments:
| 2013/4/2 <luismarques@gmail.com>"@puremagic.com <"\"Luís".Marques"> > On Tuesday, 2 April 2013 at 02:52:48 UTC, Steven Schveighoffer wrote: > >> You see, indexing does NOT dereference the pointer, it's an index for that pointer. c[0] means *(c + 0). A pointer is essentially an unchecked slice, with undefined length. This is how it works in C also. >> >> c[1] is the same as *(c + 1), completely consistent (and also sets b to >> 42, 42) >> > > OK, I think I see where I went astray. I was a case of bad induction from > a few tests :-) > So, I guess what is happening is the following, right? > > int[2] a; > > int[2] *c; > c = &a; > > c[0] = 7; // same thing as below > a = 7; // same thing above > These are doing that element-wise assignment. c[0][] = 7; // same as c[0][0] = c[0][1] = 7; a[] = 7; // same as a[0] = a[1] = 7; As a side note: >From 2.063, lack of [] for array operation would be warned with -w switch. So, there is no "implicit dereferencing". Kenji Hara |
April 02, 2013 Re: Implicit dereferencing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Luís Marques | On 04/01/2013 05:07 PM, "Luís Marques" <luismarques@gmail.com>" wrote:> Consider this (non-portable) code: Perhaps it is obvious to everyone but allow me still: It is not non-portable code, but its behavior is undefined. > { > int[2] a; > int[2] b; > int[2] *c; c is a pointer to a single int[2]. > c = &a; > > c[0] = 7; > assert(a[0] == 7); // OK, as expected > > c[1] = 42; > assert(b[0] == 42); // do we really want this semantics? A manifestation of undefined behavior. > But "c[1] = 42" is equivalent to "(*(c+1)) = 42", which might not be > obvious. In this case it ends writing to b[0]. It seems to be so but it is not defined to work that way. Ali |
April 02, 2013 Re: Implicit dereferencing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Tuesday, 2 April 2013 at 04:48:54 UTC, Steven Schveighoffer wrote: > HOWEVER, there is one horrible caveat. You must have assigned an element at least once in order to alias: Thanks Steve, I had already looked into that, but then got dense and I was treating the AA as having value semantics. I don't need the pointer. Is there a way to ensure the AA is initialized as not null, besides adding and removing an element? (I couldn't use a literal) Is the null initializer a consequence of the current opaque implementation, or just of the reference semantics? (I saw on the wiki that there was a desire to use a less opaque implementation. I think the AA ABI should be documented, like the other arrays) Slightly related, it doesn't seem very reasonable to me that the get method of AAs is not safe: @safe: void main() { int[string] x; x.get("b", 0); } Error: safe function 'D main' cannot call system function 'object.AssociativeArray!(string, int).AssociativeArray.get' |
April 02, 2013 Re: Implicit dereferencing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Luís Marques | On Tue, 02 Apr 2013 01:59:13 -0400, Luís Marques <luismarques@gmail.com> wrote: > On Tuesday, 2 April 2013 at 04:48:54 UTC, Steven Schveighoffer wrote: >> HOWEVER, there is one horrible caveat. You must have assigned an element at least once in order to alias: > > Thanks Steve, I had already looked into that, but then got dense and I was treating the AA as having value semantics. I don't need the pointer. > > Is there a way to ensure the AA is initialized as not null, besides adding and removing an element? (I couldn't use a literal) Not sure. Looking... can't find anything. So no. There really should be. > Is the null initializer a consequence of the current opaque implementation, or just of the reference semantics? (I saw on the wiki that there was a desire to use a less opaque implementation. I think the AA ABI should be documented, like the other arrays) The AA ABI is defined by druntime in object.di. AA's are currently a hybrid between a compiler-magic type and a template. The plan is to make it completely a template, that is, the compiler simply always treats T[U] as AssociativeArray!(T, U). The null initializer is a result of D's policy on struct initialization. Default constructors are not allowed on structs, they must be able to be initialized with static data when declared without a constructor. > Slightly related, it doesn't seem very reasonable to me that the get method of AAs is not safe: > > @safe: > > void main() > { > int[string] x; > x.get("b", 0); > } > > Error: safe function 'D main' cannot call system function 'object.AssociativeArray!(string, int).AssociativeArray.get' Someone (can't remember who) went through a lot of effort to try and re-implement AAs as object.AssociativeArray and ran into a lot of problems with things like @safe and pure. It really should be @safe, but I don't know if there was a specific reason why it wasn't tagged that way. -Steve |
Copyright © 1999-2021 by the D Language Foundation