"Value class instance" pattern?

Benjamin Thaut code at benjamin-thaut.de
Sun Jul 14 06:27:04 PDT 2013


Am 14.07.2013 14:25, schrieb bearophile:
> Benjamin Thaut:
>
>>> Trying to use ClassCompose in my code I have had some problems caused
>>> by const classes and ClassCompose dtor. Maybe such dtor
>>> (and isDestructed) can be versioned out for composed-in classes
>>> that only contain values...
>>
>> Can you give an example for that?
>
> As a simple example try to make this work:
>
>
> struct UseDefaultCtor {}
> /// Call default ctor type for Composition.
> enum useDefaultCtor = UseDefaultCtor();
>
>
> struct Composition(T) if (is(T == class)) {
>      void[__traits(classInstanceSize, T)] classInstanceBuf = void;
>      bool isDestructed = false;
>
>      @property T _instance() {
>          return cast(T)classInstanceBuf.ptr;
>      }
>
>      @property const(T) _instance() const {
>          return cast(const T)classInstanceBuf.ptr;
>      }
>
>      alias _instance this;
>
>      @disable this();
>      @disable this(this);
>
>      this()(UseDefaultCtor) {
>          classInstanceBuf[] = typeid(T).init[];
>          _instance.__ctor;
>      }
>
>      this(Targs...)(Targs args)
>      if (__traits(compiles, _instance.__ctor(args))) {
>          classInstanceBuf[] = typeid(T).init[];
>          _instance.__ctor(args);
>      }
>
>      ~this() {
>          if (!isDestructed) {
>              _instance.destroy;
>              isDestructed = true;
>          }
>      }
> }
>
> // ----------------------------------
>
> const class Foo {
>      int x;
>      this(int xx) const pure nothrow {
>          this.x = x;
>      }
> }
>
> const class Bar {
>      const Composition!Foo f;
>
>      this(int x) const pure nothrow {
>          f = typeof(f)(x);
>      }
> }
>
> void main() {}
>

Yes your little example perfectly illustrates a few shortcommings of the 
D type system. Thats the best I could do:

struct Composition(T) if (is(T == class)) {
     void[__traits(classInstanceSize, T)] classInstanceBuf = void;
     bool isDestructed = false;

     @property T _instance() {
         return cast(T)classInstanceBuf.ptr;
     }

     @property const(T) _instance() const {
         return cast(const T)classInstanceBuf.ptr;
     }

     alias _instance this;

     //@disable this();
     @disable this(this);

     /*this()(UseDefaultCtor) {
         classInstanceBuf[] = typeid(T).init[];
         _instance.__ctor;
     }*/

     this(Targs...)(Targs args) const pure nothrow
     if (__traits(compiles, _instance.__ctor(args))) {
         classInstanceBuf[] = typeid(T).init[];
         _instance.__ctor(args);
     }

     ~this() pure const {
         if (!isDestructed) {
			static if(is(typeof(_intance.__dtor)))
				_instance.__dtor();
         }
     }
}

But the compiler is still trying to call opAssign inside the constructor 
of Bar. I'm not sure if that isn't actually a bug.
But the main problem here is that you can not deduce the attributes of 
the given type to the constructor and destructor of the helper struct. 
Maybe something like the following would be possible:

static if(isPure!(T.__ctor))
{
   static if(isConst!(T.__ctor))
   {
     this(Targs)(Targs args) pure const { ... }
   }
   else
   {
     this(Targs)(Targs args) pure { ... }
   }
}
else
{
   static if(isConst!(T.__ctor))
   {
     this(Targs)(Targs args) const { ... }
   }
   else
   {
     this(Targs)(Targs args) { ... }
   }
}

And so on.

Or you use a string-mixin to generate the equivalent of all constructors 
inside the helper struct.

Genereally there are still so many bugs connected with structs in D that 
it is not fun doing advanced things with structs most of the time.

>
>
>>> Maybe such alignment can be added to the ClassCompose too.
>>>
>>
>> But that would be a uneccessary overhead. Just adding a align(4) or
>> align(16) like suggested below would be sufficient.
>
> I don't understand. What overhead? It's an annotation that depends on
> the contents of ClassCompose. I think it should cause no bad overhead.
>
> Bye,
> bearophile

Phobos scoped makes the void array bigger then needed because the scoped 
struct might not be properly aligend on the stack. Then inside the 
constructor scoped manually aligns the instance to the correct alignment 
using the bytes previously added to the buffer.
Lets say you use it inside a class, and the struct already happens to be 
correctly aligned, then the additional bytes will be vasted and 
introcude a unccessary overhead. So it is better to annotate the 
CompositeClass sturct with a align attribute because the compiler will 
then only introduce additional padding if actually needed.

Kind Regards
Benjamin Thaut



More information about the Digitalmars-d mailing list