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