Vector initialisation

25 May 2011

Oftentimes while doing this crazy business we refer to as “programming”, I find myself with the want to create lists of stuff. Not just any lists of stuff though, lists that I can set in code that never needs to change. A static array of stuff, if you will.

Luckily, I live in the day and age where C++ happens to be backwards-compatible with C, which has a way of setting an array to just such a list of stuff: static array initialisation.

int array[] = 
{
	1,
	2,
	3,
	4,
	56,
};

And that’s all well and good if what I want to use is an array of stuff. But what if what I really want to use is a solution grounded in the standard C++ libraries? What if I want to have a vector that starts out with certain contents that I can then manipulate as my twisted and purile mind sees fit? What if I wanted to use C++ classes that were more than just structs or Plain Old Data Structures (PODS)? Why, then I would need a way to initialise a vector! What’s that? There isn’t one?

 

Okay, I speak too soon. C++ 0x has/will have one. I never know whether to speak in the future or present tense with that one, as it’s technically not really ratified yet but a lot of people are getting sick of waiting. Anyways, with C++ more-shiny-version the syntax is simple and delicious:

 

//declare a class that takes an initializer list in a constructor
class ShinyClass
{
public:
	ShinyClass(std::initializer_list<int> list);
};

//use the list
void main()
{
	ShinyClass array = 
	{
		1,
		2,
		3,
		44,
		56,
	};
}

 

 

However, for those poor souls who, like myself, are stuck with the version of C++ that actually has an official standard and universal compiler support, this snippet is nothing more than a tease. Generally, the closest we can get is to first initialise an array and then copy the contents in to a vector like so:

const int array[] = {1, 2, 33, 46, 55};
const std::vector<const int> vec(array, array+sizeof(array)*sizeof(int) );

This is quick, but it doesn’t really work with anything that isn’t a struct or a PODS, and doesn’t play all that nice with constructors. There’s a Boost alternative, Boost.Assign, that can be used for initializing vectors. Frankly though, Boost just adds so much compile time and bloat to anything that uses it that I’m not even going to show a snippet of it. Feel free to look it up yourself if you’re interested.

 

The final solution is one which I rolled myself after seeing a similar (but more evil one) on a Stack Overflow question.

template <typename T>
class vector_init
{
public:
    vector_init(const T& val)
    {
        vec.push_back(val);
    }
    inline vector_init& operator()(T val)
    {
        vec.push_back(val);
        return *this;
    }
    inline std::vector<T> end()
    {
        return vec;
    }
private:
    std::vector<T> vec;
};

 

Use it like so:

std::vector<int> testVec = vector_init<int>(1)(2)(3)(4)(5).end();

It won’t relieve all your initialization worries, but it will certainly help with calling classes with specific C++ functions and filling vectors with pre-calculated content. Enjoy!

 | Posted by | Categories: Blog |

Tricky Vector3

8 May 2011

Anyone who’s done any work in 3D is familiar with some sort of Vector class. I’m not talking about STL’s std::vector, which is used more as a list, no I’m talking about specialised maths classes for the purpose of doing linear algebra: Vector2, Vector3, Vector4 and their 2D counterparts Matrix2x3, Matrix3x3 and Matrix4x4. Vector3 and Vector4 I tend to use for colours as well, where x, y, z, w maps to r, g, b, a. But I’ve always wanted my Vector classes to be able to use either of those names, and be able to use the Vector as an array of values.

i.e.:

Vector3 position;

position.X = 1.0f;

 

Vector3 color;

color.R = 1.0f;

 

Vector3 something;

something[0] = 1.0f;

are all valid code.

 

The way I’d been doing it before was using an anonymous union and an anonymous struct:

 

struct Vector3

{

    union

    {

        struct {float X; float Y; float Z;}

        struct {float R; float G; float B;}

        float array[3];

    };

    const float& operator[](size_t i) const { return array[i]; }

    float& operator[](size_t i) { return array[i]; }

};

And this did what I needed it to, but with an annoying side effect: I kept getting compiler warnings about my anonymous structs.

 

Anonymous structs are not a part of the C++ standard. The reason why this is so evades me, but it seems the popular compilers – Visual Studio and gcc – implemented extensions for supporting them a while ago. However, they still throw that warning at me, and I’m a “warnings as errors” kind of guy, so I determined to find a fix for this. Eventually.

 

Recently I stumbled upon a post on the GameDev forums which described how to do such a thing, and after staring at the code until it felt like my eyes were bleeding I came to understand it. So here is a standards-compliant Vector class:

 

struct Vector3

{

private:

    typedef float Vector3::* const locations[3];

    static const locations v;

public:

    union { float X; float R; };

    union { float Y; float G; };

    union { float Z; float B; };

    

    const float& operator[](size_t i) const { return this->*v[i]; }

    float& operator[](size_t i) { return this->*v[i]; }

};

const Vector3::locations Vector3::v = { &Vector3::X, &Vector3::Y, &Vector3::Z };

 

Confused? I know I was. Here’s an explanation:

The Vector3::* type is a special type called a pointer-to-member. I find it useful to think of it as an offset into any class of type Vector3 (e.g. the value for v[0] is found 2-bytes from the start of the ‘this’ pointer, although that’s apparently not technically true. The pointer-to-member operator ->* is used to access the value of the pointer-to-member in a specific instance of the class.

So in the Vector3 class there’s a constant static list of 3 pointer-to-members that let us overload the [] operator and treat X, Y and Z as if they were arrays. And the great thing is, there’s almost no memory overhead for it: a good compiler will recognise the const allocations and optimise the pointers away completely.

 

The anonymous unions allow you to refer to each component by either name (and them both being the same type removes any weirdness that might happen from assigning to one and then the other), and C++ doesn’t allow compilers to re-order or place extra bytes between members for alignment so it’s safe to treat <code>&X</code> as the start of a float array of length 3.

 

And that’s my new Vector3 class. For fun, here’s a templated version

template <typename T>

struct Vector3_t

{

private:

    typedef T Vector3_t<T>::* const locations[3];

    static const locations loc;

public:

    union { T X; T R; };

    union { T Y; T G; };

    union { T Z; T B; };

    const T& operator[](size_t i) const { return this->*loc[i]; }

    T& operator[](size_t i) { return this->*loc[i]; }

};

template<typename T>

const typename Vector3_t<T>::locations Vector3_t<T>::loc = { &Vector3_t<T>::X, &Vector3_t<T>::Y, &Vector3_t<T>::Z };

 

typedef Vector3_t<float> Vector3;

see here for an explanation of that second typename in the array initialisation.

 | Posted by | Categories: Blog |