3

I've got two classes: a template class, and a regular class that inherits from it:

template <int N> class Vector
{
    float data[N];
    //etc. (math, mostly)
};

class Vector3 : public Vector<3>
{
    //Vector3-specific stuff, like the cross product
};

Now, I'd like to have x/y/z member variables in the child class (full members, not just getters - I want to be able to set them as well). But to make sure that all the (inherited) math works out, x would have to refer to the same memory as data[0], y to data[1], etc. Essentially, I want a union, but I can't declare one in the base class because I don't know the number of floats in the vector at that point.

So - can this be done? Is there some sort of preprocessor / typedef / template magic that will achieve what I'm looking for?

PS: I'm using g++ 4.6.0 with -std=c++0x, if that helps.

Edit: While references would give the syntax I'm looking for, the ideal solution wouldn't make the class any bigger (And references do - a lot! A Vector<3> is 12 bytes. A Vector3 with references is 40!).

timrau
  • 22,578
  • 4
  • 51
  • 64
Xavier Holt
  • 14,471
  • 4
  • 43
  • 56
  • Why do you want this, instead of just having getters and setters which refer to the appropriate values of data? – Dave S Sep 07 '11 at 17:46
  • why getters don't solve your problem? – Andriy Tylychko Sep 07 '11 at 17:46
  • Why don't you simply use getters AND setters? It is significatly less magical than any other possible solution. e.g. `float x() const; float x(float newx);` – Kristóf Marussy Sep 07 '11 at 17:48
  • @Dave - Because function-style getters and setters are obnoxious. Kristóf's jQuery-style setup is reasonably elegant, but... it's just a variable, and I wanna use an equal sign, dammit! (I've been doing a lot of Ruby programming lately - I think I'm spoiled.) – Xavier Holt Sep 07 '11 at 18:06
  • If you want to use equal signs, use reference getters. `float& x()` and `const float& x() const`. Then you could say `v.x() = 1`. You still need the extra parenthesis but it's closer. – Dave S Sep 07 '11 at 18:12
  • @Xavier: what about other things that can be done more elegantly in Ruby than in C++? to what level are you going to mimic Ruby, or jQuery or whatever else? – Andriy Tylychko Sep 07 '11 at 18:21
  • @Andy - It's a stylistic thing. I'm using C++ for speed and power, but compared to, say, Ruby it's not a terribly elegant language. So the question was really: Can I make C++ behave more elegantly without impacting its performance? In this case, the answer was yes - see Rob's template specialization answer - and I was pleasantly surprised by that. Cheers! – Xavier Holt Sep 07 '11 at 18:43

6 Answers6

8

How about:

class Vector3 : public Vector<3>
{
public:
  // initialize the references...
  Vector3() : x(data[0]), y(data[1]), z(data[2]){}
private:
  float& x;
  float& y;
  float& z;
};

Of course, if you want them to occupy the same space, then that's a different story...

With a little template magic, you can do the following...

#include <iostream>

template <int N, typename UnionType = void*> struct Vector
{
    union
    {
      float data[N];
      UnionType field;
    };

    void set(int i, float f)
    {
      data[i] = f;
    }

    // in here, now work with data
    void print()
    {
      for(int i = 0; i < N; ++i)
        std::cout << i << ":" << data[i] << std::endl;
    }
};

// Define a structure of three floats
struct Float3
{
  float x;
  float y;
  float z;
};

struct Vector3 : public Vector<3, Float3>
{
};

int main(void)
{
  Vector<2> v1;
  v1.set(0, 0.1);
  v1.set(1, 0.2);
  v1.print();

  Vector3 v2;
  v2.field.x = 0.2;
  v2.field.y = 0.3;
  v2.field.z = 0.4;
  v2.print();

}

EDIT: Having read the comment, I realise what I posted before was really no different, so a slight tweak to the previous iteration to provide direct access to the field (which is what I guess you are after) - I guess the difference between this and Rob's solution below is that you don't need all the specializations to implement all the logic again and again...

Nim
  • 33,299
  • 2
  • 62
  • 101
  • Of course, this solution means that we get from `sizeof(float)*3` (probably 6 bytes) to `sizeof(float*3) + padding + sizeof(float*)*3` (probably 32 bytes), thus a 5-fold increase in the size of the object. – Matthieu M. Sep 07 '11 at 17:57
  • what's the benefit of this approach in comparison to setters/getters? – Andriy Tylychko Sep 07 '11 at 17:58
  • @Nim: so it would be honest to warn OP about this. Seems he mixes up languages – Andriy Tylychko Sep 07 '11 at 18:18
  • @AndyT - I think he realises that from his last comment.. :) – Nim Sep 07 '11 at 18:23
  • @AndyT: One advantage is slightly nicer syntax from the user's perspective (`my_vec.x = 5` rather than `my_vec.setX(5)`). – Oliver Charlesworth Sep 07 '11 at 18:31
  • @Matthieu: That's an artefact of your implementation. References do not necessarily require storage. – MSalters Sep 08 '11 at 08:30
  • @MSalters: (*to be honest, it's top off my hat computation*) I didn't know that a compiler would be allowed to optimize out this. It makes sense here, since it's an internal reference... do you know where it is precised in the Standard ? – Matthieu M. Sep 08 '11 at 12:25
  • @Matthieu : 8.3.2/4 "It is unspecified whether or not a reference requires storage (3.7)." – MSalters Sep 08 '11 at 13:07
  • @MSalters: thanks ! It looks like a great optimization in this case, though I guess it would require inline constructors. – Matthieu M. Sep 08 '11 at 15:29
7

How about template specialization?

template <int N> class Vector
{
  public:
  float data[N];
};

template <>
class Vector<1>
{
  public:
  union {
    float data[1];
    struct {
      float x;
    };
  };
};

template <>
class Vector<2>
{
  public:
  union {
    float data[2];
    struct {
      float x, y;
    };
  };
};


template <>
class Vector<3>
{
  public:
  union {
    float data[3];
    struct {
      float x, y, z;
    };
  };
};

class Vector3 : public Vector<3>
{
};

int main() {
  Vector3 v3;
  v3.x;
  v3.data[1];
};


EDIT Okay, here is a different approach, but it introduces an extra identifier.
template <int N> class Data
{
  public:
  float data[N];
};

template <> class Data<3>
{
  public:
  union {
    float data[3];
    struct {
      float x, y, z;
    };
  };
};

template <int N> class Vector
{
  public:
  Data<N> data;
  float sum() { }
  float average() {}
  float mean() {}
};

class Vector3 : public Vector<3>
{
};

int main() {
  Vector3 v3;
  v3.data.x = 0; // Note the extra "data".
  v3.data.y = v3.data.data[0];
};
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • your new solution looks nice, however are you sure this is well-defined ? I am unsure of the Standard here, I know there are specific provisions for struct sharing the same leading sequence, but I know nothing about superposing structs and arrays. – Matthieu M. Sep 07 '11 at 18:03
  • Haha - YES! That's perfect. Makes the "named" members directly available. Doesn't increase the size of the class. Exactly what I was looking for. That is some deep, dark template magic, my friend! – Xavier Holt Sep 07 '11 at 18:26
  • 1
    Won't you now have to replicate all the other stuff in every single specialization? – Oliver Charlesworth Sep 07 '11 at 18:33
  • @Oli - Hm... If by "all the other stuff" you mean the functions defined on `template class Vector`, then... maybe? I had only tested size and member access, and now that I'm trying it, I can't seem to access `Vector::whatever()` via `Vector3.whatever()`... I'm looking for a workaround now, but that could kill this answer - the whole reason the `Vector` class exists is so I would only have to declare (and code) common vector functions once. – Xavier Holt Sep 07 '11 at 19:01
  • Sigh... No it didn't. You can't access Data's members from Vector's functions, making it useless as a function repository - I guess because Vector doesn't know which implementation of Data it would use at that point? I'll keep trying things, but I'll probably just end up resorting to an ugly #include hack... – Xavier Holt Sep 07 '11 at 21:56
  • It sounds like you should give up on the template approach and use the reference approach. Good luck -- sorry I couldn't help. – Robᵩ Sep 08 '11 at 03:36
3

Here's one possibility, cribbed from my answer to this question:

class Vector3 : public Vector<3>
{
public:
    float &x, &y, &z;

    Vector3() : x(data[0]), y(data[1]), z(data[2]) { }
};

This has some problems, like requiring you to define your own copy constructor, assignment operator etc.

Community
  • 1
  • 1
Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
1

You can make the following:

template <int N> struct Vector
{
    float data[N];
    //etc. (math, mostly)
};
struct Vector3_n : Vector<3>
{
    //Vector3-specific stuff, like the cross product
};
struct Vector3_a
{
    float x, y, z;
};
union Vector3
{
    Vector3_n n;
    Vector3_a a;
};

Now:

Vector3 v;
v.n.CrossWhatEver();
std::cout << v.a.x << v.a.y << v.a.z

You could try the anonymous union trick, but that is not standard nor very portable.

But note that with this kind of union it is just too easy to fall into undefined behaviour without even noticing. It will probably mostly work anyway, though.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
1

I wrote a way a while back (that also allowed getters/setters), but it was such a non-portable garrish hack that YOU REALLY SHOULD NOT DO THIS. But, I thought I'd throw it out anyway. Basically, it uses a special type with 0 data for each member. Then, that type's member functions grab the this pointer, calculate the position of the parent Vector3, and then use the Vector3s members to access the data. This hack works more or less like a reference, but takes no additional memory, has no reseating issues, and I'm pretty sure this is undefined behavior, so it can cause nasal demons.

class Vector3 : public Vector<3>
{
public: 
    struct xwrap {
        operator float() const;
        float& operator=(float b);
        float& operator=(const xwrap) {}
    }x;
    struct ywrap {
        operator float() const;
        float& operator=(float b);
        float& operator=(const ywrap) {}
    }y;
    struct zwrap {
        operator float() const;
        float& operator=(float b);
        float& operator=(const zwrap) {}
    }z;
    //Vector3-specific stuff, like the cross product 
};
#define parent(member) \
(*reinterpret_cast<Vector3*>(size_t(this)-offsetof(Vector3,member)))

Vector3::xwrap::operator float() const {
    return parent(x)[0];
}
float& Vector3::xwrap::operator=(float b) {
    return parent(x)[0] = b;
}
Vector3::ywrap::operator float() const {
    return parent(y)[1];
}
float& Vector3::ywrap::operator=(float b) {
    return parent(y)[1] = b;
}
Vector3::zwrap::operator float() const {
    return parent(z)[2];
}
float& Vector3::zwrap::operator=(float b) {
    return parent(z)[2] = b;
}
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
0

To finish off an old question: No. It makes me sad, but you can't do it.

You can get close. Things like:

Vector3.x() = 42;

or

Vector3.x(42);

or

Vector3.n.x = 42;

or even

Vector3.x = 42; //At the expense of almost quadrupling the size of Vector3!

are within reach (see the other answers - they're all very good). But my ideal

Vector3.x = 42; //In only 12 bytes...

just isn't doable. Not if you want to inherit all your functions from the base class.

In the end, the code in question ended up getting tweaked quite a bit - it's now strictly 4-member vectors (x, y, z, w), uses SSE for vector math, and has multiple geometry classes (Point, Vector, Scale, etc.), so inheriting core functions is no longer an option for type-correctness reasons. So it goes.

Hope this saves someone else a few days of frustrated searching!

Xavier Holt
  • 14,471
  • 4
  • 43
  • 56