www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - scoped allocations

reply "Namespace" <rswhite4 googlemail.com> writes:
First of all: I apologize for my bad english.

In the last few weeks I searched for a way to allocate nicely 
temporary buffer of unknown lengths. Since D has no VLA's like C 
(and alloca works only partial for some strange reason: 
https://d.puremagic.com/issues/show_bug.cgi?id=3753), malloc / 
free was obiously the solution, but you have only two choices:
1. Allocate them (and maybe slice them) and free the ptr at the 
end of the scope (or use scope(exit)). But: Make sure that your 
array has not been taken over by the GC, because you made a 
mistake [1].

Example:
----
int* ptr = .malloc(N * int.sizeof); /// N is a runtime lengths
int[] arr = ptr[0 .. N]; /// optional slice
scope(exit) .free(ptr); // or more dangerous: free(arr.ptr);
----

Fail example:
----
int[] arr = (cast(T*) .malloc(N * int.sizeof))[0 .. N];
arr ~= 42; /// <-- [1]
scope(exit) .free(arr.ptr);
/*
  * Because you don't thought about that 'arr' is a full filled 
array and nothing you can append to,
  * the GC takes over the memory and you don't have a chance to 
get the right C pointer to free your C memory: memory leak [A 
-nogc flag would maybe help here]
  */
----

2. Use a struct.
----
struct Temp(T) {
     T* ptr;
	
	alias ptr this;
	
	this(size_t N) {
		this.arr = cast(T*) .malloc(N * T.sizeof);
	}
	
	~this() {
		.free(this.ptr);
	}
}

Temp!int arr = Temp!int(512); /// arr is destroyed at the end of 
the scope
----

But that does not look very nice (especially because Temp!int 
does not indicate that it holds an array).
Something like:
----
int[] arr = new int[512];
----
Looks nicer and more intuitive. But: it is controlled by the GC. 
If you want to destroy it, you have to call GC.free manually. So 
we have the same problems/options to destroy it as with 
malloc/free.

During the std.allocator thread I had made ​​the suggestion to 
use this allocators for such temporary arrays (See also Andrej 
Mitrovic post: 
http://forum.dlang.org/thread/l4btsk$5u8$1 digitalmars.com?page=2#post-mailman.2478.1382651916.1719.digitalmars-d:40puremagic.com)
For example:
----
Mallocator m;
with (m) {
	int[] arr;
	arr ~= 42; /// use Mallocator m for memory allocation [Question 
is: when it is freed? At the end of the scope? Maybe we need a 
(Temp|Scope)Alloc]
}
----

Or, even better.
----
with (Mallocator) {
	int[] arr;
	arr ~= 42; /// use Mallocator for memory allocation [Same 
question]
}
----

Yet another idea of mine was:
----
int[] arr;
arr.use(Mallocator);
----

Or something better: "use" blocks:
----
use (Mallocator) {
	int[] arr;
}

arr ~= 42; /// use Mallocator for memory allocation [Same 
question]
----

But both would bring the introduction of a new keywords with them.

Over the last few days I had a few other ideas, besides the nice 
stuff from std.allocator.
One of them was to use scope:
----
scope int[] arr = new int[42]; /// arr is freed at the end of the 
scope
----

The compiler could rewrite this e.g. with:
----
struct scoped(A : T[], T) {
	T[] arr;
	
	~this() {
		GC.free(arr.ptr);
	}
}

scoped!(int[]) arr = new int[42]; /// arr is freed as far as 
scoped's DTor is called.
----
In this case we have no need to introduce a new keyword: scope is 
already there.

Or, to use UDA's (but they must be improved):
----
struct temp {
	void* ptr;
	
	~this() {
	    GC.free(this.ptr);
	}
}

 temp int[] arr = new int[42]; /// arr is freed as far as temp's 
DTor is called ( -> if temp leaves the scope)
----

The code above would be simply rewritten to:
----
int[] arr = new int[42];
temp __udatmp = temp(arr.ptr); /// temp's DTor free the whole 
array [Need compiler magic]
----

Another suggestion would be DIP 46 (http://wiki.dlang.org/DIP46) 
with a few improvements:
----
gc_push(Mallocator);

int[] arr;
arr ~= 42;

gc_pop(); /// arr is freed (maybe it would make sense if gc_pop 
would be automatically inserted at the end of the scope).
----

That's it so far. Any thoughts/further suggestions?
Nov 26 2013
next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Tuesday, 26 November 2013 at 23:33:59 UTC, Namespace wrote:
 First of all: I apologize for my bad english.

 In the last few weeks I searched for a way to allocate nicely 
 temporary buffer of unknown lengths.
Should be "[...] of unknown lengths at compile time".
Nov 26 2013
prev sibling next sibling parent reply "Andrea Fontana" <nospam example.com> writes:
On Tuesday, 26 November 2013 at 23:33:59 UTC, Namespace wrote:
 Temp!int arr = Temp!int(512); /// arr is destroyed at the end 
 of the scope
 ----

 But that does not look very nice (especially because Temp!int 
 does not indicate that it holds an array).
If you call it something other than "Temp" maybe it sounds better. auto arr = ScopedArray!int(512); Makes sense for me.
Nov 27 2013
next sibling parent =?UTF-8?B?U8O2bmtlIEx1ZHdpZw==?= <sludwig outerproduct.org> writes:
Am 27.11.2013 09:54, schrieb Andrea Fontana:
 On Tuesday, 26 November 2013 at 23:33:59 UTC, Namespace wrote:
 Temp!int arr = Temp!int(512); /// arr is destroyed at the end of the
 scope
 ----

 But that does not look very nice (especially because Temp!int does not
 indicate that it holds an array).
If you call it something other than "Temp" maybe it sounds better. auto arr = ScopedArray!int(512); Makes sense for me.
+ (I think that) std.container.Array does exactly the desired thing (haven't used it so far).
Nov 27 2013
prev sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Wednesday, 27 November 2013 at 08:55:01 UTC, Andrea Fontana 
wrote:
 On Tuesday, 26 November 2013 at 23:33:59 UTC, Namespace wrote:
 Temp!int arr = Temp!int(512); /// arr is destroyed at the end 
 of the scope
 ----

 But that does not look very nice (especially because Temp!int 
 does not indicate that it holds an array).
If you call it something other than "Temp" maybe it sounds better. auto arr = ScopedArray!int(512); Makes sense for me.
It was just an example, but you're right. But even auto arr = ScopedArray!int(512); doesn't look very nice. Some syntax sugar like e.g. scope int[] arr = new int[512]; would be much nicer + more intuitive. And with some of the other examples, like the DIP 46 variant, D could keep his promise to work even without a GC. Currently you can disable it, but then you have very little benefit of the most basic things, such as strings, arrays, etc. With an exchangeable GC this would be different.
Nov 27 2013
prev sibling parent reply Denis Shelomovskij <verylonglogin.reg gmail.com> writes:
27.11.2013 3:33, Namespace пишет:
 First of all: I apologize for my bad english.

 In the last few weeks I searched for a way to allocate nicely temporary
 buffer of unknown lengths.
You can use `unstd.memory.allocation.tempAlloc` [1]. Also there is `unstd.c.string.tempCString` [2] for common case of temporary C strings. [1] http://denis-sh.bitbucket.org/unstandard/unstd.memory.allocation.html#tempAlloc [2] http://denis-sh.bitbucket.org/unstandard/unstd.c.string.html#tempCString -- Денис В. Шеломовский Denis V. Shelomovskij
Nov 28 2013
parent "Namespace" <rswhite4 googlemail.com> writes:
On Thursday, 28 November 2013 at 17:12:01 UTC, Denis Shelomovskij 
wrote:
 27.11.2013 3:33, Namespace пишет:
 First of all: I apologize for my bad english.

 In the last few weeks I searched for a way to allocate nicely 
 temporary
 buffer of unknown lengths.
You can use `unstd.memory.allocation.tempAlloc` [1]. Also there is `unstd.c.string.tempCString` [2] for common case of temporary C strings. [1] http://denis-sh.bitbucket.org/unstandard/unstd.memory.allocation.html#tempAlloc [2] http://denis-sh.bitbucket.org/unstandard/unstd.c.string.html#tempCString
Currently I use something like: ---- import std.stdio; import core.memory : GC; struct scoped(A : T[], T) { T[] arr; this(T[] arr) { this.arr = arr; writefln("Allocate %d %s's (ptr = %x)", arr.length, T.stringof, arr.ptr); } alias arr this; disable this(this); ~this() { writefln("Deallocate %d %s's (ptr = %x)", this.arr.length, T.stringof, this.arr.ptr); version(none) { GC.free(this.arr.ptr); } else { delete this.arr; } GC.minimize(); this.arr = null; } } void main() { scoped!(int[]) bytes = new int[100_000]; } ---- It's the nearest to scope int[] bytes = new int[100_000]; I could find.
Nov 28 2013