www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - "Value class instance" pattern?

reply "bearophile" <bearophileHUGS lycos.com> writes:
I don't know if this post is more fit for the main D newsgroup. 
In the end I have decided that it's better to ask here first.

Here inside every instance of Bar there is a reference to an 
instance of Foo:



abstract class Something {
     int spam(int);
}

class Foo : Something {
     int x;
     this(int xx) { x = xx; }
     override int spam(int x) { return x; }
}

class Bar : Something {
     Foo f;
     int y;
     this(Foo ff) { f = ff; }
     override int spam(int x) { return x; }
}

void main() {
     auto b = new Bar(new Foo(1));
}


In C++ class instances are values, so the class Bar can contain 
an instance of Foo as a value. This removes one allocation and 
one indirection and decreases a bit the pressure on the GC. How 
to do the same thing in D? Can I use Scoped or something to do 
that?

This is one solution I have found, to create a FooValue struct, 
and use Foo only as a wrapper:


abstract class Something {
     int spam(int);
}

struct FooValue {
     int x;
     this(int xx) { x = xx; }
     int spam(int x) { return x; }
}

class Foo : Something {
     FooValue f1;
     this(int xx) { f1.x = xx; }
     override int spam(int x) {
         return f1.spam(x);
     }
}

class Bar : Something {
     FooValue f2;
     int y;
     this(FooValue fv) { f2 = fv; }
     override int spam(int x) { return x; }
}

void main() {
     auto b = new Bar(FooValue(1));
}


But is this the best solution? (Beside containing some 
boilerplate code, calling Foo.spam requires a function call and a 
virtual call, instead of just a virtual call. Can we do with just 
one virtual call?).


Recently Andrei has suggested to create a Phobos wrapper (not yet 
implemented), I don't know if it's usable here:
http://d.puremagic.com/issues/show_bug.cgi?id=10404

If that wrapper will also allow derivation (as I have suggested) 
then I think it will allow shorter code like this (but I think 
this will keep needing a a function call and a virtual call to 
call Foo.spam):


import std.typecons: Class;

abstract class Something {
     int spam(int);
}

struct FooValue {
     int x;
     this(int xx) { x = xx; }
     int spam(int x) { return x; }
}

alias Foo = Class!(FooValue, Something);

class Bar : Something {
     FooValue f2;
     int y;
     this(FooValue fv) { f2 = fv; }
     override int spam(int x) { return x; }
}

void main() {
     auto b = new Bar(FooValue(1));
}


Bye and thank you,
bearophile
Jul 13 2013
next sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
I had the very same problem and I'm using a composite helper struct for 
this purpose:

struct DefaultCtor {}; //call default ctor type

struct composite(T)
{
   static assert(is(T == class),"can only composite classes");
   void[__traits(classInstanceSize, T)] _classMemory = void;
   bool m_destructed = false;
   debug {
     T _instance;
   }
   else
   {
      property T _instance()
     {
       return cast(T)_classMemory.ptr;
     }

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

   alias _instance this;

    disable this();
    disable this(this); //prevent evil stuff from happening

   this(DefaultCtor c){
   };

   void construct(ARGS...)(ARGS args) //TODO fix: workaround because 
constructor can not be a template
   {
     _classMemory[] = typeid(T).init[];
     T result = (cast(T)_classMemory.ptr);
     static if(is(typeof(result.__ctor(args))))
     {
       result.__ctor(args);
     }
     else
     {
       static assert(args.length == 0 && !is(typeof(T.__ctor)),
                     "Don't know how to initialize an object of type "
                     ~ T.stringof ~ " with arguments:\n" ~ ARGS.stringof 
~ "\nAvailable ctors:\n" ~ ListAvailableCtors!T() );
     }
     debug _instance = result;
   }

   void destruct()
   {
     assert(!m_destructed);
     Destruct(_instance);
     debug _instance = null;
     m_destructed = true;
   }

   ~this()
   {
     if(!m_destructed)
     {
       Destruct(_instance);
       m_destructed = true;
     }
   }
}


I and I use it often (especially with containers, I always tend to 
forget newing containers, which can not happen with this helper struct 
because of  disable this();

Usage is something like the following:

class Foo
{
   int m_i;
   this()
   {
     m_i = 5;
   }

   this(int i)
   {
     m_i = i;
   }
}

class Bar
{
   composite!Foo m_foo;

   this()
   {
     m_foo = typeof(m_foo)();
     m_foo.construct(DefaultCtor()); // would call Foo.this()
     // alternative
     m_foo.construct(5); // would call Foo.this(int)
   }
}

I wrote this before constructors of structs could be templated, I plan 
on updating it so you can write

m_foo = typeof(m_foo)(5);

The m_destructed member is kind of optional, if the default destruction 
order is ok, you can omit it and save the additional bytes of overhead 
it imposes. A advantage is also that Foo is a normal class, you can 
inherit from it and you don't have to write any boilerplate code to use 
the value type of Foo.
Jul 13 2013
parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 13.07.2013 15:17, schrieb Benjamin Thaut:
 m_foo = typeof(m_foo)();

Sorry this line should read: m_foo = typeof(m_foo)(DefaultCtor()); there should really be some kind of edit option for the newsgroup.
Jul 13 2013
next sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 13.07.2013 17:15, schrieb bearophile:
 (Sorry, this post has gone in this newsgroup by mistake, but it's a
 small mistake.)


 To change and understand your code a bit (now the ComposeClass
 constructor is a template) I have removed some of the tests and debug
 code, but they should be added back later:


 /*
 string listAvailableCtors(T)() {
      string result = "";
      foreach(t; __traits(getOverloads, T, "__ctor"))
          result ~= typeof(t).stringof ~ "\n";
      return result;
 }
 */

 struct DefaultCtor {} //call default ctor type
 enum defaultCtor = DefaultCtor();

 struct ComposeClass(T) if (is(T == class)) {
      void[__traits(classInstanceSize, T)] _classMemory = void;
      bool m_destructed = false;

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

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

      alias _instance this;

       disable this();
       disable this(this);

      this(DefaultCtor) {
          // _classMemory[] = typeid(T).init[]; // needed?
          _instance.__ctor;
      }

      this(Targs...)(Targs args) {
          _classMemory[] = typeid(T).init[];
          _instance.__ctor(args);
      }

      ~this() {
          if (!m_destructed) {
              _instance.destroy;
              m_destructed = true;
          }
      }
 }

 // Usage example ----------

 class Foo {
      int i, j;
      this() {
          this.i = 5;
      }

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

 class Bar {
      ComposeClass!Foo f;

      this() {
          //f = typeof(f)(defaultCtor);
          f = typeof(f)(2); // alternative
      }
 }

 void main() {
      import std.stdio;
      auto bar = new Bar;
      writeln(bar.f.i);
      bar.f.i = 1;
      writeln(bar.f.i);
 }


 This code is unfinished.
 Is the assignment needed in this(DefaultCtor)?

 This code contains some sharp edges (for the D programmer and even for
 the compiler, I have opened a new bug report:
 http://d.puremagic.com/issues/show_bug.cgi?id=10629 ), for me it's easy
 to get wrong, hard to get right & good, and I believe it's of general
 usefulness, so I think it's fit for Phobos.

 But isn't it a replacement for Phobos Scoped?

 Bye,
 bearophile

Yes the assignment in this(DefaultCtor) is needed, because the construction process of a D object is defined as: 1) Initialize memory with T.init 2) Call the constructor You can test that with a class that looks as follows class Foo { int i = 5; this() { assert(i == 5); } } I really like the constant defaultCtor value idea which makes the usage somewhat nicer. The debugging values are needed because most debuggers won't be able to evaluate properties while debugging. Yes this looks pretty similar to scoped in phobos but I always thought that scoped was inteded for objects to be placed on the stack instead of inside other objects. Thats also why scoped does some additional alignment which is garantueed when you do it within a class. Also from reading the sourcecode of scoped, doesn't the address of the scoped object change between construction and destruction? Because it is created inside the scoped function and then returned, which will do a move. And then later destroyed at its final destination. Wouldn't that break with interior pointers? Kind Regards Benjamin Thaut
Jul 13 2013
next sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
I also wanted to mention the "ListAvailableCtors" template which is a 
nice addition in case there is no constructor available to be called 
with the given arguments. It will generate a list of all aviable ctors 
with the types of its arguments, and thus greatly improve the error 
message given when no appropriate constructor can be found:

string ListAvailableCtors(T)()
{
   string result = "";
   foreach(t; __traits(getOverloads, T, "__ctor"))
     result ~= typeof(t).stringof ~ "\n";
   return result;
}

In my original code it was used during construction like this:

static if(is(typeof(result.__ctor(args))))
{
   result.__ctor(args);
}
else
{
   static assert(args.length == 0 && !is(typeof(T.__ctor)), "Don't know
   how to initialize an object of type " ~ T.stringof ~ " with
   arguments:\n" ~ ARGS.stringof ~ "\nAvailable ctors:\n" ~
   ListAvailableCtors!T() );
}
Jul 13 2013
parent Benjamin Thaut <code benjamin-thaut.de> writes:
Am 13.07.2013 18:41, schrieb bearophile:
 Benjamin Thaut:

 I also wanted to mention the "ListAvailableCtors" template which is a
 nice addition in case there is no constructor available to be called
 with the given arguments. It will generate a list of all aviable ctors
 with the types of its arguments, and thus greatly improve the error
 message given when no appropriate constructor can be found:

 string ListAvailableCtors(T)()
 {
   string result = "";
   foreach(t; __traits(getOverloads, T, "__ctor"))
     result ~= typeof(t).stringof ~ "\n";
   return result;
 }

 In my original code it was used during construction like this:

 static if(is(typeof(result.__ctor(args))))
 {
   result.__ctor(args);
 }
 else
 {
   static assert(args.length == 0 && !is(typeof(T.__ctor)), "Don't know
   how to initialize an object of type " ~ T.stringof ~ " with
   arguments:\n" ~ ARGS.stringof ~ "\nAvailable ctors:\n" ~
   ListAvailableCtors!T() );
 }

In my version of your code I have just added a template constraint, this is simpler, and it generates an error at the calling point: this(Targs...)(Targs args) if (__traits(compiles, _instance.__ctor(args))) { classInstanceBuf[] = typeid(T).init[]; _instance.__ctor(args); } Isn't this enough? Bye, bearophile

The problem with your version is that you will get something like: Can't call ComposedClass.this with arguments (int, int, float) With my version you will get: Can't initialize object of type 'Foo' with arguments (int, int, float) available ctors: (int, int, int) (int) (float) With my version you will instantly know what ctors are available and you don't have to go look it up in the sourcecode. Kind Regards Benjamin Thaut
Jul 14 2013
prev sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 13.07.2013 18:49, schrieb bearophile:
 Benjamin Thaut:

 Yes the assignment in this(DefaultCtor) is needed, because the
 construction process of a D object is defined as:

OK.
 The debugging values are needed because most debuggers won't be able
 to evaluate properties while debugging.

OK. Is that ClassCompose destructor enough (with something added for the debug build)? ~this() { if (!isDestructed) { _instance.destroy; isDestructed = true; } }

Thats enough for the debug build.
 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?
 Yes this looks pretty similar to scoped in phobos but I always thought
 that scoped was inteded for objects to be placed on the stack instead
 of inside other objects.

Right. I think Scoped can't be used for class composition. But isn't such ClassCompose enough for both purposes? Or is it better to have in Phobos both Scoped and something similar to ClassCompose?
 Thats also why scoped does some additional alignment which is
 garantueed when you do it within a class.

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. Kind Regards Benjamin Thaut
Jul 14 2013
parent Benjamin Thaut <code benjamin-thaut.de> writes:
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
Jul 14 2013
prev sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 13.07.2013 17:15, schrieb bearophile:
 (Sorry, this post has gone in this newsgroup by mistake, but it's a
 small mistake.)


 To change and understand your code a bit (now the ComposeClass
 constructor is a template) I have removed some of the tests and debug
 code, but they should be added back later:


 /*
 string listAvailableCtors(T)() {
      string result = "";
      foreach(t; __traits(getOverloads, T, "__ctor"))
          result ~= typeof(t).stringof ~ "\n";
      return result;
 }
 */

 struct DefaultCtor {} //call default ctor type
 enum defaultCtor = DefaultCtor();

 struct ComposeClass(T) if (is(T == class)) {
      void[__traits(classInstanceSize, T)] _classMemory = void;
      bool m_destructed = false;

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

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

      alias _instance this;

       disable this();
       disable this(this);

      this(DefaultCtor) {
          // _classMemory[] = typeid(T).init[]; // needed?
          _instance.__ctor;
      }

      this(Targs...)(Targs args) {
          _classMemory[] = typeid(T).init[];
          _instance.__ctor(args);
      }

      ~this() {
          if (!m_destructed) {
              _instance.destroy;
              m_destructed = true;
          }
      }
 }

 // Usage example ----------

 class Foo {
      int i, j;
      this() {
          this.i = 5;
      }

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

 class Bar {
      ComposeClass!Foo f;

      this() {
          //f = typeof(f)(defaultCtor);
          f = typeof(f)(2); // alternative
      }
 }

 void main() {
      import std.stdio;
      auto bar = new Bar;
      writeln(bar.f.i);
      bar.f.i = 1;
      writeln(bar.f.i);
 }


 This code is unfinished.
 Is the assignment needed in this(DefaultCtor)?

 This code contains some sharp edges (for the D programmer and even for
 the compiler, I have opened a new bug report:
 http://d.puremagic.com/issues/show_bug.cgi?id=10629 ), for me it's easy
 to get wrong, hard to get right & good, and I believe it's of general
 usefulness, so I think it's fit for Phobos.

 But isn't it a replacement for Phobos Scoped?

 Bye,
 bearophile

I just noticed that this still does not work. Even with dmd 2.063 I get the error message: main.d(28): Error: template main.ComposeClass!(Object).ComposeClass.__ctor(Targs...)(Targs args) conflicts with constructor main.ComposeClass!(Object).ComposeClass.this at main.d(20) If you have a templated constructor you can't have any non templated constructors. (http://d.puremagic.com/issues/show_bug.cgi?id=4749) So a version with a templated constructor is still not doable at the moment. Kind Regards Benjamin Thaut
Jul 14 2013
parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 14.07.2013 14:03, schrieb bearophile:
 Benjamin Thaut:

 I just noticed that this still does not work. Even with dmd 2.063 I
 get the error message:

 main.d(28): Error: template
 main.ComposeClass!(Object).ComposeClass.__ctor(Targs...)(Targs args)
 conflicts with constructor
 main.ComposeClass!(Object).ComposeClass.this at main.d(20)

 If you have a templated constructor you can't have any non templated
 constructors. (http://d.puremagic.com/issues/show_bug.cgi?id=4749)
 So a version with a templated constructor is still not doable at the
 moment.

Sorry, I forgot to say that I am using this: this()(UseDefaultCtor) { classInstanceBuf[] = typeid(T).init[]; _instance.__ctor; } Bye, bearophile

This still doesn't work for me. The compiler (2.063.2) will tell me that it conflicts with disable this(); Does this actually work for you? Kind Regards Benjamin Thaut
Jul 14 2013
parent Benjamin Thaut <code benjamin-thaut.de> writes:
Am 14.07.2013 16:25, schrieb bearophile:
 Benjamin Thaut:

 Does this actually work for you?

It seems to work. (But in that little program I have found two new unrelated compiler bugs that don't allow me to compile the program in an useful way. I have reported both of them in Bugzilla and one of them has now a patch, so probably I will not have to wait a lot). Bye, bearophile

Can you give a link to the two respective bugs please?
Jul 14 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
(Sorry, this post has gone in this newsgroup by mistake, but it's 
a small mistake.)


To change and understand your code a bit (now the ComposeClass 
constructor is a template) I have removed some of the tests and 
debug code, but they should be added back later:


/*
string listAvailableCtors(T)() {
     string result = "";
     foreach(t; __traits(getOverloads, T, "__ctor"))
         result ~= typeof(t).stringof ~ "\n";
     return result;
}
*/

struct DefaultCtor {} //call default ctor type
enum defaultCtor = DefaultCtor();

struct ComposeClass(T) if (is(T == class)) {
     void[__traits(classInstanceSize, T)] _classMemory = void;
     bool m_destructed = false;

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

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

     alias _instance this;

      disable this();
      disable this(this);

     this(DefaultCtor) {
         // _classMemory[] = typeid(T).init[]; // needed?
         _instance.__ctor;
     }

     this(Targs...)(Targs args) {
         _classMemory[] = typeid(T).init[];
         _instance.__ctor(args);
     }

     ~this() {
         if (!m_destructed) {
             _instance.destroy;
             m_destructed = true;
         }
     }
}

// Usage example ----------

class Foo {
     int i, j;
     this() {
         this.i = 5;
     }

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

class Bar {
     ComposeClass!Foo f;

     this() {
         //f = typeof(f)(defaultCtor);
         f = typeof(f)(2); // alternative
     }
}

void main() {
     import std.stdio;
     auto bar = new Bar;
     writeln(bar.f.i);
     bar.f.i = 1;
     writeln(bar.f.i);
}


This code is unfinished.
Is the assignment needed in this(DefaultCtor)?

This code contains some sharp edges (for the D programmer and 
even for the compiler, I have opened a new bug report: 
http://d.puremagic.com/issues/show_bug.cgi?id=10629 ), for me 
it's easy to get wrong, hard to get right & good, and I believe 
it's of general usefulness, so I think it's fit for Phobos.

But isn't it a replacement for Phobos Scoped?

Bye,
bearophile
Jul 13 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Benjamin Thaut:

 I also wanted to mention the "ListAvailableCtors" template 
 which is a nice addition in case there is no constructor 
 available to be called with the given arguments. It will 
 generate a list of all aviable ctors with the types of its 
 arguments, and thus greatly improve the error message given 
 when no appropriate constructor can be found:

 string ListAvailableCtors(T)()
 {
   string result = "";
   foreach(t; __traits(getOverloads, T, "__ctor"))
     result ~= typeof(t).stringof ~ "\n";
   return result;
 }

 In my original code it was used during construction like this:

 static if(is(typeof(result.__ctor(args))))
 {
   result.__ctor(args);
 }
 else
 {
   static assert(args.length == 0 && !is(typeof(T.__ctor)), 
 "Don't know
   how to initialize an object of type " ~ T.stringof ~ " with
   arguments:\n" ~ ARGS.stringof ~ "\nAvailable ctors:\n" ~
   ListAvailableCtors!T() );
 }

In my version of your code I have just added a template constraint, this is simpler, and it generates an error at the calling point: this(Targs...)(Targs args) if (__traits(compiles, _instance.__ctor(args))) { classInstanceBuf[] = typeid(T).init[]; _instance.__ctor(args); } Isn't this enough? Bye, bearophile
Jul 13 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Benjamin Thaut:

 Yes the assignment in this(DefaultCtor) is needed, because the 
 construction process of a D object is defined as:

OK.
 The debugging values are needed because most debuggers won't be 
 able to evaluate properties while debugging.

OK. Is that ClassCompose destructor enough (with something added for the debug build)? ~this() { if (!isDestructed) { _instance.destroy; isDestructed = true; } } 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...
 Yes this looks pretty similar to scoped in phobos but I always 
 thought that scoped was inteded for objects to be placed on the 
 stack instead of inside other objects.

Right. I think Scoped can't be used for class composition. But isn't such ClassCompose enough for both purposes? Or is it better to have in Phobos both Scoped and something similar to ClassCompose?
 Thats also why scoped does some additional alignment which is 
 garantueed when you do it within a class.

Maybe such alignment can be added to the ClassCompose too.
 Also from reading the sourcecode of scoped, doesn't the address 
 of the scoped object change between construction and 
 destruction? Because it is created inside the scoped function 
 and then returned, which will do a move. And then later 
 destroyed at its final destination. Wouldn't that break with 
 interior pointers?

Code like Scoped or ClassCompose is fit for Phobos because it's commonly useful, and because similar questions show that it's far from obvious code. Perhaps Andrei can offer us some guidance. Bye, bearophile
Jul 13 2013
prev sibling next sibling parent "Kagamin" <spam here.lot> writes:
On Saturday, 13 July 2013 at 12:47:28 UTC, bearophile wrote:
 In C++ class instances are values, so the class Bar can contain 
 an instance of Foo as a value. This removes one allocation and 
 one indirection and decreases a bit the pressure on the GC. How 
 to do the same thing in D? Can I use Scoped or something to do 
 that?

emplace?
Jul 13 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Kagamin:

 emplace?

Can you show a little compilable program that solves my problem using emplace? Bye, bearophile
Jul 13 2013
prev sibling next sibling parent "Kagamin" <spam here.lot> writes:
On Saturday, 13 July 2013 at 16:19:06 UTC, Benjamin Thaut wrote:
 Yes this looks pretty similar to scoped in phobos but I always 
 thought that scoped was inteded for objects to be placed on the 
 stack instead of inside other objects. Thats also why scoped 
 does some additional alignment which is garantueed when you do 
 it within a class.

The alignment is guaranteed only for enclosing class, not for the composite struct. Though in case on embedding it's enough to add align(16), I suppose.
Jul 13 2013
prev sibling next sibling parent "Kagamin" <spam here.lot> writes:
On Saturday, 13 July 2013 at 17:48:40 UTC, bearophile wrote:
 Kagamin:

 emplace?

Can you show a little compilable program that solves my problem using emplace?

Do you want just to construct unique instances of Foo in each Bar or you want to copy Foo by value like in C++?
Jul 13 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Kagamin:

 Do you want just to construct unique instances of Foo in each 
 Bar or you want to copy Foo by value like in C++?

Construct an instance of Foo in Bar is enough. (Copying Foo is nice but not necessary.) Bye, bearophile
Jul 13 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Benjamin Thaut:

 I just noticed that this still does not work. Even with dmd 
 2.063 I get the error message:

 main.d(28): Error: template 
 main.ComposeClass!(Object).ComposeClass.__ctor(Targs...)(Targs 
 args) conflicts with constructor 
 main.ComposeClass!(Object).ComposeClass.this at main.d(20)

 If you have a templated constructor you can't have any non 
 templated constructors. 
 (http://d.puremagic.com/issues/show_bug.cgi?id=4749)
 So a version with a templated constructor is still not doable 
 at the moment.

Sorry, I forgot to say that I am using this: this()(UseDefaultCtor) { classInstanceBuf[] = typeid(T).init[]; _instance.__ctor; } Bye, bearophile
Jul 14 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Benjamin Thaut:

 With my version you will instantly know what ctors are 
 available and you don't have to go look it up in the sourcecode.

Right. On the other hand this is what happens when you call any constructor or any overloaded function. So I don't think it's so important. One possible difference I see between my simpler ComposedClass and normal overloaded functions/constructors is that with ComposedClass an IDE can't give you a list of suggestions... So perhaps your idea is useful for D programmers that use an IDE. Bye, bearophile
Jul 14 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
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() {}
 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
Jul 14 2013
prev sibling next sibling parent reply "Dicebot" <public dicebot.lv> writes:
On Saturday, 13 July 2013 at 12:47:28 UTC, bearophile wrote:
 ...

Hm, actually naive scoped usage seems to work for me: --------------------------------------- import std.typecons; class B { byte a; } class A { typeof(scoped!B()) b = void; this() { b = scoped!B(); } } --------------------------------------- ...with only exception (bug?) - I can't default-construct A on heap after that. It works as expected if I created A via scoped or use non-default constructor.
Jul 14 2013
parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 07/14/13 18:24, Dicebot wrote:
 class A
 {
     // immutable int x = void; // works not

`void` is special - yes. But keep in mind that this currently does not actually do what it intuitively appears to, and what the code posted in this thread apparently expects. IOW void-initialization of aggregate fields does *not* actually disable initialization; the result can be /worse/ codegen (blitting of `.init` instead of bzero'ing). At least this was how it worked last time i tried this, months ago; had to use ugly template mixins to avoid the expensive useless copy (`x` was a static array). Handling it properly requires supporting holes in .init. artur
Jul 14 2013
parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 07/14/13 18:59, Dicebot wrote:
 Ugh, rly? As far as I understand spec it should work like this:
 
 T x; // sets x to T.init
 T x = void; // don't initialize x at all, leave garbage

"void-initialization of aggregate fields does *not* actually disable initialization": struct S { T x = void; } S s; // Copies S.init -> s; including s.x. The initialization of s.x is *not* skipped. When T==ubyte[100_000] etc adding the '=void' initializer can make things /worse/ (blit instead of bzero).
 What hole in .init are you referring to?

See above - handling this properly means treating every `void` initialized field as a hole. Also, '=void' alone should not result in copying of T.init when it is just a zero-filled chunk. artur
Jul 14 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 07/14/2013 07:51 PM, Dicebot wrote:
 Still can't find this in the spec but I see what you are getting at,
 makes sense. ...

(This is a QOI issue.)
Jul 14 2013
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
On Sunday, 14 July 2013 at 12:28:48 UTC, Dicebot wrote:
 On Saturday, 13 July 2013 at 12:47:28 UTC, bearophile wrote:
 ...


P.S. but reading scoped docs I got no idea if this is a legal safe usage.
Jul 14 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Benjamin Thaut:

 Does this actually work for you?

It seems to work. (But in that little program I have found two new unrelated compiler bugs that don't allow me to compile the program in an useful way. I have reported both of them in Bugzilla and one of them has now a patch, so probably I will not have to wait a lot). Bye, bearophile
Jul 14 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Benjamin Thaut:

 Can you give a link to the two respective bugs please?

I think they are: http://d.puremagic.com/issues/show_bug.cgi?id=10629 http://d.puremagic.com/issues/show_bug.cgi?id=10632 The first has already a patch, and probably the second is a regression. Bye, bearophile
Jul 14 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Dicebot:

 Hm, actually naive scoped usage seems to work for me:

 ---------------------------------------
 import std.typecons;

 class B
 {
 	byte a;
 }

 class A
 {
 	typeof(scoped!B()) b = void;
 	
 	this()
 	{
 		b = scoped!B();
 	}
 }

Good. If I comment away most tags it seems to work: import std.typecons; const class Foo { int x; this(int xx) const pure nothrow { this.x = x; } } /*const*/ class Bar { /*const*/ typeof(scoped!Foo(1)) f = void; this(int x) /*const pure nothrow*/ { // f = typeof(f)(x); // Can't be used. f = scoped!Foo(x); } } void main() { auto b = new Bar(10); } I will try it in my code to see if and how well it works. I have seen code like this: class Bar { const int[1000] x = void; this(int n) { x[] = n; } } void main() {} That gives me: test.d(2): Warning: const field with initializer should be static, __gshared, or an enum test.d(4): Error: slice x[] is not mutable Are those error messages right? Bye, bearophile
Jul 14 2013
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
The warning comes from:
http://dlang.org/changelog.html#staticfields
Jul 14 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Namespace:

 The warning comes from:
 http://dlang.org/changelog.html#staticfields

But is "void" an initializer? Bye, bearophile
Jul 14 2013
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Sunday, 14 July 2013 at 15:28:26 UTC, bearophile wrote:
 Namespace:

 The warning comes from:
 http://dlang.org/changelog.html#staticfields

But is "void" an initializer? Bye, bearophile

Seems so, but I don't know why. Anyway, it's obviously wrong, you should fill a bug report.
Jul 14 2013
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
On Sunday, 14 July 2013 at 15:28:26 UTC, bearophile wrote:
 Namespace:

 The warning comes from:
 http://dlang.org/changelog.html#staticfields

But is "void" an initializer? Bye, bearophile

Technically - yes, but I think it should be an exclusion from general rules mentioned there because of special semantics it has. Until then you can safely just ignore this warning, it exists only as part of behavior change process.
Jul 14 2013
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
On Sunday, 14 July 2013 at 15:17:43 UTC, bearophile wrote:
 test.d(4): Error: slice x[] is not mutable

This is almost 100% a bug. I have played a bit with this snippet and seems like void initialization breaks usual rule that you can cook const/immutable field in constructor (which is kind of guaranteed by TDPL). Simple test case: class A { immutable int x; // works // immutable int x = void; // works not this() { x = 42; } }
Jul 14 2013
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
Ugh, rly? As far as I understand spec it should work like this:

T x; // sets x to T.init
T x = void; // don't initialize x at all, leave garbage

What hole in .init are you referring to?

On Sunday, 14 July 2013 at 16:49:13 UTC, Artur Skawina wrote:
 ...

Jul 14 2013
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
Still can't find this in the spec but I see what you are getting 
at, makes sense. I think this actually is yet another case where 
CTFE-able default constructor for structs would have been a major 
win.

On Sunday, 14 July 2013 at 17:41:35 UTC, Artur Skawina wrote:
 On 07/14/13 18:59, Dicebot wrote:
 Ugh, rly? As far as I understand spec it should work like this:
 
 T x; // sets x to T.init
 T x = void; // don't initialize x at all, leave garbage

"void-initialization of aggregate fields does *not* actually disable initialization": struct S { T x = void; } S s; // Copies S.init -> s; including s.x.

Jul 14 2013
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
On Sunday, 14 July 2013 at 18:12:07 UTC, Timon Gehr wrote:
 On 07/14/2013 07:51 PM, Dicebot wrote:
 Still can't find this in the spec but I see what you are 
 getting at,
 makes sense. ...

(This is a QOI issue.)

Is it really? I see certain conflicting interests between bit-wise T.init copy for structs and void initialization of a field. At least given the fact void initialization is considered to be a performance optimization tool. Currently there does not seem to be a away to create a struct by running postblit on top of garbage.
Jul 14 2013
prev sibling parent "Kagamin" <spam here.lot> writes:
void initialization is for stack variables only. It doesn't make 
sense for fields as they're initialized with init.
Jul 15 2013