DMD 0.177 release
Burton Radons
burton-radons at smocky.com
Thu Dec 21 12:46:39 PST 2006
Pragma wrote:
> Stewart Gordon wrote:
>> Pragma wrote:
>> <snip>
>>> But I'd like to echo the other comments in this thread regarding
>>> structs. IMO, we're not there yet. I think folks are looking for a
>>> solution that does this:
>>>
>>> - A ctor like syntax for creating a new struct
>>> - No more forced copy of the entire struct on creation
>>
>> What do you mean by this?
>
> I'm glad you asked. :)
>
> Static opCall() is not a ctor. It never was. People have been
> clamoring to be able to use this() inside of a struct, much like they
> can with classes and modules. But the desire here goes beyond mere
> symmetry between type definitions.
>
> The forced copy issue is something that is an artifact of emulating a
> constructor for a struct. Take the standard approach for example:
>
> struct Foo{
> int a,b,c;
> }
>
> Foo f = {a:1, b:2, c:3};
> Foo f = {1,2,3}; // more succinct version
>
> So here we create a struct in place, and break encapsulation in the
> process. What we really want is an opaque type, that has a little more
> smarts on creation. Taking advantage of in/body/out would be nice too.
> No problem, we'll just use opCall():
>
> struct Foo{
> int a,b,c;
> static Foo opCall(int a,int b,int c){
> Foo _this;
> _this.a = a;
> _this.b = b;
> _this.c = c;
> return _this;
> }
> }
>
> Foo f = Foo(1,2,3);
>
> That's better, but look at what's really happening here. Inlining and
> compiler optimization aside, the 'constructor' here creates a Foo on the
> stack which is then returned and *copied* to the destination 'f'.
If that's not compiled into a direct write (even to the point of keeping
the value virtual unless if it's actually needed in contiguous memory)
then there's something wrong with the optimiser.
> To most, that won't ever seem like a problem. But for folks who are
> working with Vector types or Matrix implementations, that's something to
> scream about. In a nutshell, any struct wider than a register that is
> populated in the 100's to 1000's is wasting cycles needlessly.
I've never liked doing that - if you're going to have very large vectors
or matrices, it's usually better just to switch to a programmatic model
(run-time sized) rather than keeping it parametric (templated). At some
point you're spending more time compiling than you are executing.
This requires duplication of a lot of code, which is unacceptable. I've
been thinking about this problem for a long time and I still have no
solution to it (mixins are so very not the right way); perhaps we're
misconsidering how parametric and programmatic types should interact.
Let's say that the values used in the constructor of a type are
recorded. So we might have (excuse the language):
#!/usr/bin/moki --version=1
#using: #moki.(size, static array);
Matrix := #class
{
data : static array of (type, rows, cols);
// No content needed.
#this (type, rows : size, cols : size);
};
Now "rows" and "cols" are both automatically constant properties of any
created Matrix. If our algorithm requires certain limitations on the
matrix, we can "specialise" it:
transpose (matrix : Matrix (type, rows, rows)) : Matrix (type, rows, rows)
...
And the compiler can generally optimise the Matrix like it were
parametric - it could even apply discretionary optimisation so that it
doesn't waste compile time on what doesn't even matter - but you could
still use it programmatically.
> So that brings us to something like this:
>
> struct Foo{
> int a,b,c;
> this(int a,int b,int c){
> this.a = a;
> this.b = b;
> this.c = c;
> }
> }
>
> Foo f = Foo(1,2,3);
>
> Ambiguity aside, this fixes encapsulation, gives a familiar syntax, and
> almost fixes the allocation issues. (see below)
There's still an implied copy, but again, that shouldn't be relevant for
any properly-working optimiser.
>>
>>> - Something that is disambiguated from static opCall
>> <snip>
>>
>> Do you mean that constructors for structs should have a notation
>> distinct from S(...)?
>>
>
> Well, I think it's one of the reasons why we don't have ctors for
> structs right now. The preferred syntax for a "struct ctor" would
> probably be this:
>
> S foo = S(a,b,c);
>
> Which is indistinct from "static opCall". Throwing 'new' in there
> wouldn't work either, since that would be a dynamic allocation:
>
> S* foo = new S(a,b,c);
"new <struct>" didn't used to parse, and I argued then that it shouldn't
because everywhere else the "new" operator exactly described the type it
would create. If this special case were removed, it could work as a
constructor call.
But really I think static opCall should just be killed off.
> So that leaves us with "something else" that provides both a way to
> invoke a ctor, yet allocates the data on the stack and doesn't force you
> to create an additional copy:
>
> S foo(a,b,c); // c++ style
> S foo = stackalloc S(a,b,c); // alloca() style (in place of new)
> S foo = new(stack) S(a,b,c): // another idea
>
More information about the Digitalmars-d-announce
mailing list