emplace, scope, enforce [Was: Re: Manual...]

Dmitry Olshansky dmitry.olsh at gmail.com
Wed Jul 21 04:32:58 PDT 2010


On 21.07.2010 14:20, bearophile wrote:
> Dmitry Olshansky:
>    
>> Well, I'm using this for structs, very straightforward:
>>
>> T* create(T, Args...)(Args args)
>> if ( !is(T == class) ){
>>       return emplace!T(malloc(T.sizeof)[0..T.sizeof], args);
>> }
>>      
> That's not good enough, you are allocating on the (C) heap. If you use that in the D benchmark I have shown you probably can get bad timing results.
>
>    
Uh, yes I guess I should have read your post to the end :). Stack 
allocation is risky business to say least. Some kind of memory pool 
should be handy.
>> This is dtor not get called, and it's because emplace is a library
>> replacement for placement new( no pun).
>> Sure enough with manual memory management you need to call clear(t) at exit.
>>      
> If the class is allocated on the stack it's much better if the destructor is called when the class gets out of scope. Otherwise it's like C programming.
> (I suggest to edit your post, to remove useless parts of the original post.)
>
>    
To that end one should prefer vanilla structs with destructor, not final 
classes and scope. That's, of course, losing the inheritance and such.
The problem is designing such classes and then documenting: "you should 
always use it as 'scope' ", is awkward.
Moreover, the function which you pass the stack allocated class instance 
are unaware of that clever trick.

Slight modification of your benchmark:

import std.conv: emplace;
import std.contracts;

import std.stdio;
final class Test { // 32 bytes each instance
     int i1, i2, i3, i4, i5, i6;
     this(int ii1, int ii2, int ii3, int ii4, int ii5, int ii6) {
         this.i1 = ii1;
         this.i2 = ii2;
         this.i3 = ii3;
         this.i4 = ii4;
         this.i5 = ii5;
         this.i6 = ii6;
     }
     void doSomething(int ii1, int ii2, int ii3, int ii4, int ii5, int 
ii6) {
     }
}

Test hidden;

void fun(Test t){
     hidden = t;
}

void bench(){
     enum int N = 10_000_000;
     int i;

     while (i < N) {
         scope Test testObject = new Test(i, i, i, i, i, i);
         fun(testObject);
         testObject.doSomething(i, i, i, i, i, i);
         testObject.doSomething(i, i, i, i, i, i);
         testObject.doSomething(i, i, i, i, i, i);
         testObject.doSomething(i, i, i, i, i, i);
         i++;
     }

}

void main() {
     int a,b,c;//
     bench();
//what's the hidden now?
     writefln("%d %d", hidden.i1,hidden.i2);
     writefln("%d %d", hidden.i1,hidden.i2);
}

The second writefln prints garbage. I guess it's because of pointer to 
the long gone stackframe, which is ovewritten by the first writeln.

-- 
Dmitry Olshansky



More information about the Digitalmars-d mailing list