Jump to content
  • Advertisement
Sign in to follow this  
rozz666

[C++, math] vector

This topic is 3828 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

What's the best way to implement a 3D vector class? It has to be: 1) Portable 2) Compatible with array T[3], e.g. for using in vertex buffers etc. 3) As straight-forward to use as possible Because the compiler can place struct members in any order and add paddings, then a struct with 3 members does not satisfy the second condition. I came up with this:
template <typename T>
class vec3 {
public:

    typedef T value_type;
    typedef T value_reference;
    typedef T const_value_reference;
    typedef vec3<T>& reference;
    typedef const vec3<T>& const_reference;

    value_type xyz[3];
    
    // accessors

    value_reference x() { return xyz[0]; }

    // should it return a value instead of const reference?
    const_value_reference x() const { return xyz[0]; }

    value_reference y() { return xyz[1]; }
    const_value_reference y() const { return xyz[1]; }

    value_reference z() { return xyz[2]; }
    const_value_reference z() const { return xyz[2]; }

    // are these accessors more apropriate?

    value_reference x(value_type p0) { return xyz[0] = p0; }
    value_reference y(value_type p1) { return xyz[1] = p1; }
    value_reference z(value_type p2) { return xyz[2] = p2; }

    // constructors, operators, etc.
};
Is it guarateed that sizeof(vec3<T>) == sizeof(vec3<T>::xyz)? Should I return references in acessors or, values or void?

Share this post


Link to post
Share on other sites
Advertisement
The compiler can't reorder the variables, but you're right about the padding. In practice though, I doubt you'll encounter a situation where that's the case when using float or double. As a precaution, I put a compile-time check at the end of my vec3 class that static asserts something like:

static_assert( sizeof(vec3<float>) == sizeof(float) * 3 );

I use a combination of these two ideas:

http://www.gamedev.net/community/forums/topic.asp?topic_id=426538
http://www.gamedev.net/community/forums/topic.asp?topic_id=261920

Basically I store x, y, z members, compile-time assert that it can be treated as a contiguous array, and get array-like access using the 'slick trick' in the second link.

[Edited by - mfawcett on June 17, 2008 4:47:47 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by rozz666
Because the compiler can place struct members in any order


no it can't.

Quote:
and add paddings, then a struct with 3 members does not satisfy the second condition.


It won't add paddings for floats.

Quote:
Is it guarateed that sizeof(vec3<T>) == sizeof(vec3<T>::xyz)?


assuming you don't use any virtual functions.... as mfawcett says, it might be worth a compile time assert - though to be honest you could just write unit tests....

Quote:
Should I return references in acessors or, values or void?


I can't see any benefit whatsoever for using accessors here. This is non-standard, but I'd be suprised if you can find a compiler in which it won't work....


template <typename T>
class vec3 {
public:

typedef T value_type;
typedef T value_reference;
typedef T const_value_reference;
typedef vec3<T>& reference;
typedef const vec3<T>& const_reference;

union {
value_type xyz[3];
struct
{
value_type x;
value_type y;
value_type z;
};
};

// constructors, operators, etc.
};


Share this post


Link to post
Share on other sites
Quote:
Original post by RobTheBloke
Quote:
Original post by rozz666
Because the compiler can place struct members in any order


no it can't.

You're both right. Members declared sequentially in an aggregate (class or struct) type, without an intervening access specifier) are arranged in declaration order -- that is, members declared later have higher addresses than those declared earlier.

However, members separated by an access specifier have unspecified ordering. Essentially, access specifiers creates 'groups' of members; within the group members have a defined order, but the groups themselves may not be allocated in declaration order.

This only applies to non-static members of aggregate types that are not unions. See section 9.2.10 of the standard.

Since the OP doesn't have member data that crosses an access specifier boundary, this is a non-issue. I've never found myself in a situation where it actually bit me, but its worth noting if one is actually pedantically concerned about standards compliance.

Quote:

Quote:
and add paddings, then a struct with 3 members does not satisfy the second condition.


It won't add paddings for floats.

Most compilers won't, because most implementations default to four-byte alignment and four-byte floats. That doesn't mean they can't (if you were to change the default to 8 bytes for some silly reason).

This is not likely to be a problem, but if it is it needs to be controlled via compiler-specific directives.

Quote:

Is it guarateed that sizeof(vec3<T>) == sizeof(vec3<T>::xyz)?

No. But depending on how you implement things, it may be a safe assumption in practice.

As you may gather from the extremely pedantic comments I've made above, actually being 'portable' is a really annoying thing to achieve sometimes. In practice it is often better to rely on at least some implementation-specific details, because you're probably more tied to an implementation than you might think.

Quote:

Should I return references in acessors or, values or void?

I'd return values, in cases like this there doesn't seem to be a big benefit to returning references.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!