Thread overview
struct vs class for small math types?
Oct 24, 2006
Bill Baxter
Oct 24, 2006
Andrey Khropov
Oct 25, 2006
Bill Baxter
Oct 25, 2006
Mike Parker
Oct 25, 2006
Bill Baxter
October 24, 2006
Seems like struct is the way to go for implementing small math types like 3D vectors or quaternions that have POD semantics.

However, for example, with 4 doubles in a quaternion we're talking 32 bytes for just one of those guys.  Normally in C++ I would write operator overloads and such using 'const quat&' as the argument type to avoid copying the data on the stack.  Is there a way to do that in D? Should I be worried?  General rule of thumb for C++ I got from somewhere (a Scott Meyers book?) is, if the data is bigger than a pointer, pass it by const reference rather than by value.

The lack of constructors or usable initializers seems another issue for structs.  I guess I'm supposed to use static opCall to make something that looks like a constructor:

static vector opCall(double x, double y, double z)
{
   vector ret;
   ret.x = x; ret.y = y; ret.z = z;
   return ret;
}

and opAdd then looks like
vector opAdd(vector v) { return vector(x+v.x, y+v.y, z+v.z); }

which makes the syntax for opAdd and the rest not so bad, but makes me more worried about extra temporaries created in an expression like vector c = a + b.

(Also it took me a while to realize I could use static opCall for this purpose.  I don't think it as obvious as would be using the same constructor syntax as classes -- which is what I tried first).

--bb
October 24, 2006
Bill Baxter wrote:

> Seems like struct is the way to go for implementing small math types like 3D vectors or quaternions that have POD semantics.

Have you looked at what's already available in this area (small math types):

http://www.dsource.org/projects/helix/wiki ?


> However, for example, with 4 doubles in a quaternion we're talking 32 bytes for just one of those guys.  Normally in C++ I would write operator overloads and such using 'const quat&' as the argument type to avoid copying the data on the stack.  Is there a way to do that in D? Should I be worried?

I think small functions like addition and such should be inlined anyway so it shouldn't be an issue. However this depends on a particular compiler implementation quality.

> The lack of constructors or usable initializers seems another issue for structs.  I guess I'm supposed to use static opCall to make something that looks like a constructor:
> 
> static vector opCall(double x, double y, double z)
> {
>    vector ret;
>    ret.x = x; ret.y = y; ret.z = z;
>    return ret;
> }

I doesn't really understand why shouldn't constructors for structs be allowed: look at C# - it's perfectly legal there.

People will use this static opCall hack anyway for that purpose.

> and opAdd then looks like
> vector opAdd(vector v) { return vector(x+v.x, y+v.y, z+v.z); }
> 
> which makes the syntax for opAdd and the rest not so bad, but makes me more worried about extra temporaries created in an expression like vector c = a + b.

C++ doesn't address this problem too, but good compilers may optimize temporaries away. Another option could be to use expression templates but D doesn't support them as there's no possibility to overload '=' operator.

-- 
'non-optimal' is a politically correct term for s*it
October 25, 2006
Andrey Khropov wrote:
> Bill Baxter wrote:
> 
>> Seems like struct is the way to go for implementing small math types like 3D
>> vectors or quaternions that have POD semantics.
> 
> Have you looked at what's already available in this area (small math types): 
> 
> http://www.dsource.org/projects/helix/wiki ?
> 

Yeh, that's pretty good.  The great thing about 3D math routines though is that everyone can have their own.  :-)  Porting my own C++ classes over is more an exercise in figuring out how to work with D than anything else, though.  Maybe I'll use helix when I'm satisfied that I've got it.  The interface and coding conventions look pretty similar to my own, and it looks quite complete.  Actually the more I look at it the more I think I already get it, so I should just use Helix and move on to more challenging tasks.

Thanks for the link.

>> However, for example, with 4 doubles in a quaternion we're talking 32 bytes
>> for just one of those guys.  Normally in C++ I would write operator overloads
>> and such using 'const quat&' as the argument type to avoid copying the data
>> on the stack.  Is there a way to do that in D? Should I be worried?
> 
> I think small functions like addition and such should be inlined anyway so it
> shouldn't be an issue. However this depends on a particular compiler
> implementation quality.

Ok.  I was hoping this was the answer.  And hopefully it actually works out to be true in practice with real compilers, too.

In the event that it's not the case, I assume I can always just make a parameter be a (Matrix44f*) and make users deal with having to pass (&m) as the value.  At least inside the function I can use '.' and pretend m is not a pointer!

Hmm I wonder if automatic conversion of value types to their corresponding pointer types would be a good idea?

Then you could have:
void doSomething(Matrix44f *a, Matrix44f *b)
{
  ...
}
and just call it like:

Matrix44f a,b;
doSomething(a,b);

That implicit conversion could also maybe get rid of that annoying & here:

  foreach(i; &a.iter) {..}



> C++ doesn't address this problem too, but good compilers may optimize
> temporaries away. Another option could be to use expression templates but D
> doesn't support them as there's no possibility to overload '=' operator.

I think this has been discussed before, but if you're willing to use ~= as your assignment operator or call some function like "eval", then you should be able to get pretty much the same benefit as with expression templates.

  x ~= a + b + c + d;
or
  x = eval(a + b + c + d);

I seem to recall there was something else holding back expression templates, though.

--bb
October 25, 2006
Bill Baxter wrote:
> 
> However, for example, with 4 doubles in a quaternion we're talking 32 bytes for just one of those guys.  Normally in C++ I would write operator overloads and such using 'const quat&' as the argument type to avoid copying the data on the stack.  Is there a way to do that in D? 

Use inout arg types. Doesn't give you const, but gives you reference semantics:

void func(inout MyStruct ms)
{
   ms.x = 10;
   ms.y = 20;
}

void main()
{
   MyStruct ms;
   func(ms);
   writefln(ms.x, ", ", ms.y);
}
October 25, 2006
Mike Parker wrote:
> Bill Baxter wrote:
>>
>> However, for example, with 4 doubles in a quaternion we're talking 32 bytes for just one of those guys.  Normally in C++ I would write operator overloads and such using 'const quat&' as the argument type to avoid copying the data on the stack.  Is there a way to do that in D? 
> 
> Use inout arg types. Doesn't give you const, but gives you reference semantics:
> 
> void func(inout MyStruct ms)
> {
>    ms.x = 10;
>    ms.y = 20;
> }
> 
> void main()
> {
>    MyStruct ms;
>    func(ms);
>    writefln(ms.x, ", ", ms.y);
> }

Ah, right.  That is the equivalent of &, isn't it.  Passing pointers doesn't give me const either so I guess it's ok.

Wasn't D going to get const-by-default at some point?  I would like that.

--bb