Not logged in, Join Here! or Log In Below:  
 
News Articles Search    
 

 Home / General Programming / References exist? Account Manager
 
Archive Notice: This thread is old and no longer active. It is here for reference purposes. This thread was created on an older version of the flipcode forums, before the site closed in 2005. Please keep that in mind as you view this thread, as many of the topics and opinions may be outdated.
 
RAZORUNREAL

May 14, 2005, 09:24 PM

I wanted to make a better vector (as in 3d calculations) class, one which would be nice and quick and work with any number of any data type. I wanted to be able to add, multiply, dot etc. vectors of the same data type with the same number of dimensions at good enough performance for use in games. And I also wanted to be able to use .x, .y etc to access parts of the vector to avoids silly mistakes.

It was while attempting this last part that I ran into a problem. I thought I could just use public references into the private data array and the compiler would optimise them out, meaning it was basicly syntactic suger. I tried it, and it worked beutifully. Except the references take memory. I thought seeing as references can't be reassigned they wouldn't need to exist like pointers do, you could just access the variable directly, and they would never appear in the compiled code at all. Seems I don't know how references work. Why is it wasting memory on a variable that will never change? I don't really want purely convinient references to use more memory than my data. In case it helps, heres my vector class stripped down:

  1.  
  2. template<class T, int S>
  3. class RVec
  4. {
  5. public:
  6.     RVec()
  7.     : x(Data[0]), y(Data[1]), z(Data[2]), w(Data[3])
  8.     {
  9.     }
  10.     T& x; //These take memory
  11.     T& y;
  12.     T& z;
  13.     T& w;
  14. private:
  15.     T Data[S];
  16. };
  17.  


By the way, this is the first time I've tried to write a general template class like this, so if you have any tips I'd really appreciate them.

 
LastInquisitor

May 15, 2005, 12:22 AM

It is perfectly valid to change a reference. In your case
the compiler doesn't know it will never change. Try it with const:
const T& x

But at least my good old borland isn't smart enough to optimise it away...
At the end a reference is just a like a pointer that can't be null. And you wouldn't expect that the size of a pointer suddenly becomes zero?

Why not use a union? Okay, you'll have to type a bit too much for a simple attribute access I guess :P

 
RAZORUNREAL

May 15, 2005, 01:53 AM

How do you change a reference then? If you use = it just assigns the other variable to the one it references. I tried to use &Ref = var; but that complained about cast to refrerence type. &Ref = &Var; had the same problem.

I tried to make it const, but const T& x; makes it a reference to a const variable so I can't assign things to it, and T& const x; gives the error "'const' qualifiers cannot be applied to 'T&'".

I havn't really used references much, as you can probably tell.

 
fruit

May 15, 2005, 02:16 AM

How about a union? It seems to be what you are trying to do.

template
class RVec
{
public:
RVec();
union
{
public:
struct
{
T x;
T y;
T z;
T w;
};
private:
T Data[S];
};

};

 
Gerald Knizia (cgk)

May 15, 2005, 04:09 AM

It is possible to change referenced data, but to change an reference itself is not allowed and with usual C++-ways not even possible. There is no C++ mechanismn to rebind a reference.

RAZORUNREAL: The union-approach posted by fruit will work as long as T and all of its members (if present) have no user-supplied constructor. For example, if T is some number type, this will work.
For a cleaner but more ugly way to do it you might consider adding an accessor function, i.e.

  1.  
  2.     T& x(){ return Data[0]; }
  3.     const T& x() const { return Data[0]; }
  4.     ...
  5.  

 
RAZORUNREAL

May 15, 2005, 04:42 AM

Well the union way works quite well. I'd never heard of them before. It means I can never have a vector smaller than 4 variables, but thats ok. I can just have a base class for my vector that doesn't use the union in case it becomes a problem. Come to think of it I could have a color class with rgba too. Why doesn't it work for classes with constructors?

So it isn't possible to change a reference. Why can't the compiler just substitute in the referenced variable at compile time then? I actually thought that was the point in them...

I realise I could use vec.x() but I don't really want to. If other ways get too painfull it might happen though.

Thanks guys.

 
Chris

May 15, 2005, 04:51 AM

Why doesn't it work for classes with constructors?


The standard forbids it (Visual Studio used to permit it anyway). Technically it is not a problem, but it creates ambiguities.

Why can't the compiler just substitute in the referenced variable at compile time then? I actually thought that was the point in them...


It cannot detect at compile time to which variable the reference is bound, it's simply not possible.

As references are nothing more that constant pointers you could hack your way to changing references using something like this:

  1.  
  2. int x = 42;
  3. int z = 84;
  4. int & y = x;
  5. cout << y << endl;  // prints 42;
  6. (int*)(y) = &z;
  7. cout << y << endl;  // prints 84;
  8.  


But it's hacky, you really musn't do such things.

 
Fabian 'ryg' Giesen

May 15, 2005, 05:44 AM

It cannot detect at compile time to which variable the reference is bound, it's simply not possible.


It is possible, with global dataflow analysis.

 
Chris

May 15, 2005, 06:36 AM

Let me put it this way: It's usually not feasible to do it. I don't know of any compiler that does it on a regular basis.
Certain simplistic constructs like scope-local references will be eliminated, I don't think global references will be.

 
JCash

May 15, 2005, 12:00 PM

Ok, I know it is nice to use .xyzw (I do it too) but have you considered this option?

  1.  
  2. inline T&           operator[] ( int index )       { return Data[ index ]; }
  3. inline const float& operator[] ( int index ) const { return Data[ index ]; }
  4.  

 
RAZORUNREAL

May 16, 2005, 12:36 AM

Yes, in fact I already use that method, .xyz is just a convinient alternative. I probably should have included that in my code snippet. I guess I'll use do it .x() way for robustness, but that doesn't make this thread pointless, I've learned alot.

 
Reedbeta

May 16, 2005, 01:09 AM

I just use this:

  1. struct vec4
  2. {
  3.     float x, y, z, w;
  4.  
  5.     // ....
  6.  
  7.     const float operator[] (int i) const
  8.     { return (i == 0) ? x : (i == 1) ? y : (i == 2) ? z : w; }
  9.     float &operator [] (int i)
  10.     { return /* ... same as before */ }
  11.  
  12.     // ....
  13. };


Doesn't allow you to easily template the size, but as I only normally use 2-, 3-, and 4-vectors I just have a class for each and it's no big deal.

 
Chad Austin

May 16, 2005, 02:49 AM

Does your compiler generate good code for operator[]?

 
IronChefC++

May 16, 2005, 12:04 PM

The compiled operator[] code is certainly far from optimal. The function takes an int as parameter. Given the indexing expression, all of the numbers that an int may take other than 0, 1, and 2 must map to w; therefore, the function must have some code to do the collapsing of the numbers outside [0,2]. This inefficiency cannot be avoided.

 
Reedbeta

May 16, 2005, 11:26 PM

Another way to do the operator[] is like this:

  1. const float operator[] (int i) const
  2. { return reinterpret_cast<const float *>(this) + i; }


But this seems rather hackish to me. I don't think it's guaranteed to always work, as the memory layout of the class members is up to the compiler.

 
Victor Widell

May 17, 2005, 02:53 PM

"I don't think it's guaranteed to always work, as the memory layout of the class members is up to the compiler."

There might be padding, but the members should allways come in the same order as the source code. And I doubt any compiler will pad between members of the same type. (Native types, that is. I guess there would be padding between objects of a class if the size of the class was not a power of 2 bytes.)

Minor change: (This is what my class looks like.)

  1.  
  2. const float operator[] (int i) const
  3. { return reinterpret_cast<const float *>&x + i; }
  4.  

 
adel

May 19, 2005, 07:00 AM

Reedbeta is right. You should have a different class for each type you will be using. At least this is more readable. Also I think it would spare you the overhead of dereferencing a pointer every time you access a member, which is important if you're concerned with performance. Also, I remember reading that code written using references and the same code written with pointers will probably end up compiled to the same machine code.

 
Chris

May 19, 2005, 09:08 AM

And you could make exchange between the different classes easy by adding the appropriate constructors and type-cast operators.

 
RAZORUNREAL

May 20, 2005, 12:42 AM

I wanted a template class because I got sick of modifying loads of different classes every time I found a way to improve the vector. I had float2, float3, float4, fixed2, fixed3, fixed4, colour, it's crazy. And the template class has already proved usefull, while loading png's of all things! Performance is comparable, my template class sometimes beats my normal one on the better of my 2 pc's. Seems to have a pretty slow + operator but += is about the same as float3, it even constructs in similar time, it's all good enough for real time use. Unlike certain boost libraries. They should really warn people they sacrifice all performance to make classes pointlessly general...

If you know of a fast vector class I should be profiling against rather than another class I wrote myself, please let me know.

I don't think RVec Vertex; has readability problems either. If you mean it's hard to read the class, you shouldn't really need to, it works about how you'd expect. I'll have to think about conversions.

 
IronChefC++

May 22, 2005, 02:56 AM

...the memory layout of the class members is up to the compiler.

That's only semi-true according to the standard. For members declared in a single access block (public, private, etc.), the members must be stored in the order declared; however, there may be padding as the compiler sees fit. Blocks may be set in any order. Base class data come before any derived class data in the order listed. The standard does not guarantee that the y member in the declaration "float x,y,z;" is the same as x[1]. It only guarantees that vec4::&y > vec4::&x.

 
James Bellinger

May 22, 2005, 07:11 PM

You can have a vector smaller than 4 elements and still do it with templates.

What you'll want to do is have a templated base class which has all of your functionality, but does _not_ store the actual data it works on.

Now, this base vector class will look like this:

  1.  
  2. template <size_t Dims, typename ElementType, class VectorType> struct VectorBase
  3. {
  4.   ...all of my code goes here...
  5. };
  6.  


Now, to access any of your data, the VectorBase should do an explicit class call, for instance VectorType::At(i) which will return a reference. Also if you need to return a vector have it create one of type VectorType, so that it returns the child class type.

Then, have a Vector class like this...

  1.  
  2. template <size_t Dims, typename ElementType = float> struct Vector: public VectorBase<Dims, ElementType, Vector<Dims, ElementType> >
  3. {
  4.   const ElementType& const At(size_t index) const { return mData[index]; }
  5.         ElementType&       At(size_t index)       { return mData[index]; }
  6.  
  7.   private:
  8.     ElementType mData[Dims];
  9. };
  10.  


By the way in VC++ 7 you might get an internal compiler error on the Vector part. I did when I first tried something like this. Explicitly specifying the namespace, like 'public VectorBase' seems to fix it.

Next, for say a Vector, do a partial specialization

  1.  
  2. template <typename ElementType> struct Vector<3, ElementType>: public VectorBase<3, ElementType, Vector<3, ElementType> >
  3. {
  4.   const ElementType& const At(size_t index) const { return mData[index]; }
  5.         ElementType&       At(size_t index)       { return mData[index]; }
  6.  
  7.   union
  8.   {
  9.     struct
  10.     {
  11.       ElementType X, Y, Z;
  12.     };
  13.  
  14.   private:
  15.     ElementType mData[3];
  16.   };
  17. };
  18.  


Anyhow if you do that specialization for your 2D, 3D, and 4D vectors you should be in good shape.

 
This thread contains 21 messages.
 
 
Hosting by Solid Eight Studios, maker of PhotoTangler Collage Maker.