Thread overview
Cleverness of the compiler
Nov 25, 2013
Namespace
Nov 25, 2013
growler
Nov 25, 2013
growler
Nov 25, 2013
Namespace
Nov 25, 2013
Shammah Chancellor
Nov 25, 2013
Namespace
Nov 25, 2013
Shammah Chancellor
November 25, 2013
I love this feature, but I'm unsure how it works. Can someone explain me, how the compiler deduce that he should read 4 bytes for each index (the 'at' function)? The type is void*, not int*.

----
import std.stdio;
import core.stdc.stdlib : calloc, realloc, free;
import core.stdc.string : memcpy;

struct Tarray {
	void* ptr;
	size_t length;
	size_t capacity;

	~this() {
		.free(this.ptr);
		this.ptr = null;
	}
}

void push(T)(ref Tarray arr, T elem) {
	if (arr.length == arr.capacity)
		arr.reserve(T.sizeof);

	memcpy(&arr.ptr[arr.length++], &elem, T.sizeof);
}

void reserve(ref Tarray arr, size_t typeOf, size_t cap = 0) {
	if (arr.capacity != 0) {
		arr.capacity = cap == 0 ? (arr.capacity * 2) + 1 : arr.capacity + cap;
		arr.ptr = .realloc(arr.ptr, arr.capacity * typeOf);
	} else {
		arr.capacity = cap == 0 ? 3 : cap;
		arr.ptr = .calloc(arr.capacity, typeOf);
	}
}

void at(T)(ref Tarray arr, size_t index, T* elem) {
	if (index >= arr.length || elem is null)
		return;

	memcpy(elem, &arr.ptr[index], T.sizeof);
}

void main() {
	Tarray arr;
	arr.push(42);
	int a;
	arr.at(0, &a);
	writeln(a, "::", arr.length, "::", arr.capacity);
	arr.push(23);
	arr.at(1, &a);
	writeln(a, "::", arr.length, "::", arr.capacity);
	arr.push(1337);
	arr.at(2, &a);
	writeln(a, "::", arr.length, "::", arr.capacity);
	arr.push(ushort.max + 1);
	arr.at(3, &a);
	writeln(a, "::", arr.length, "::", arr.capacity);
}
----
November 25, 2013
On Monday, 25 November 2013 at 00:08:52 UTC, Namespace wrote:
> I love this feature, but I'm unsure how it works. Can someone explain me, how the compiler deduce that he should read 4 bytes for each index (the 'at' function)? The type is void*, not int*.
>
> ----
> import std.stdio;
> import core.stdc.stdlib : calloc, realloc, free;
> import core.stdc.string : memcpy;
>
> struct Tarray {
> 	void* ptr;
> 	size_t length;
> 	size_t capacity;
>
> 	~this() {
> 		.free(this.ptr);
> 		this.ptr = null;
> 	}
> }
>
> void push(T)(ref Tarray arr, T elem) {
> 	if (arr.length == arr.capacity)
> 		arr.reserve(T.sizeof);
>
> 	memcpy(&arr.ptr[arr.length++], &elem, T.sizeof);
> }
>
> void reserve(ref Tarray arr, size_t typeOf, size_t cap = 0) {
> 	if (arr.capacity != 0) {
> 		arr.capacity = cap == 0 ? (arr.capacity * 2) + 1 : arr.capacity + cap;
> 		arr.ptr = .realloc(arr.ptr, arr.capacity * typeOf);
> 	} else {
> 		arr.capacity = cap == 0 ? 3 : cap;
> 		arr.ptr = .calloc(arr.capacity, typeOf);
> 	}
> }
>
> void at(T)(ref Tarray arr, size_t index, T* elem) {
> 	if (index >= arr.length || elem is null)
> 		return;
>
> 	memcpy(elem, &arr.ptr[index], T.sizeof);
> }
>
> void main() {
> 	Tarray arr;
> 	arr.push(42);
> 	int a;
> 	arr.at(0, &a);
> 	writeln(a, "::", arr.length, "::", arr.capacity);
> 	arr.push(23);
> 	arr.at(1, &a);
> 	writeln(a, "::", arr.length, "::", arr.capacity);
> 	arr.push(1337);
> 	arr.at(2, &a);
> 	writeln(a, "::", arr.length, "::", arr.capacity);
> 	arr.push(ushort.max + 1);
> 	arr.at(3, &a);
> 	writeln(a, "::", arr.length, "::", arr.capacity);
> }
> ----

All the calls to 'at' are using T=int (implied), as far as I can tell.
...
int a;
...
arr.at(0, &a); // tyepof(a) is int so in 'at' T.sizeof = 4.

Is this the call to 'at' you're referring to?

Cheers

November 25, 2013
On Monday, 25 November 2013 at 00:51:47 UTC, growler wrote:
> On Monday, 25 November 2013 at 00:08:52 UTC, Namespace wrote:
>> I love this feature, but I'm unsure how it works. Can someone explain me, how the compiler deduce that he should read 4 bytes for each index (the 'at' function)? The type is void*, not int*.
>>
>> ----
>> import std.stdio;
>> import core.stdc.stdlib : calloc, realloc, free;
>> import core.stdc.string : memcpy;
>>
>> struct Tarray {
>> 	void* ptr;
>> 	size_t length;
>> 	size_t capacity;
>>
>> 	~this() {
>> 		.free(this.ptr);
>> 		this.ptr = null;
>> 	}
>> }
>>
>> void push(T)(ref Tarray arr, T elem) {
>> 	if (arr.length == arr.capacity)
>> 		arr.reserve(T.sizeof);
>>
>> 	memcpy(&arr.ptr[arr.length++], &elem, T.sizeof);
>> }
>>
>> void reserve(ref Tarray arr, size_t typeOf, size_t cap = 0) {
>> 	if (arr.capacity != 0) {
>> 		arr.capacity = cap == 0 ? (arr.capacity * 2) + 1 : arr.capacity + cap;
>> 		arr.ptr = .realloc(arr.ptr, arr.capacity * typeOf);
>> 	} else {
>> 		arr.capacity = cap == 0 ? 3 : cap;
>> 		arr.ptr = .calloc(arr.capacity, typeOf);
>> 	}
>> }
>>
>> void at(T)(ref Tarray arr, size_t index, T* elem) {
>> 	if (index >= arr.length || elem is null)
>> 		return;
>>
>> 	memcpy(elem, &arr.ptr[index], T.sizeof);
>> }
>>
>> void main() {
>> 	Tarray arr;
>> 	arr.push(42);
>> 	int a;
>> 	arr.at(0, &a);
>> 	writeln(a, "::", arr.length, "::", arr.capacity);
>> 	arr.push(23);
>> 	arr.at(1, &a);
>> 	writeln(a, "::", arr.length, "::", arr.capacity);
>> 	arr.push(1337);
>> 	arr.at(2, &a);
>> 	writeln(a, "::", arr.length, "::", arr.capacity);
>> 	arr.push(ushort.max + 1);
>> 	arr.at(3, &a);
>> 	writeln(a, "::", arr.length, "::", arr.capacity);
>> }
>> ----
>
> All the calls to 'at' are using T=int (implied), as far as I can tell.
> ...
> int a;
> ...
> arr.at(0, &a); // tyepof(a) is int so in 'at' T.sizeof = 4.
>
> Is this the call to 'at' you're referring to?
>
> Cheers

Sorry, T=int should be T=int*
November 25, 2013
I meant the function:
----
void at(T)(ref Tarray arr, size_t index, T* elem) {
 	if (index >= arr.length || elem is null)
 		return;

 	memcpy(elem, &arr.ptr[index], T.sizeof);
}
----
-> arr.ptr[index]
November 25, 2013
On 2013-11-25 00:08:50 +0000, Namespace said:

> I love this feature, but I'm unsure how it works. Can someone explain me, how the compiler deduce that he should read 4 bytes for each index (the 'at' function)? The type is void*, not int*.

It doesn't work.  That code is buggy.  It's overwriting previous elements with new ones.   Indexing a void* only moves up by 1 byte.

void main() {
 pragma(msg, void.sizeof)
	Tarray arr;
	arr.push(42);
	int a;
	arr.at(0, &a);
	writeln(a, "::", arr.length, "::", arr.capacity);
	arr.push(23);
	arr.at(1, &a);
	writeln(a, "::", arr.length, "::", arr.capacity);
	arr.push(1337);
	arr.at(2, &a);
	writeln(a, "::", arr.length, "::", arr.capacity);
 writeln(arr.capacity);
	arr.push(ushort.max); //Write a ushort to test.
	arr.at(3, &a); //Only works because we're on a little endian platform
	writeln(a, "::", arr.length, "::", arr.capacity);
	arr.at(2, &a);
	writeln(a, "::", arr.length, "::", arr.capacity);
}

November 25, 2013
On Monday, 25 November 2013 at 03:13:48 UTC, Shammah Chancellor wrote:
> On 2013-11-25 00:08:50 +0000, Namespace said:
>
>> I love this feature, but I'm unsure how it works. Can someone explain me, how the compiler deduce that he should read 4 bytes for each index (the 'at' function)? The type is void*, not int*.
>
> It doesn't work.  That code is buggy.  It's overwriting previous elements with new ones.   Indexing a void* only moves up by 1 byte.
>
> void main() {
>  pragma(msg, void.sizeof)
> 	Tarray arr;
> 	arr.push(42);
> 	int a;
> 	arr.at(0, &a);
> 	writeln(a, "::", arr.length, "::", arr.capacity);
> 	arr.push(23);
> 	arr.at(1, &a);
> 	writeln(a, "::", arr.length, "::", arr.capacity);
> 	arr.push(1337);
> 	arr.at(2, &a);
> 	writeln(a, "::", arr.length, "::", arr.capacity);
>  writeln(arr.capacity);
> 	arr.push(ushort.max); //Write a ushort to test.
> 	arr.at(3, &a); //Only works because we're on a little endian platform
> 	writeln(a, "::", arr.length, "::", arr.capacity);
> 	arr.at(2, &a);
> 	writeln(a, "::", arr.length, "::", arr.capacity);
> }

Ok, that calms me down. Thought I had missed something.
November 25, 2013
On 2013-11-25 10:34:39 +0000, Namespace said:

> On Monday, 25 November 2013 at 03:13:48 UTC, Shammah Chancellor wrote:
>> On 2013-11-25 00:08:50 +0000, Namespace said:
>> 
>>> I love this feature, but I'm unsure how it works. Can someone explain me, how the compiler deduce that he should read 4 bytes for each index (the 'at' function)? The type is void*, not int*.
>> 
>> It doesn't work.  That code is buggy.  It's overwriting previous elements with new ones.   Indexing a void* only moves up by 1 byte.
>> 
>> void main() {
>> pragma(msg, void.sizeof)
>> 	Tarray arr;
>> 	arr.push(42);
>> 	int a;
>> 	arr.at(0, &a);
>> 	writeln(a, "::", arr.length, "::", arr.capacity);
>> 	arr.push(23);
>> 	arr.at(1, &a);
>> 	writeln(a, "::", arr.length, "::", arr.capacity);
>> 	arr.push(1337);
>> 	arr.at(2, &a);
>> 	writeln(a, "::", arr.length, "::", arr.capacity);
>> writeln(arr.capacity);
>> 	arr.push(ushort.max); //Write a ushort to test.
>> 	arr.at(3, &a); //Only works because we're on a little endian platform
>> 	writeln(a, "::", arr.length, "::", arr.capacity);
>> 	arr.at(2, &a);
>> 	writeln(a, "::", arr.length, "::", arr.capacity);
>> }
> 
> Ok, that calms me down. Thought I had missed something.

Yeah.  You had me confused for a bit too.  :)  Couldn't figure out why Ushort.max was being re-read correctly.  I'm use to big-endian platforms.   It certainly would have been miraculous if the compiler knew what kind of elements you put in the array to be able to index to the right location.

-Shammah