scoped allocations

Namespace rswhite4 at googlemail.com
Tue Nov 26 15:33:57 PST 2013


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?


More information about the Digitalmars-d mailing list