Skip to main content
gamedev.net
🔒 Locked

constructors for template class

Started by Koen Dec 29, 2003 at 6:03 PM 3 replies 750+ views
Original Post
Koen
Koen
I'm writing a template vector class and I would like to do stuff like:
TVector<double,3> v1(1.0, 2.0, 3.0);
TVector<int,2> v2(12, 14);
This would require a constructor that has a number of arguments equal to the size of the vector. As far as I know, this cannot directly be done with templates. So I tried the following:
template<typename t_type,unsigned int t_dimension>
class TVector
  {
  protected:
    t_type m_components[t_dimension];

  public:
    
    TVector(const t_type array[t_dimension])
      {
      for(unsigned int k=0;k<t_dimension;++k)
        {
        m_components[k] = array[k];
        }
      }
  }
  
I hoped that would make it possible to write stuff like:
TVector<double,3> v1( { 1.0, 2.0, 3.0 } );
TVector<int,2> v2( { 12, 14 } );
  
Unfortunately, this doesn't work either. Is there a general (preferably clean) way to solve this? Or do I have to stick with individual assignment of components after creation of the vector. Thanks. mod: trying to make those template-parameters visible :-) [edited by - Koen on December 29, 2003 7:09:25 PM]
Sneftel
Sneftel
put code in [ source ] amd [ /source ] tags (no spaces) to make template parameters show up.

Generally, no, there''s no way to do that. In the special case of integer constants, I suppose you could use typelists; but that would mean that the initializing list would be part of the type. Yuck.

Another solution would be to have a temporary array which you assign from. Like so:



class A
{
public:
A(int* initlist, int size);
};

int initlist[] = { 1, 1, 2, 3, 5 };
A myA(initlist, 5);


That''s ugly, though; and RAII semantics are such that it isn''t possible to declare initlist in a tighter scope than myA without a second initialization step (which, of course, is what you''re trying to avoid).

I advise that you just live with it. And if you find yourself peppering game content throughout your code, consider refactoring; engine and content should be separated as much as possible.


"Sneftel is correct, if rather vulgar." --Flarelocke
SiCrane
SiCrane
There''s a way to do what you''re trying, but I''m not sure it qualifies as clean.

// pretend this is the header file


template <typename T, int dim>
class TVectorBase {
public:
TVectorBase() {}
T data_[dim];
// any other common vector functionality (dot products, etc.)

};

template <typename T, int dim>
class TVector : public TVectorBase<T, dim> {
public:
TVector() {} // if someone tries a 5 or 6 dimension

// vector they''ll have to manually initialize it

};

template <typename T>
class TVector<T, 2> : public TVectorBase<T, 2> {
public:
TVector(T arg1, T arg2) { data_[0] = arg1; data_[1] = arg2; }
};

template <typename T>
class TVector<T, 3> : public TVectorBase<T, 3> {
public:
TVector(T arg1, T arg2, T arg3) { data_[0] = arg1; data_[1] = arg2; data_[2] = arg3; }
};

template <typename T>
class TVector<T, 4> : public TVectorBase<T, 4> {
public:
TVector(T arg1, T arg2, T arg3, T arg4) {
data_[0] = arg1; data_[1] = arg2; data_[2] = arg3; data_[3] = arg4;
}
};

// example function working happily with different vector types

template <typename T, int dim>
std::ostream & operator<<(std::ostream & left, TVector<T, dim> & right) {
for (int i = 0; i < dim; i++) {
left << right.data_[i] << std::endl;
}
return left;
}

// pretend this is the source file

int main() {
TVector<double, 3> v1(1.0, 2.0, 3.0);
TVector<int, 2> v2(12, 14);
TVector<std::string, 4> v3("cow", "horse", "pig", "sheep");

std::cout << v1;
std::cout << v2;
std::cout << v3;

return 0;
}

Strictly speaking, the TVectorBase class isn''t necessary. But having it reduces some extra typing when you introduce increased base functionality.

And there are other ways to do it; but most of them aren''t pretty.
Koen
Koen
Cool. Thanks.
I guess this will be clean enough :-) My goal was to be able to use different sizes for vectors and write common code only once. Your method does exactly that (and as an extra: the cross-product for 3d-vectors fits in nicely).
Now, when I want to implement matrix-vector-products, it will be something like:

template<typename t_type, int t_dimension>
class TMatrix
{
TVectorBase<t_type,t_dimension> operator * (const TVectorBase<t_type,t_dimension>& vector_argument)
{
//Do multiplication stuff here

}
}

Is there performance loss here, because of the use of inheritance (The parameter of the multiplication-operator will not be an TVectorBase, but an TVector)? There are no virtual functions, so I suppose performance is not an issue, but I''d like to know for sure :-)
SiCrane
SiCrane
There shouldn''t be any overhead, but you really should be working with TVector objects in your operator * function instead of TVectorBase objects, otherwise you''ll have to do some strange casts.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.