www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - struct vs class for small math types?

reply Bill Baxter <wbaxter gmail.com> writes:
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
Oct 24 2006
next sibling parent reply "Andrey Khropov" <andkhropov_nosp m_mtu-net.ru> writes:
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
Oct 24 2006
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
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
Oct 24 2006
prev sibling parent reply Mike Parker <aldacron71 yahoo.com> writes:
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); }
Oct 24 2006
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
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
Oct 24 2006