Bug 10404 – Class!T should be the class version of type T

Status
NEW
Severity
enhancement
Priority
P4
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2013-06-18T07:39:05Z
Last change time
2024-12-13T18:08:24Z
Assigned to
No Owner
Creator
Andrei Alexandrescu
Moved to GitHub: dmd#18613 →

Comments

Comment #0 by andrei — 2013-06-18T07:39:05Z
Given a struct S with state and methods, Class!S should be a type that: * is a class type * contains exactly one member, having type S * has constructors that forward to S's constructors * has the same methods as S, with implementations that forward to the member's methods This offers an immediate means to convert a struct into a class that has the same functionality but typical class semantics (heap allocation, reference semantics, overridable methods etc). Further ideas: * define a clone() method that duplicates the state by copying the S member * allow S to be a primitive type * offer flags such as "all methods should be overridable" or "all methods should be final", i.e. Class!(S, ClassOptions.allOverridable) vs. Class!(S, ClassOptions.allFinal).
Comment #1 by bearophile_hugs — 2013-06-19T05:25:03Z
(In reply to comment #0) > Given a struct S with state and methods, Class!S should be a type that: Can you list some use cases? > * contains exactly one member, having type S Is this going to waste some space because of struct alignment sometimes being higher than the alignment of its members? Also D classes are free to reorder their fields to pack them (see Issue 8873 ), but if you put the whole D inside the class you lose that optimization. ----------------- Is it a good idea to also add optional interfaces to Class, as shown below? import std.stdio, std.math; interface Shape {} struct CircleS { int x, y, r; } alias Circle = Class!(CircleS, Shape); struct RectS { int x, y, w, h; } alias Rect = Circle!(RectS, Shape); real area(in Shape s) { if (cast(Circle)s) { const c = cast(Circle)s; return c.r ^^ 2 * PI; } else { const r = cast(Rect)s; return r.w * r.h; } } void main() { new Circle(5, 10, 20).area.writeln; } This is a bit like an Algebraic(Circle, Rect) where Circle and Rect are by reference... ----------------- Similarly this is a ClassTuple, it's defined with the syntax of std.typecon.Tuple: alias Circle2 = ClassTuple!(int,"x", int,"y", int,"r"); But probably this is enough, no need for a ClassTuple: alias Circle3 = Class!(Tuple!(int,"x", int,"y", int,"r"));
Comment #2 by irritate — 2013-06-19T06:13:57Z
> has the same methods as S, with implementations that forward to the member's methods I think this idea deserves some independent consideration. It would be nice to have some generic tools for wrapping one type around another with flexibility to automatically override-with-forwarding some/all of its members. Some brain dump I have about this: 1. For example, it's very common in Phobos to wrap one range with another. I'd like to be able to get automatic forwarding members (and custom ones; and hide some; all at my option) so that my wrapper range can provide the same functions as my wrapped range without the boilerplate. 2. In particular I'd like the qualifiers on my wrapper type's members to be the same as the wrapped ones. So if my inner range has a const length property, mine will be made const at instantiation, etc. (This reminds me of inout for function parameters). Maybe this would obviate the need for some of the rampant Unqual that goes on when wrapping ranges. 3. I tried to use __traits to creating a wrapping template, but I am an amateur at this. I also could not find a good way to define a member with the same qualifiers as another type's member. 4. Getting generic functionality for wrapping would align with "prefer composition to inheritance". Sorry if I am speaking out of turn. I have just started looking at issues in Phobos and have already run into these problems with boilerplate/qualifiers with wrapping ranges. And when I saw this issue for Class!T, I thought maybe wrapping a struct in a class was just a special case of what I wanted with wrapping one type in another.
Comment #3 by andrei — 2013-06-19T12:29:39Z
Update: if a method returns an S by value, the Class!S corresponding wrapper should return a new instance of Class!S with S as payload.
Comment #4 by monarchdodra — 2013-06-19T23:34:08Z
(In reply to comment #3) > Update: if a method returns an S by value, the Class!S corresponding wrapper > should return a new instance of Class!S with S as payload. I think we're fucked in a lot more cases than just that actually: - What about functions that take an S by value as an argument? DO we change it to Class!S, and duplicate the class on receive (pass by value) ? - Or functions that take an S by ref? - Or return S by Ref (return this)?
Comment #5 by andrei — 2013-06-20T04:52:34Z
(In reply to comment #4) > (In reply to comment #3) > > Update: if a method returns an S by value, the Class!S corresponding wrapper > > should return a new instance of Class!S with S as payload. > > I think we're fucked in a lot more cases than just that actually: > - What about functions that take an S by value as an argument? DO we change it > to Class!S, and duplicate the class on receive (pass by value) ? > - Or functions that take an S by ref? > - Or return S by Ref (return this)? It's all about reasoning through the cases. We may conservatively not generate wrappers for such functions, or generate them with a specific semantics.
Comment #6 by robert.schadek — 2024-12-13T18:08:24Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/18613 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB