Related
Sometimes there is a need to have a Vector3f class, which has x, y and z members, and can be indexed as a float[3] array at the same time (there are several questions here at SO already about this).
Something like:
struct Vector3f {
float data[3];
float &x = data[0];
float &y = data[1];
float &z = data[2];
};
With this, we can write this:
Vector3f v;
v.x = 2.0f;
v.y = 3.0f;
v.z = 4.0f;
glVertex3fv(v.data);
But this implementation is bad, because references take space in the struct (which is quite unfortunate. I don't see any reason why references cannot be removed in this particular case, maybe it is missed optimization from the compiler's part).
But, with [[no_unique_address]] I had this idea:
#include <new>
template <int INDEX>
class Vector3fProperty {
public:
operator float() const {
return propertyValue();
}
float &operator=(float value) {
float &v = propertyValue();
v = value;
return v;
}
private:
float &propertyValue() {
return std::launder(reinterpret_cast<float*>(this))[INDEX];
}
float propertyValue() const {
return std::launder(reinterpret_cast<const float*>(this))[INDEX];
}
};
struct Vector3f {
[[no_unique_address]]
Vector3fProperty<0> x;
[[no_unique_address]]
Vector3fProperty<1> y;
[[no_unique_address]]
Vector3fProperty<2> z;
float data[3];
};
static_assert(sizeof(Vector3f)==12);
So, basically, I have properties in the struct, which handles the access to x, y and z. These properties should not take space, as they are empty, and have the attribute of [[no_unique_address]]
What do you think about this approach? Does it have UB?
Note, this question is about a class, for which all these are possible:
Vector3f v;
v.x = 1;
float tmp = v.x;
float *c = v.<something>; // there, c points to a float[3] array
If this is going to live in a header, and you have some confidence in your compiler's optimizing capabilities, you can probably stick to a plain-old operator[]() overload and expect the compiler to be smart enough to elide the call and return the element that you want. E.g.:
class Vec3f {
public:
float x;
float y;
float z;
float &operator[](int i) {
if(i == 0) {
return x;
}
if(i == 1) {
return y;
}
if(i == 2) {
return z;
}
}
};
I tossed this into Compiler Explorer (https://godbolt.org/z/0X4FPL), which showed clang optimizing the operator[] call away at -O2, and GCC at -O3. Less exciting than your approach, but simple and should work under most circumstances.
But this implementation is bad, because references take space in the struct (which is quite unfortunate. I don't see any reason why references cannot be removed in this particular case, maybe it is missed optimization from the compiler's part).
This looks like a complicated issue. Standard-layout classes have to be compatible between each other. And so compilers are not allowed to eliminate any member, regardless of how they are defined. For non standard-layout? Who knows. For more info read this: Do the C++ standards guarantee that unused private fields will influence sizeof?
From my experience compilers never remove class members, even if they are "unused" (e.g. formally sizeof does use them).
Does it have UB?
I think this is UB. First of all [[no_unique_address]] only means that the member need not have a unique address, not that it must not have a unique address. Secondly it is not clear where your data member starts. Again, compilers are free to use or not paddings of previous [[no_unique_address]] class members. Meaning your accessors may access incorrect piece of memory.
Another problem is that you want to access "outer" memory from the "inner" class. AFAIK such thing is also UB in C++.
What do you think about this approach?
Assuming it is correct (which is not) I still don't like it. You want getters/setters but C++ does not support this feature. So instead of doing those weird, complicated constructs (imagine other people maintaining this code) how about simply do
struct Vector3f {
float data[3];
float x() {
return data[0];
}
void x(float value) {
data[0] = value;
}
...
};
You say this code is ugly. Maybe it is. But it is simple, easy to read and maintain. There's no UB, it does not depend on potential hacks with unions, and does exactly what you want, except for beauty requirement. :)
GLM implements this kind of functionality using anonymous structs inside an anonymous union
I can't personally guarantee that this is standard-compliant, but most major compilers (MSVC, GCC, Clang) will support this idiom:
struct Vector3f {
union {
struct {
float x, y, z;
};
struct {
float data[3];
};
};
Vector3f() : Vector3f(0,0,0) {}
Vector3f(float x, float y, float z) : x(x), y(y), z(z) {}
};
int main() {
Vector3f vec;
vec.x = 14.5;
std::cout << vec.data[0] << std::endl; //Should print 14.5
vec.y = -22.345;
std::cout << vec.data[1] << std::endl; //Should print -22.345
std::cout << sizeof(vec) << std::endl; //On most platforms will print 12
}
The non-standard behavior is in the anonymous struct used to group the letters together, which GCC will issue a warning about. As far as I know, the union itself should be valid, because the datatypes are all identical, but you should still check with your compiler documentation if you're unsure whether this is valid or not.
As an added convenience, we can also overload the brackets operator to shorten our syntax a little:
struct Vector3f {
/*...*/
float& operator[](size_t index) {return data[index];}
float operator[](size_t index) const {return data[index];}
};
int main() {
Vector3f vec;
vec.x = 14.5;
std::cout << vec[0] << std::endl; //Should print 14.5
vec.y = -22.345;
std::cout << vec[1] << std::endl; //Should print -22.345
std::cout << sizeof(vec) << std::endl; //On most platforms will print 12
}
Just for clarity, accessing inactive members in the way I am is valid according to the C++ standard, because those members share a "common subsequence":
If two union members are standard-layout types, it's well-defined to examine their common subsequence on any compiler.
CPP Reference: Union Declaration
Because x and data[0] are
Both floats,
Both occupy the same memory,
Are both standard Layout types as the standard defines them,
It's perfectly valid to access one or the other regardless of which is currently active.
As stated, this is impossible: pointer arithmetic is defined only within an array, and there’s no way (without putting a reference in the class, which takes up space in current implementations) to have v.x refer to an array element.
I read somewhere a piece of code for defining 3d coordinate. So, they used x, y & z coordinate like this below:
union
{
struct
{
float x, y, z;
};
float _v[3];
};
My question is that why a union is required here and also what is the advantage of using struct with an array?
Important note: this construct leads to undefined behavior. What follows is a description of its authors' intentions, which, unfortunately, many compilers translate to precisely the behavior the authors expect, which in turn leads to proliferation of code like that.
union construct is not really required here: the authors used it for convenience. The reason they put a union there is to give themselves a syntax for accessing x, y, and z in two distinct ways:
By specifying the field name - that is, coord.x, coord.y, or coord.z, or
By specifying the field index - that is, coord._v[0], coord._v[1], or coord._v[2].
An approach that provides comparable functionality without running into undefined behavior is to use inline member functions for your access:
struct Vector3D {
int v[3];
int& x() { return v[0]; }
int& y() { return v[1]; }
int& z() { return v[2]; }
};
int main() {
Vector3D coord;
coord.v[0] = 5;
cout << coord.x() << endl;
coord.y() = 10;
cout << coord.v[1] << endl;
return 0;
}
Demo.
As Gill Bates says, in this way you can (maybe) access the 3 coordinates both as x, y, z and as v[0], v[1], v[2]
But as per #Richard Critten comment, this is actually an UB.
You can get the same result in a "safe" way with something like this:
struct Coordinates
{
Coordinates():x(v[0]), y(v[1]), z(v[2])
{
}
int v[3];
int& x;
int& y;
int& z;
};
i.e., using references to the array values and initializing them in constructor
Size of this struct will be obviously different (larger) than the union you show in OP
This way you can address the 3d coordinate as a struct..
foo.x;
foo.y; // etc
But it also allows you to get the 3 variables as an array while occupying the same space (that's the plan at least), accessing them like..
foo._v[0] // x, etc
Anyway, that's the idea. But the moment the struct has any sort of padding between members your array will be misaligned and you will end up reading garbage values. Long story short, this code has undefined behaviour, a bad implementation that shouldn't be used.
I am writing a small program for learning C++ and 3D.
I have already written a vertex class with usefull methods. (like Dot,Cross, etc...)
class cVector {
...
float x, y, z;
...
float dot(cVector& v);
cVector cross(cVector& v);
...
}
Now I realize OpenGL expects buffers where elements are more like a struct (VBO).
struct sVector {
float x, y, z;
}
So my vertex class is no longer useless, because if i want to manipulate data in the buffer :
1 - I need to extract data of elements in the buffer.
2 - Create a temporary instance of vertex class with the data.
3 - Use vertex class method. (Dot, cross, etc...)
4 - Put the data back to the buffer.
It's not very efficient :(.
I wonder if I should not use a struct to organize my vectors and create global functions that take a pointer to a struct as an argument.
I could handle data buffers more efficiently (just moving pointer) but I feel i would lose the "convenient power" of C++.
In every 3D C++ source code i ever see, all use class for vertex but i dont understand how they can manipulate large amount of vertex in a "struct like" buffer.
Can you help me to understand ? What is the best approach ?
The most common approach in a language like C++ is actually neither of these things.
You are more likely to encounter the following:
struct Vector3 {
union {
struct {
float x,y,z;
};
float v [3];
};
...
Vector3 (float x_, float y_, float z_) : x (x_), y (y_), z (z_) { };
float Norm (void) { return sqrt ((x * x) + (y * y) + (z * z)); }
void Normalize (void) {
float norm = Norm ();
v [0] /= norm;
v [1] /= norm;
v [2] /= norm;
}
};
The reason for this is because using anonymous unions and structs, you can treat the data as either an array of floats (v [...]) or reference the individual components by their name (x, y, z) without a lot of muss or fuss. You get the best of both worlds by using the language more intelligently.
As for the difference between a struct and a class in this particular case, there is none from the perspective of memory representation. The only real difference between a class and a struct in C++ is the default access; struct has public access by default.
When GL needs to access the object's internal memory, you would accomplish this by passing it the pointer: Vector3::v or the individual components, depending on the particular function.
For instance:
Vector3 vec (1.0f, 2.0f, 3.0f);
---------------------------------
glVertex3fv (vec.v);
and
glVertex3f (vec.x, vec.y, vec.z);
are equivalent
On a side-note, anonymous structures are a non-standard extension to C++ but supported virtually everywhere. In the case that you have a compiler that does not support them, you may have to qualify access to x, y, and z by giving the struct a name.
struct Vector3 {
union {
struct {
float x,y,z;
} s;
float v [3];
};
};
If you write your struct this way, then:
Vector3 vec;
assert (vec.v [0] == vec.s.x);
It is messier to have to qualify x that way (using an anonymous struct you can use vec.x).
There is exactly one difference between struct and class: For class the default scope is private, while for struct it is public.
So
class cVector {
...
float x, y, z; // data
...
float dot(cVector& v); // just a function
cVector cross(cVector& v); // just a function
...
}
and
struct sVector {
float x, y, z; // data
}
have exactly the same memory layout (given that x,y,z are the only members variables of cVector).
You can use &v.x to get a pointer to (x,y,z) for OpenGL, e.g. glVertex3f(&v.x);.
You can even do the following to get a pointer to a continuous sequence of vertices for usage with OpenGL:
std::vector<cVector> vertices(100);
const float* data = &(vertices[0].x);
I am writing a program that asks the user for two vectors (with a force and magnitude) and then returns the sum of the two vectors. I'm not really looking for someone to just give me the code, but I really just need some guidance on how to proceed. I feel like I really don't understand the implementation of classes/constructors yet and so I'm pretty sure I'm doing things incorrectly, or at least inefficiently. NOTE: I hope it's obvious I'm not finished. I'm just having kind of a "coder's block" :P
#include "std_lib_facilities_4.h"
class Physics_vector {
double force, magnitude, x, y, f, m;
vector<double> final;
vector<double> v;
public:
Physics_vector(double x, double y) :force(x), magnitude(y) {};
void set_vector(double f, double m);
int get_vector(vector<double> final);
double add_physics_vector();
};
void Physics_vector::set_vector(double f, double m)
{
f = force;
m = magnitude;
vector<double> final;
final.push_back(f);
final.push_back(m);
}
int Physics_vector::get_vector(vector<double> final)
{
for (int i = 0; i < 2; ++i) {
cout << final[i] << '\n';
}
return 0;
}
int main()
{
cout << "Howdy!" << '\n';
cout << "This program adds together two vectors."
<< endl;
cout << "First, enter in the force and magnitude of your first vector."
<< "\nExample: 4 7." << endl;
double user_force, user_magnitude, force, magnitude;
cin >> user_force >> user_magnitude;
Physics_vector first(user_force, user_magnitude);
first.set_vector(force, magnitude);
cout << "Next, enter in the force and magnitude of your second vector."
<< endl;
cin >> user_force >> user_magnitude;
Physics_vector second(user_force, user_magnitude);
}
EDIT: Ok, so I changed my code a little to make it cleaner (if it's not tell me). But now my problem is function calls.
class Physics_vector {
public:
Physics_vector(double x = 0, double y = 0) :x(x), y(y) {}
double get_vector(double x, double y);
private:
double x, y;
};
double Physics_vector::get_vector(double x, double y)
{
return x;
return y;
}
double add_physics_vector(vector<double> vect_1, vector<double> vect_2)
{
return 0.0;
}
int main()
{
cout << "Howdy! Please enter your first vector (direction and magnitude) ."
<< "\nExample: 1 2." << endl;
double user_direction = 0;
double user_magnitude = 0;
cin >> user_direction >> user_magnitude;
Physics_vector(user_direction, user_magnitude);
//get_vector(...aaaand I'm stuck...
}
How do I get get_vector(double x, double y) to use the x and y values from Physics_vector() as it's arguments? I'm sure this seems so rudimentary to most of you; I hate that I'm having so much trouble with classes...
Thanks in advance.
Generally, classes are used to represent a single unit of data--in your case, a vector (in the physics sense). The class itself should ideally contain only the member variables it needs to hold the data it represents and (only if absolutely necessary) any other pieces of data its member functions require to work. It appears you are trying to implement a vector that has two dimensions, so we'll start from there.
First, we need to determine the minimum required member data the class needs to perform its function. I find that thinking too hard about this does nothing but make the class overly complicated. In physics, a vector in two dimensions can be represented in multiple ways, but the most common is the Cartesian form (x, y). So, in order to implement this all that is required are those two variables. All other information about the vector can be calculated from those two numbers. To begin the declaration of the class:
class Physics_vector {
double x, y;
public:
// Constructors and member functions here...
...
You seem to have the constructor down: all it needs to do is initialize those two variables. However, we can add some additional functionality: what if we wanted to declare a Physics_vector without actually giving it a value yet? Well, then we could code the constructor to give x and y some reasonable default values when it's constructed without any values:
...
explicit Physics_vector(double x = 0, double y = 0):x(x), y(y) {}
...
The explicit keyword is to make sure that the constructor must be called explicitly, because in C++ a constructor that may take one argument also defines an implicit conversion from that argument to the class's type, and Physics_vector vec = 0; isn't a sensible conversion. Next, we'll need some way to access x and y from outside the class, so we'll make some getter and setter functions that access the two values:
...
double get_x() const {
return x;
}
double get_y() const {
return y;
}
void set_x(double x) {
this->x = x;
}
void set_y(double y) {
this->y = y;
}
...
Think of the const keyword as a promise you're making to the compiler that the member function won't modify any of the member variables. You may remove it and the class will still work, but it's usually best to put it in when it makes sense.
The syntax this->x = x; is necessary because x is declared twice in the scope of setter member functions: once in the function's arguments, and once in the classes member variables. The compiler isn't able to tell which is being referred to, so the C++ standard defines that the local declaration takes precedence. Therefore, trying to write x = x; will assign the function's argument x to itself. I noticed you did this by accident in your set_vector function (you assigned the member variable force to the function argument f, and the same thing for m and magnitude, and I don't think that's what you intended). The way to get around it is to use the this pointer (which is defined in member functions, constructors, and destructors, and always points to the current instance of the class).
Next, we'll define an add function, because it's sensible to code the addition of two Physics_vectors with the class. The function should take another Physics_vector and add it to *this, and return the result.
...
Physics_vector add(Physics_vector other) const {
return Physics_vector(x + other.x, y + other.y);
}
...
Although this next part is more advanced (and may not have been covered in your class yet), I'll put it out there anyway. What if we wanted to add two Physics_vectors like we would two doubles? In other words add Physics_vectors vec1 and vec2 like Physics_vector result = vec1 + vec2;. Well, C++ provides a way to define such an operation. We would code it like this:
...
Physics_vector operator + (Physics_vector other) const {
return Physics_vector(x + other.x, y + other.y);
}
...
We can also add some other useful functions, such as a function that returns the magnitude of the Physics_vector.
...
double magnitude() const {
return sqrt(x*x + y*y);
}
}; // End of the the declaration/definition of Physics_vector.
The sqrt function is defined in the header cmath, and computes the square root of its argument. The magical std_lib_facilities_4.h header you're including (probably created by your instructor for your convenience) may or may not have included cmath for you.
Given this class you can then implement your program like this:
int main()
{
double x1, y1, x2, y2;
cout << "Please give two vectors to add.\n";
cout << "x1: ";
cin >> x1;
cout << "\ny1: ";
cin >> y1;
cout << "\nx2: ";
cin >> x2;
cout << "\ny2: ";
cin >> y2;
Physics_vector vec1(x1, y1);
Physics_vector vec2(x2, y2);
Physics_vector result = vec1.add(vec2); // Or vec1 + vec2, if you use the operator + overload I described.
cout << "\n\nvec1 + vec2 = ("
<< result.get_x() << ", "
<< result.get_y() << ")";
}
Hope that helps you with your programmer's block!
It looks like you should probably improve your understanding of C++ classes in general. Here is a good place to start. I'll try to give you a few pointers though.
Assuming your vector class only needs to store two values you have
way to many member variables. You actually only need two doubles:
force and magnitude (do you mean magnitude and direction?). The
variables of type vector are unnecessary, as despite its name, an
std::vector is only a dynamic array.
You don't need a separate set_vector() method as you are already
setting the values for your vector in the constructor by passing
force and magnitude values in there. On the other hand you may want
to add accessors for the member variables as get_force() and
get_magnitude() as well as perhaps set_force() and
set_magnitude() if you think you may want to change these values
from outside.
For adding the vectors you should use operator overloading and
implement a method called operator+ which will let you you easily
add two vectors together by simply typing something like v3 = v1 + v2.
This is mostly a matter of style and its not necessary, but I'd
suggest you to add some kind of prefix to your member variables, so
they are easily distinguished from local variables in the code. A
popular convention is to use m_variable. Also listing your public
members first, generally makes class interface slightly easier to
read.
With all the above in mind your vector interface could look like this (I'll leave the implementation to you):
class PhysicsVector
{
public:
PhysicsVector(double force, double magnitude) : m_force(force), m_magnitude(magnitude)
{
}
double getForce() const;
double getMagnitude() const;
void setForce(double force);
void setMagnitude(double magnitude);
PhysicsVector operator+(PhysicsVector other) const;
private:
double m_force, m_magnitude;
}
If you're getting the effect you want, you're doing things fine. However I'm not really sure if that's the case. As far as I know, a vector in physics is a body having magnitude and direction.
If you're trying to design a simple Physics_vector class, I would probably stick with:
class Physics_vector{
double x, y;
public:
Physics_vector(double x, double y);
};
Or, in case you want to operate in N-dimensional space:
class Physics_vector{
unsigned int dimensions; // Optional, same as magnitudes.size();
vector<double> magnitudes;
public:
Physics_vector(unsigned int dimensions, ...);
}
Or, in the last case - simply use templates (possibly variadic if you're operating in C++11).
I'm pretty sure this is possible, because I'm pretty sure I've seen it done. I think it is awesome, but I will gladly accept answers along the lines of "this is a terrible idea because ____".
Say we have a basic struct.
struct vertex
{
float x, y, z;
};
Now, I want to implement aliases on these variables.
vertex pos;
vertex col;
vertex arr;
pos.x = 0.0f; pos.y = 0.5f; pos.z = 1.0f;
col.r = 0.0f; col.g = 0.5f; col.b = 1.0f;
arr[0] = 0.0f; arr[1] = 0.5f; arr[2] = 1.0f;
Ideally the third syntax would be indistinguishable from an array. That is, if I sent arr as a reference parameter to a function expecting an array of floats into which it will store data (eg many of the OpenGL glGet functions), it would work fine.
What do you think? Possible? Possible but stupid?
What I would do is make accessors:
struct Vertex {
float& r() { return values[0]; }
float& g() { return values[1]; }
float& b() { return values[2]; }
float& x() { return values[0]; }
float& y() { return values[1]; }
float& z() { return values[2]; }
float operator [] (unsigned i) const { return this->values_[i]; }
float& operator [] (unsigned i) { return this->values_[i]; }
operator float*() const { return this->values_; }
private:
float[3] values_;
}
Nameless nested structs in a union are not standard C++. This, however, should work:
struct Vertex
{
private:
typedef float Vertex::* const vert[3];
static const vert v;
public:
typedef size_t size_type;
float x, y, z;
const float& operator[](size_type i) const {
return this->*v[i];
}
float& operator[](size_type i) {
return this->*v[i];
}
};
const Vertex::vert Vertex::v = {&Vertex::x, &Vertex::y, &Vertex::z};
EDIT: A little more information. The struct uses an array of 3 pointer-to-data-members to access the data in the overloaded [] operators.
The line "typedef float Vertex::* const vert" means that vert is a pointer to a float member of the Vertex struct. The [3] means that it's an array of 3 of these. In the overloaded operator[], this array is indexed and the pointer-to-data-member is dereferenced and the value returned.
Additionally, this method should work regardless of packing issues - the compiler is free to pad the Vertex structure however it likes and it'll still work just fine. An anonymous union will run into problems if the floats are packed differently.
Use a union?
union vertex
{
struct { float x, y, z; };
struct { float r, g, b; };
float arr[3];
};
I wouldn't recommend it - it will lead to confusion.
Added:
As noted by Adrian in his answer, this union with anonymous struct members is not supported by ISO C++. It works in GNU G++ (with complaints about not being supported when you turn on '-Wall -ansi -pedantic'). It is reminiscent of the pre-pre-standard C days (pre-K&R 1st Edn), when structure element names had to be unique across all structures, and you could use contracted notations to get to an offset within the structure, and you could use member names from other structure types - a form of anarchy. By the time I started using C (a long time ago, but post-K&R1), that was already historical usage.
The notation shown with anonymous union members (for the two structures) is supported by C11 (ISO/IEC 9899:2011), but not by earlier versions of the C standard. Section 9.5 of ISO/IEC 14882:2011 (C++11) provides for anonymous unions, but GNU g++ (4.9.1) does not accept the code shown with -pedantic, identifying "warning: ISO C++ prohibits anonymous structs [-Wpedantic]".
Since the idea will lead to confusion, I'm not particularly concerned that it isn't standard; I would not use the mechanism for this task (and I'd be leery of using anonymous structures in a union even if it was beneficial).
A concern was raised:
The three (x-y-z, r-g-b and the array) do not necessarily align.
It is a union with three elements; the three elements start at the same address. The first two are structures containing 3 float values. There's no inheritance and there are no virtual functions to give different layouts, etc. The structures will be laid out with the three elements contiguous (in practice, even if the standard permits padding). The array also starts at the same address, and subject to 'no padding' in the structures, the elements overlap the two structures. I really don't see that there would be a problem.
References?
template<typename T>
struct vertex {
vertex() :
r(data[0]), g(data[1]), b(data[2]),
x(data[0]), y(data[1]), z(data[2])
{
}
T *operator *() {
return data;
}
const T *operator *() const {
return data;
}
T data[3];
T &r, &g, &b;
T &x, &y, &z;
};
You can get this with a union as others have mentioned. Overloading color and position onto the same structure like this may not be a good idea ( for example, adding two colors usually means you want to saturate to 1.0, whereas adding vectors happens linearly ), but overlaying a float[] on top of them like that is perfectly fine and a well accepted means of interchanging data with GL/DirectX/etc.
I recommend you avoid referring to the same member by different aliases in the same function scope, though, because this will drive you into a nasty hardware stall called a load-hit-store. In particular, avoid this if you can:
vector foo;
foo.x = 1.0f;
return foo[0] + foo[1];
Following structure will have the requested behavior:
struct vertex
{
private:
float data[3];
public:
float &x, &y, &z;
float &r, &g, &b;
vertex() : x(data[0]), y(data[1]), z(data[2]), r(data[0]), g(data[1]), b(data[2]) {
}
float& operator [](int i) {
return data[i];
}
};
I guess you can do some macro magic to get what you want.
But that will look ugly. Why do you want to use same struct, vertex for 3 different types? Why can't you define class for color?
Also keep in mind that vertex and color are not same. If you change something to vertex, that will affect the color also, if you have the same class for both.
I am not sure whether I understood the question correctly. But it looks like you need to overload the operator[] to provide array like access to your struct/class. See the example mentioned here: Operator overloading
I have a template and two Vector classes below, one crazy, one sane. The template implements a simple fixed at compile time array of values. It is designed for subclassing and uses a protected array variable to avoid you having to jump through hoops to access the array. (Some folks might not like such a design. I say, if your subclasses are calling your overloaded operators, coupling might be a good idea.)
The crazy class allows you to have member variables called x, y, z and it acts like an array for calls to glGetFloatV. The sane one just has accessor functions x(), y(), z() and still works with glGetFloatV. You can use either class as a basis for other vector objects you might pass to the OpenGL library. Although the classes below are specific to points, you can obviously just do a search/replace to turn them into a rgb color classes.
The crazy class is crazy because the cost of the syntactic sugar vec.x instead of vec.x() is 3 reference variables. That could take up a lot of space in a large application. Use the simpler sane version.
template <typename T, int N>
class FixedVector {
protected:
T arr[N];
public:
FixedVector();
FixedVector(const T* a) {
for (int i = 0; i < N; ++i) {
arr[i] = a[i];
}
}
FixedVector(const T& other) {
for (int i = 0; i < N; ++i) {
arr[i] = other.arr[i];
}
}
FixedVector& operator=(const T& other) {
for (int i = 0; i < N; ++i) {
arr[i] = other.arr[i];
}
return *this;
}
T* operator&() { return arr; }
const T* operator&() const { return arr; }
T& operator[](int ofs) {
assert(ofs >= 0 && ofs < N);
return arr[ofs];
}
const T& operator[](int ofs) const {
assert(ofs >= 0 && ofs < N);
return arr[ofs];
}
};
class CrazyPoint : public FixedVector<float, 3> {
public:
float &x, &y, &z;
CrazyPoint()
: x(arr[0]), y(arr[1]), z(arr[2])
{ arr[0] = arr[1] = arr[2] = 0.0; }
CrazyPoint(const float* a)
: x(arr[0]), y(arr[1]), z(arr[2])
{
arr[0] = a[0];
arr[1] = a[1];
arr[2] = a[2];
}
CrazyPoint(float a, float b, float c)
: x(a), y(b), z(c)
{
arr[0] = a;
arr[1] = b;
arr[2] = c;
}
};
class SanePoint : public FixedVector<float, 3> {
public:
float& x() { return arr[0]; }
float& y() { return arr[1]; }
float& z() { return arr[2]; }
SanePoint() { arr[0] = arr[1] = arr[2] = 0.0; }
SanePoint(float a, float b, float c)
{
arr[0] = a;
arr[1] = b;
arr[2] = c;
}
};
// usage
SanePoint normal;
glGetFloatV(GL_CURRENT_NORMAL, &normal);
Bad idea in my opinion, at least for the example given: the downside is that, for just about any solution to this, you're probably going to be able to freely assign "rgb" instances to/from "xyz" instances, which is probably rarely sensible or correct. ie you risk giving up some useful type safety.
Personally, for the example you give, I'd subclass rgb and xyz types from a base boost::array<float,3> or similar. So both of them inherit operator[], can be passed to functions expecting arrays, and passed with more type safety to things expecting colours/coordinates. It's often you want to treat an xyz or an rgb as an array, but rare you want to treat an xyz as an rgb or vice-versa. (rgb IS-A array: OK. xyz IS-A array: OK. rgb IS-A xyz ???? I don't think so!)
Of course that means access to x,y,z & r,g,b needs to be by accessor (forwarding to the appropriate operator[](...) ) rather than direct to the member. (You'd need C#'s properties for that).
You can try adding references to variables, like this:
struct test {
float x, y, z;
float &r, &g, &b;
test() : r(x), g(y), b(z) {}
};
But your structure gets bigger (from 12 bytes to 40 bytes).
To use [] on it, use overloading of operator[], as mentioned before.
Just a warning about using reference members pointing to value members. You need to define a copy constructor (and possibly also assignment operator), if you ever copy such an object (like transfer it by value). The default copy constructor will leave you with a copy whose reference members point to the value members of the original object, not the ones of the new object. This is certainly not something you want.
Considering you also end up with larger objects, as already pointed out, I think using accessor methods is to be preferred over reference members.
I think the poster was looking for something very simple, with no performance overhead - as you would want with something like a 3D vector class. So adding virtual functions (vtable indirection cost), additional members (memory cost), unions (each new name may require a recompile of all users), or even preprocessor magic (increased program size, cross-type equivalency) is undesirable.
Real world use case would be to take a templated Vector3 class (which could be double-based or float-based) and apply it to other scenarios in a user-friendly manner. It is likely defined with [x, y, z] members, but if you want to use it for rotations, you might want [psi, theta, phi], for speeds [dx, dy, dz], etc.
For the entire type, you can use the following to alias it at compile time:
using Rotation3 = Vector3;
But there appears to be nothing as simple or performant that you can do for aliasing the underlying variables, right?