RedBlackTree thin wrapper initialization

Rene Zwanenburg via Digitalmars-d digitalmars-d at puremagic.com
Wed May 28 09:25:38 PDT 2014


On Wednesday, 28 May 2014 at 09:37:55 UTC, Edwin van Leeuwen 
wrote:
> Thank you for the reply. Does this mean I should never 
> initialize classes/objects like that or is it more specific to 
> RBT?

It's the same for any class.

> I guess structs have the same problem with classes/objects?

That's right.

> That makes struct that hold objects quite awkward to use, since 
> you can't define a default constructor for structs.
>
> struct S
> {
>     auto tree = new RedBlackTree!string();
> }

There is a workaround for struct default construction. First let 
me describe the way D initialization works:

Every type, built in or user defined, has an init value. For ints 
this is 0, floats NaN, and so on. User defined types have their 
init value stored in the corresponding TypeInfo, for example [0], 
[1].

When initializing a value type the space is already reserved, 
either on the stack or on the heap inside a dynamic array. All 
the runtime does is memcpy the init value of that type to the 
right location.

When new-ing a class the GC first allocates some space on the 
heap. Then it copies the init value to that location, just like 
with a value type. Finally it calls a constructor on the newly 
allocated object.

When declaring a field in your struct or class and assign a 
default value to it, it ends up in the init value for your type. 
For example:

class C
{
   int i = uniform(0, 10); // Uniform random number
}

The generated random value will end up in C's init value and thus 
be the same for every instance. Generating the number in a 
constructor will of course result in a fresh random number for 
every instance.

The reason initialization works this way is, as I understand it, 
both safety and speed. The memcpy will never fail, so if you 
successfully allocated the space for something it will _always_ 
be properly initialized. And since the init value of everything 
is known at compile time the init values of composite types 
contain the init values of all contained fields. In other words, 
no matter how complex your type, it will always be initialized 
with a single memcpy.

Now, a workaround for struct default constructors is to use 
static opCall:

struct S
{
   int i;

   @disable this(); // Disable default initialization. See the 
comment in [1]

   private this(int i)
   {
     this.i = i;
   }

   static auto opCall()
   {
     return S(uniform(0, 10));
   }
}

void main()
{
   S s; // Should not compile. No init value for S
   auto s = S(); // Calls S.opCall
}

I hope my ramblings make it a bit more clear what is happening 
instead of confusing the hell out of you ;). I didn't try any of 
this code so there may be some typos.


[0]https://github.com/D-Programming-Language/druntime/blob/master/src/object_.d#L844
[1] 
https://github.com/D-Programming-Language/druntime/blob/master/src/object_.d#L1060


More information about the Digitalmars-d mailing list