digitalmars.D - "Value class instance" pattern?
- bearophile (84/84) Jul 13 2013 I don't know if this post is more fit for the main D newsgroup.
- Benjamin Thaut (96/96) Jul 13 2013 I had the very same problem and I'm using a composite helper struct for
- Benjamin Thaut (4/5) Jul 13 2013 Sorry this line should read:
- bearophile (76/76) Jul 13 2013 (Sorry, this post has gone in this newsgroup by mistake, but it's
- Benjamin Thaut (25/101) Jul 13 2013 Yes the assignment in this(DefaultCtor) is needed, because the
- Benjamin Thaut (24/24) Jul 13 2013 I also wanted to mention the "ListAvailableCtors" template which is a
- bearophile (12/38) Jul 13 2013 In my version of your code I have just added a template
- Benjamin Thaut (13/51) Jul 14 2013 The problem with your version is that you will get something like:
- bearophile (10/12) Jul 14 2013 Right. On the other hand this is what happens when you call any
- bearophile (26/41) Jul 13 2013 OK.
- Benjamin Thaut (7/34) Jul 14 2013 Can you give an example for that?
- bearophile (52/61) Jul 14 2013 As a simple example try to make this work:
- Benjamin Thaut (74/136) Jul 14 2013 Yes your little example perfectly illustrates a few shortcommings of the...
- Kagamin (4/9) Jul 13 2013 The alignment is guaranteed only for enclosing class, not for the
- Benjamin Thaut (12/88) Jul 14 2013 I just noticed that this still does not work. Even with dmd 2.063 I get
- bearophile (8/19) Jul 14 2013 Sorry, I forgot to say that I am using this:
- Benjamin Thaut (7/27) Jul 14 2013 This still doesn't work for me. The compiler (2.063.2) will tell me that...
- bearophile (8/9) Jul 14 2013 It seems to work. (But in that little program I have found two
- Benjamin Thaut (2/10) Jul 14 2013 Can you give a link to the two respective bugs please?
- bearophile (8/9) Jul 14 2013 I think they are:
- Kagamin (2/7) Jul 13 2013 emplace?
- bearophile (5/6) Jul 13 2013 Can you show a little compilable program that solves my problem
- Kagamin (3/7) Jul 13 2013 Do you want just to construct unique instances of Foo in each Bar
- bearophile (5/7) Jul 13 2013 Construct an instance of Foo in Bar is enough.
- Dicebot (21/22) Jul 14 2013 Hm, actually naive scoped usage seems to work for me:
- Dicebot (3/5) Jul 14 2013 P.S. but reading scoped docs I got no idea if this is a legal
- bearophile (35/51) Jul 14 2013 Good. If I comment away most tags it seems to work:
- Namespace (2/2) Jul 14 2013 The warning comes from:
- bearophile (4/6) Jul 14 2013 But is "void" an initializer?
- Dicebot (14/15) Jul 14 2013 This is almost 100% a bug. I have played a bit with this snippet
- Artur Skawina (11/14) Jul 14 2013 `void` is special - yes. But keep in mind that this currently does
- Dicebot (5/6) Jul 14 2013 Ugh, rly? As far as I understand spec it should work like this:
- Artur Skawina (15/20) Jul 14 2013 "void-initialization of aggregate fields does *not* actually disable
- Dicebot (5/17) Jul 14 2013 Still can't find this in the spec but I see what you are getting
- Timon Gehr (2/4) Jul 14 2013 (This is a QOI issue.)
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
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
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
(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
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, bearophileYes 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
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
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
Am 13.07.2013 18:41, schrieb bearophile:Benjamin Thaut: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 ThautI 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 14 2013
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
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
Am 13.07.2013 18:49, schrieb bearophile:Benjamin Thaut:Thats enough for the debug build.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...Can you give an example for that?But that would be a uneccessary overhead. Just adding a align(4) or align(16) like suggested below would be sufficient. Kind Regards Benjamin ThautYes 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.
Jul 14 2013
Benjamin Thaut: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() {}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?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, bearophileMaybe 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.
Jul 14 2013
Am 14.07.2013 14:25, schrieb bearophile:Benjamin Thaut: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.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() {}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?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 ThautI 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, bearophileMaybe 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.
Jul 14 2013
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
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, bearophileI 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
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
Am 14.07.2013 14:03, schrieb bearophile:Benjamin Thaut: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 ThautI 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
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
Am 14.07.2013 16:25, schrieb bearophile:Benjamin Thaut:Can you give a link to the two respective bugs please?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
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
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
Kagamin:emplace?Can you show a little compilable program that solves my problem using emplace? Bye, bearophile
Jul 13 2013
On Saturday, 13 July 2013 at 17:48:40 UTC, bearophile wrote: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++?emplace?Can you show a little compilable program that solves my problem using emplace?
Jul 13 2013
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
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
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
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
The warning comes from: http://dlang.org/changelog.html#staticfields
Jul 14 2013
Namespace:The warning comes from: http://dlang.org/changelog.html#staticfieldsBut is "void" an initializer? Bye, bearophile
Jul 14 2013
On Sunday, 14 July 2013 at 15:28:26 UTC, bearophile wrote:Namespace:Seems so, but I don't know why. Anyway, it's obviously wrong, you should fill a bug report.The warning comes from: http://dlang.org/changelog.html#staticfieldsBut is "void" an initializer? Bye, bearophile
Jul 14 2013
On Sunday, 14 July 2013 at 15:28:26 UTC, bearophile wrote:Namespace: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.The warning comes from: http://dlang.org/changelog.html#staticfieldsBut is "void" an initializer? Bye, bearophile
Jul 14 2013
On Sunday, 14 July 2013 at 15:17:43 UTC, bearophile wrote:test.d(4): Error: slice x[] is not mutableThis 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
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
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
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
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
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
On Sunday, 14 July 2013 at 18:12:07 UTC, Timon Gehr wrote:On 07/14/2013 07:51 PM, Dicebot wrote: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.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
void initialization is for stack variables only. It doesn't make sense for fields as they're initialized with init.
Jul 15 2013