C++ operator + and * non-const overloading - c++

I have the following tricky problem: I have implemented a (rather complicated) class which represents mathematical functions in a multiwavelet basis. Since operations like +, - and * are quite natural in this context, I have implemented overloaded operators for this class:
FunctionTree<D> operator+(FunctionTree<D> &inpTree);
FunctionTree<D> operator-(FunctionTree<D> &inpTree);
FunctionTree<D> operator*(FunctionTree<D> &inpTree);
There operators work finer in simple, non-chained operations, and even in some cases when chaining the operators. Statements like
FunctionTree<3> y = a * b + c;
FunctionTree<3> z = a * b + b;
compile and seemingly work fine. The first line is actually ok, but the second line causes valgrind to tell you grim stories about memory being freed inside already freed regions and uninitialized variables being accessed. Furthermore, a statement like
FunctionTree<D> y = a + b * c;
will not even compile, because I have not defined (an ambiguous operator taking an actual object, not a reference, as argument). Of course the solution is clear: All arguments, and methods ought to be made const, and perhaps even return a const object or reference. Unfortunately this is not possible, since NONE of the objects involved are constant during the operations! This may sound strange, but this is an unavoidable consequence of the mathematics involved. I can fake it, using const_cast, but the code is still wrong!
Is there a way to solve this problem? The only solution I have currently is to make the return objects const, thus effectively disabling the operator chaining.
Regards, .jonas.

If your objects are 1GB (I guess that's memory they allocate on the heap, not their actual sizeof size), you probably shouldn't be supporting these operators on them. The problem is that your examples of operator chaining more or less presume immutable objects as its basic model of "what's going on", and create lots of temporaries to serve as intermediate results. You can't count on the compiler to be able to re-use the space efficiently. But you also can't copy 1GB objects willy-nilly.
Instead, you should only support the various assigning operators. Then your client instead of writing:
y = a * b + c;
which is liable to create enormous temporaries, should instead write:
// y = a * b + c
y = a;
y *= b;
y += c;
That way the user can keep a lid on things. It's easily seen that no temporaries are created, and you won't accidentally write a simple line of code that requires 18GB to run. If you want to do:
y = a * b + c * d;
then your code must explicitly note that a temporary result is required here (assuming you can't trash any of a, b, c, d):
// y = a * b + c * d
y = a;
y *= b;
{
FunctionTree x = c;
x *= d;
y += x;
}
but if the caller happens to know that for example c isn't needed after this, you can explicitly do:
// y = a * b + c * d [c is trashed]
c *= d;
y = a;
y *= b;
y += c;
In theory the compiler might be up to working all this stuff out for itself based on big expression with the chained operators, and data-flow analysis to show that c is unused. Good compilers with lots of optimisation switched on are good at this for integer arithmetic, so the machinery is there. In practice I wouldn't count on it. If the compiler cannot prove that the constructor and destructor of FunctionTree have no observable side-effects, then its ability to skip them is limited to the specific cases of legal "copy elision" in the standard.
Or you could look at the C interface to GMP to see how this is done without operator overloading at all. All the functions there take an "out parameter", which the result is written to. So for example if x is a huge multiple precision integer, that you want to multiply by 10, you choose whether to write:
mpz_mul_ui(x, x, 10); // modifies x in place, uses less memory
or:
mpz_t y;
mpz_init(y);
mpz_mul_ui(y, x, 10); // leaves x alone, result occupies as much memory again.

There is no simple solution to this problem. Your binary operators are (correctly) producing nameless temporary objects - such objects cannot be bound to non-const reference parameters.
One possible way round this is to forgo the chaining of operators - for a class X:
X a;
X b;
X c = a * b;
X d;
X e = c + d;
Another (rather horrible) solution is to make the the data members of the class mutable - that way they would be logically const but physically alterable. You would then make your parameters be const references. Like I said, this is horrible and may cause other problems.

You can use proxies instead of real values, and proxies can be constant, as they are not going to be changed. Below is a small example of how it might look like. Be aware that all the temporaries are still going to be created in that example but if you want to be smart, you can just save the operations, not the actual results of operations, and calculate only when someone wants to finally get result or a part of it. It might even speed up your code enormously as it helped APL
Also, you might want to make most of the members private.
#include <memory>
#include <iostream>
struct FunctionTreeProxy;
struct FunctionTree;
struct FunctionTreeProxy {
mutable std::auto_ptr<FunctionTree> ft;
explicit FunctionTreeProxy(FunctionTree * _ft): ft(_ft) {}
FunctionTreeProxy(FunctionTreeProxy const & rhs): ft(rhs.ft) {}
FunctionTreeProxy operator+(FunctionTree & rhs);
FunctionTreeProxy operator*(FunctionTree & rhs);
FunctionTreeProxy operator+(FunctionTreeProxy const & rhs);
FunctionTreeProxy operator*(FunctionTreeProxy const & rhs);
};
struct FunctionTree {
int i;
FunctionTree(int _i): i(_i) {}
FunctionTree(FunctionTreeProxy const & rhs): i(rhs.ft->i) {}
FunctionTree * add(FunctionTree & rhs) {
return new FunctionTree(i + rhs.i);
}
FunctionTree * mult(FunctionTree & rhs) {
return new FunctionTree(i * rhs.i);
}
FunctionTreeProxy operator+(FunctionTree & rhs) {
return FunctionTreeProxy(add(rhs));
}
FunctionTreeProxy operator*(FunctionTree & rhs) {
return FunctionTreeProxy(mult(rhs));
}
FunctionTreeProxy operator+(FunctionTreeProxy const & rhs) {
return FunctionTreeProxy(add(*rhs.ft));
}
FunctionTreeProxy operator*(FunctionTreeProxy const & rhs) {
return FunctionTreeProxy(mult(*rhs.ft));
}
};
FunctionTreeProxy FunctionTreeProxy::operator+(FunctionTree & rhs) {
return FunctionTreeProxy(ft.get()->add(rhs));
}
FunctionTreeProxy FunctionTreeProxy::operator*(FunctionTree & rhs) {
return FunctionTreeProxy(ft.get()->mult(rhs));
}
FunctionTreeProxy FunctionTreeProxy::operator+(FunctionTreeProxy const & rhs) {
return FunctionTreeProxy(ft.get()->add(*rhs.ft));
}
FunctionTreeProxy FunctionTreeProxy::operator*(FunctionTreeProxy const & rhs) {
return FunctionTreeProxy(ft.get()->mult(*rhs.ft));
}
int main(int argc, char* argv[])
{
FunctionTree a(1), b(2), c(3);
FunctionTree z = a + b * c;
std::cout << z.i << std::endl;
return 0;
}

"...NONE of the objects involved are constant during the operations! This may sound strange..."
No it doesn't sound strange, it sounds wrong.
If your operators modify the objects in a way that is observable from the outside, then that's an abuse of operator overloading.
If the objects are modified, because results are cached, make the cache mutable, so functions modifying the cache can still be declared const.

Pass the arguments by value rather than by reference?
EDIT: You may want to use += -= *= instead.

Related

Deep copy of a matrix-like class

I've got a class that shall behave like matrix.
So the usecase is something like:
Matrix matrix(10,10);
matrix[0][0]=4;
//set the values for the rest of the matrix
cout<<matrix[1][2]<<endl;
code:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <sstream>
using namespace std;
class Matrix {
public:
Matrix(int x, int y);
class Proxy {
public:
Proxy(int* _array) : _array(_array) {
}
int &operator[](int index) const {
return _array[index];
}
private:
int* _array;
};
Proxy operator[](int index) const {
return Proxy(_arrayofarrays[index]);
}
Proxy operator[](int index) {
return Proxy(_arrayofarrays[index]);
}
const Matrix& operator=(const Matrix& othersales);
private:
int** _arrayofarrays;
int x, y;
};
Matrix::Matrix(int x, int y) {
_arrayofarrays = new int*[x];
for (int i = 0; i < x; ++i)
_arrayofarrays[i] = new int[y];
}
const Matrix& Matrix::operator=(const Matrix& othermatrix) {
new (this) Matrix(x, y);
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
_arrayofarrays[i][j] = othermatrix._arrayofarrays[i][j];
return *this;
}
int main() {
Matrix a(2, 3);
a[0][0] = 1;
a[0][1] = 2;
a[0][2] = 3;
a[1][0] = 4;
a[1][1] = 5;
a[1][2] = 6;
cout << a[1][2] << endl;
//prints out 6
const Matrix b = a;
cout << b[1][2] << endl;
a[1][2] = 3;
cout << a[1][2] << endl;
// prints out 3
cout << b[1][2] << endl;
// prints out 3 as well
}
By calling const Matrix b = a; I want to create new instance of Matrix, that will have the same values as a has in that moment. Nevertheless b is being affected by changing the values in a. So if I change some value in a, then it changes in b as well. And I don't want it to behave like this.
So I need to create a copy of b that would not be affected by a itself.
Those might be stupid question, but for me, as a java guy and a C++ newbie are all those stuff really confusing, so thanks for any helpful advices...
There are a few issues with your implementation. The simple one is the error you are getting...
In your Matrix class, operator[] is a non-const member function, which means that it can only be executed on non-const objects. Your operator= takes the right hand side object by const &, and thus you cannot call operator[] on it. The issue here is that you are not offering an implementation of operator[] that promises not to modify the object, once you add that to your type it should compile.
More important than that is the fact that you are leaking memory. When you call operator= on an object you are creating a different Matrix in place, without previously releasing the memory that it held. That is a memory leak.
The implementation of operator= is also not thread-safe. If allocation of memory for any of the internal arrays fails and throws an exception you are leaving your object in a state that is neither the original one nor a valid state. This is bad in itself.
Related to the previous, in as much as correcting one probably leads to the other, your implementation of operator= is not safe if there is aliasing, that is, it fails if you self-assign. The first line will leak the memory and create the new buffers, and from there on you will copy the new buffer into itself, loosing the original information.
Finally, the implementation of the type could be improved if you drop the requirement of using operator[] and use instead operator() with the two indices. User code will have to be adapted (and look less like a bidimensional array) but it provides a bit more freedom of representation (you can store the information internally in any way you want). At the same time, there is no need to allocate an array of pointers and then N arrays of int. You can perform a single memory allocation of NxM ints and do pointer arithmetic to address each location (this is independent of the use of operator[]/operator()), which will reduce the memory footprint and make the layout more compact, improving cache performance (not to mention reducing the number of dynamic allocations by a factor of M)
By calling const Matrix b = a; I want to create new instance of Matrix, that will have the same values of a in that moment. Nevertheless b is being affected by changing the values in a.
Well, this is yet another issue I missed in the first read. The expression const Matrix b = a; does not involve operator=, but rather the copy constructor. Another thing to google: Rule of the Three (basically, if you implement one of copy-constructor, assignment or destructor manually, you probably want to implement all three). Without defining your own copy constructor the compiler will implicitly define one for you that does a shallow copy (i.e. copies the pointers stored in Matrix but does not allocate memory for it). After the copy is made both Matrix share the same memory, and if your destructor releases the memory, you will run into Undefined Behavior when the second destructor runs and tries to delete [] the already deleted memory.

Is this a known pitfall of C++11 for loops?

Let's imagine we have a struct for holding 3 doubles with some member functions:
struct Vector {
double x, y, z;
// ...
Vector &negate() {
x = -x; y = -y; z = -z;
return *this;
}
Vector &normalize() {
double s = 1./sqrt(x*x+y*y+z*z);
x *= s; y *= s; z *= s;
return *this;
}
// ...
};
This is a little contrived for simplicity, but I'm sure you agree that similar code is out there. The methods allow you to conveniently chain, for example:
Vector v = ...;
v.normalize().negate();
Or even:
Vector v = Vector{1., 2., 3.}.normalize().negate();
Now if we provided begin() and end() functions, we could use our Vector in a new-style for loop, say to loop over the 3 coordinates x, y, and z (you can no doubt construct more "useful" examples by replacing Vector with e.g. String):
Vector v = ...;
for (double x : v) { ... }
We can even do:
Vector v = ...;
for (double x : v.normalize().negate()) { ... }
and also:
for (double x : Vector{1., 2., 3.}) { ... }
However, the following (it seems to me) is broken:
for (double x : Vector{1., 2., 3.}.normalize()) { ... }
While it seems like a logical combination of the previous two usages, I think this last usage creates a dangling reference while the previous two are completely fine.
Is this correct and Widely appreciated?
Which part of the above is the "bad" part, that should be avoided?
Would the language be improved by changing the definition of the range-based for loop such that temporaries constructed in the for-expression exist for the duration of the loop?
Is this correct and Widely appreciated?
Yes, your understanding of things is correct.
Which part of the above is the "bad" part, that should be avoided?
The bad part is taking an l-value reference to a temporary returned from a function, and binding it to an r-value reference. It is just as bad as this:
auto &&t = Vector{1., 2., 3.}.normalize();
The temporary Vector{1., 2., 3.}'s lifetime cannot be extended because the compiler has no idea that the return value from normalize references it.
Would the language be improved by changing the definition of the range-based for loop such that temporaries constructed in the for-expression exist for the duration of the loop?
That would be highly inconsistent with how C++ works.
Would it prevent certain gotchas made by people using chained expressions on temporaries or various lazy-evaluation methods for expressions? Yes. But it would also be require special-case compiler code, as well as be confusing as to why it doesn't work with other expression constructs.
A much more reasonable solution would be some way to inform the compiler that the return value of a function is always a reference to this, and therefore if the return value is bound to a temporary-extending construct, then it would extend the correct temporary. That's a language-level solution though.
Presently (if the compiler supports it), you can make it so that normalize cannot be called on a temporary:
struct Vector {
double x, y, z;
// ...
Vector &normalize() & {
double s = 1./sqrt(x*x+y*y+z*z);
x *= s; y *= s; z *= s;
return *this;
}
Vector &normalize() && = delete;
};
This will cause Vector{1., 2., 3.}.normalize() to give a compile error, while v.normalize() will work fine. Obviously you won't be able to do correct things like this:
Vector t = Vector{1., 2., 3.}.normalize();
But you also won't be able to do incorrect things.
Alternatively, as suggested in the comments, you can make the rvalue reference version return a value rather than a reference:
struct Vector {
double x, y, z;
// ...
Vector &normalize() & {
double s = 1./sqrt(x*x+y*y+z*z);
x *= s; y *= s; z *= s;
return *this;
}
Vector normalize() && {
Vector ret = *this;
ret.normalize();
return ret;
}
};
If Vector was a type with actual resources to move, you could use Vector ret = std::move(*this); instead. Named return value optimization makes this reasonably optimal in terms of performance.
for (double x : Vector{1., 2., 3.}.normalize()) { ... }
That is not a limitation of the language, but a problem with your code. The expression Vector{1., 2., 3.} creates a temporary, but the normalize function returns an lvalue-reference. Because the expression is an lvalue, the compiler assumes that the object will be alive, but because it is a reference to a temporary, the object dies after the full expression is evaluated, so you are left with a dangling reference.
Now, if you change your design to return a new object by value rather than a reference to the current object, then there would be no issue and the code would work as expected.
IMHO, the second example is already flawed. That the modifying operators return *this is convenient in the way you mentioned: it allows chaining of modifiers. It can be used for simply handing on the result of the modification, but doing this is error-prone because it can easily be overlooked. If I see something like
Vector v{1., 2., 3.};
auto foo = somefunction1(v, 17);
auto bar = somefunction2(true, v, 2, foo);
auto baz = somefunction3(bar.quun(v), 93.2, v.qwarv(foo));
I wouldn't automatically suspect that the functions modify v as a side-effect. Of course, they could, but it would be confusing. So if I was to write something like this, I would make sure that v stays constant. For your example, I would add free functions
auto normalized(Vector v) -> Vector {return v.normalize();}
auto negated(Vector v) -> Vector {return v.negate();}
and then write the loops
for( double x : negated(normalized(v)) ) { ... }
and
for( double x : normalized(Vector{1., 2., 3}) ) { ... }
That is IMO better readable, and it's safer. Of course, it requires an extra copy, however for heap-allocated data this could likely be done in a cheap C++11 move operation.

Efficient functional style (C++11 ok)

Lets say I have the following:
struct point
{
double x;
double y;
double z;
};
I can write the following:
void point_mult(point& p, double c) { p.x *= c; p.y *= c; p.z *= c; }
void point_add(point& p, const point& p2) { p.x += p2.x; p.y += p2.y; p.z += p2.z; }
So I can then do the following:
point p{1,2,3};
point_mult(p, 2);
point_add(p, point{4,5,6});
This requires no copies of point, and only two constructions, namely the construction of point{1,2,3} and the construction of point{4,5,6}. I believe this applies even if point_add, point_mult and point{...} are in separate compilation units (i.e. can't be inlined).
However, I'd like to write code in a more functional style like this:
point p = point_add(point_mult(point{1,2,3}, 2), point{4,5,6});
How can I write point_mult and point_add such that no copies are required (even if point_mult and point_add are in separate compilation units), or is functional style forced to be not as efficient in C++?
Let's ignore the implicit fallacy of the question (namely that copying automatically means reduced efficiency). And let's also ignore the question of whether any copying would actually happen, or whether it would all be elided away by any half-decent compiler. Let's just take it on face value: can this be done without copying?
Yes, and it is probably the only other legitimate use for r-value references (though the previously ignored stipulations make this use case dubious):
point &&point_mult(point &&p, double c);
Of course, this will only bind to temporaries. So you would need an alternate version for l-values:
point &point_mult(point &p, double c);
The point is that you pass the references through as they are, either as references to temporaries or references to l-values.
It can be done with really ugly template metaprogramming. For example eigen uses templates so that expressions like matrix1 + matrix2 * matrix3 don't need to create any temporaries. The gist of how it works is the + and * operators for matrices don't return Matrix objects but instead return some kind of matrix expression object which is templatized on the types of the expression paramaters. This matrix expression object can then compute parts of the expression only when they are needed instead of creating temporary objects to store the result of subexpressions.
Actually implementing this can get quite messy. Have a look at Eigen's source if your interested. Boost's uBlas also does something similar, though it's not as extensively as eigen.
An efficient (and generalized) technique is expression templates. You can read a nice introductory explanation here.
It's difficult to implement and being based on templates, you cannot use separate compilation units, but it's very efficient. An interesting application in symbolic computation is parsing: Boost.Spirit builds very efficient parsers out of them.
C++11 auto keywords helps usage on practical programming tasks, as always when dealing with complex types, see this other answer.
First, why not use "better" functions ?
struct Point {
double x;
double y;
double z;
Point& operator+=(Point const& right) {
x += right.x; y += right.y; z += right.z;
return *this;
}
Point& operator*=(double f) {
x *= f; y *= f; z *= f;
return *this;
}
};
Now it can be used as:
Point p = ((Point{1,2,3} *= 2) += Point{4,5,6});
But I truly think that you worry too much about copies here (and performance).
Make it work
Make it fast
If you don't have anything that already works, talking about performance is akin to chasing mills... bottlenecks are rarely where we thought they would be.
Change the definition of point_mult() to:
point& point_mult(point& p, double c) { p.x *= c; p.y *= c; p.z *= c; return p; }
^^^^^^ ^^^^^^^^^
And call it as:
point & p = point_add(point_mult(*new point{1,2,3}, 2), point{4,5,6});
^^^ ^^^^^
there is no copy involved. However, you have to later do delete &p; for releasing the memory.

C++ Matrix Class with Operator Overloading

I was implementing a small dense matrix class and instead of plan get/set operators I wanted to use operator overloading to make the API more usable and coherent.
What I want to achieve is pretty simple:
template<typename T>
class Matrix
{
public:
/* ... Leaving out CTOR and Memory Management for Simplicity */
T operator() (unsigned x, unsigned y){/* ... */ }
};
Matrix<int> m(10,10);
int value = m(5,3); // get the value at index 5,3
m(5,3) = 99; // set the value at index 5,3
While getting the value is straight forward by overloading operator(), I can't get my head around defining the setter. From what I understood the operator precedence would call operator() before the assignment, however it is not possible to overload operator() to return a correct lvalue.
What is the best approach to solve this problem?
I dispute that "it's not possible" to do the correct thing:
struct Matrix
{
int & operator()(size_t i, size_t j) { return data[i * Cols + j]; }
const int & operator()(size_t i, size_t j) const { return data[i * Cols + j]; }
/* ... */
private:
const size_t Rows, Cols;
int data[Rows * Cols]; // not real code!
};
Now you can say, m(2,3) = m(3,2) = -1; etc.
The answer to your question is what Kerrek already stated: you can provide an overload by changing the signature of the operator, which in this case can be achieved by modifying the const-ness of the function.
But I would recommend that you at least consider providing a separate setter for the values. The reason is that once you return references to your internal data structures you loose control of what external code does with your data. It might be ok in this case, but consider that if you decided to add range validation to the implementation (i.e. verify that no value in the matrix is above X or below Y), or wished to optimize some calculation on the matrix (say the sum of all of the elements in the matrix is an often checked value, and you want to optimize away the calculation by pre-caching the value and updating it on each field change) it is much easier to control with a method that receives the value to set.

C++ member variable aliases?

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?