R
Ruben Campos
Greetings.
Some time ago, I had writing a CVector <T, N> class, which implements an
algebraic vector of arbitrary both dimension and scalar type. First, I
defined the interface for the generic algebraic vector class. The
problem I encountered there was that algebraic vectors of some concrete
dimensions were susceptible to include some extra methods not included
in the most generic case. For example, 3-dimensional vectors include the
cross product, an operation not defined over vectors of lesser dimension
(and also greater... but my mathematic knowledge is not so vast); also,
I'd like to include for 2, 3 and 4-dimensional vectors, methods to
access the cartesian coordinates by name (x, y, z and w).
The two alternatives I found (after seeking and asking here for advice)
to deal with this required to write partial specializations of CVector
<T, N> class, a) rewriting all common behaviour or b) inheriting from a
CBaseVector <T, N> class which include that common behaviour.
Recently, I've reviewed CVector <T, N> source code, and I've found
another alternative (which I think is simpler and clearer than the
previous ones). Now, I have only one CVector <T, N> class generic
definition, without partial (or total) specializations, and that class
definition includes all methods expected to be both common or restricted
to certain dimension (N) values. Then, the implementation of those
restricted methods includes compile time assertions to ensure that they
are not used over vectors with wrong dimension values.
I include here a sample of (incomplete, but I hope that enough to
illustrate the explined) source code. First, the class interface:
template <typename T, std::size_t N>
class CVector
{
public:
// Common behaviour
CVector (T const & initval = T());
CVector (CVector <T,N> const & other);
T & operator[] (std::size_t const index);
T const & operator[] (std::size_t const index) const;
CVector <T,N> & operator= (CVector <T,N> const & rhs);
CVector <T,N> & operator+= (CVector <T,N> const & rhs);
CVector <T,N> & operator-= (CVector <T,N> const & rhs);
CVector <T,N> & operator*= (T const & rhs);
CVector <T,N> & operator/= (T const & rhs);
// ...
// Restricted behaviour
CVector (T const & x, T const & y);
// Only for 2-dimensional vectors
CVector (T const & x, T const & y, T const & z);
// Only for 3-dimensional vectors
CVector (T const & x, T const & y, T const & z, T const & w);
// Only for 4-dimensional vectors
void set (T const & x, T const & y);
// Only for 2-dimensional vectors
void set (T const & x, T const & y, T const & z);
// Only for 3-dimensional vectors
void set (T const & x, T const & y, T const & z, T const & w);
// Only for 4-dimensional vectors
T & x ();
// Only for 1- to 4-dimensional vectors
T const & x () const;
// Only for 1- to 4-dimensional vectors
T & y ();
// Only for 2- to 4-dimensional vectors
T const & y () const;
// Only for 2- to 4-dimensional vectors
T & z ();
// Only for 3- to 4-dimensional vectors
T const & z () const;
// Only for 3- to 4-dimensional vectors
T & w ();
// Only for 4-dimensional vectors
T const & w () const;
// Only for 4-dimensional vectors
CVector <T,N> & operator^= (CVector <T,N> const & rhs);
// Cross product, only for 3-dimensional vectors
// ...
private:
// ...
};
And then, a samples of a restricted method implementation:
template <typename T, std::size_t N>
T &
CVector <T,N>::y ()
{
STATIC_ASSERT((N >= 2) && (N <= 4));
return (*this)[1];
}
STATIC_ASSERT could be any implementation of a compile time assertion
mechanism. For now, I'm using the second variant described in the book
"Modern C++ design: generic programming and design patterns applied", by
Andrei Alexandrescu.
I've successfully tested the described approach. Also, I've found it
clearer than the two previous ones which imply partial specializations,
and it doesn't duplicate code nor it requires auxiliar classes.
But I still want to ask here about the goodness of this approach. Is it
correct, from a formal point of view? Does it have any weak spot I've
overlooked? Is there any way of further improving the design?
Thank you in advance for your time and your advice.
Some time ago, I had writing a CVector <T, N> class, which implements an
algebraic vector of arbitrary both dimension and scalar type. First, I
defined the interface for the generic algebraic vector class. The
problem I encountered there was that algebraic vectors of some concrete
dimensions were susceptible to include some extra methods not included
in the most generic case. For example, 3-dimensional vectors include the
cross product, an operation not defined over vectors of lesser dimension
(and also greater... but my mathematic knowledge is not so vast); also,
I'd like to include for 2, 3 and 4-dimensional vectors, methods to
access the cartesian coordinates by name (x, y, z and w).
The two alternatives I found (after seeking and asking here for advice)
to deal with this required to write partial specializations of CVector
<T, N> class, a) rewriting all common behaviour or b) inheriting from a
CBaseVector <T, N> class which include that common behaviour.
Recently, I've reviewed CVector <T, N> source code, and I've found
another alternative (which I think is simpler and clearer than the
previous ones). Now, I have only one CVector <T, N> class generic
definition, without partial (or total) specializations, and that class
definition includes all methods expected to be both common or restricted
to certain dimension (N) values. Then, the implementation of those
restricted methods includes compile time assertions to ensure that they
are not used over vectors with wrong dimension values.
I include here a sample of (incomplete, but I hope that enough to
illustrate the explined) source code. First, the class interface:
template <typename T, std::size_t N>
class CVector
{
public:
// Common behaviour
CVector (T const & initval = T());
CVector (CVector <T,N> const & other);
T & operator[] (std::size_t const index);
T const & operator[] (std::size_t const index) const;
CVector <T,N> & operator= (CVector <T,N> const & rhs);
CVector <T,N> & operator+= (CVector <T,N> const & rhs);
CVector <T,N> & operator-= (CVector <T,N> const & rhs);
CVector <T,N> & operator*= (T const & rhs);
CVector <T,N> & operator/= (T const & rhs);
// ...
// Restricted behaviour
CVector (T const & x, T const & y);
// Only for 2-dimensional vectors
CVector (T const & x, T const & y, T const & z);
// Only for 3-dimensional vectors
CVector (T const & x, T const & y, T const & z, T const & w);
// Only for 4-dimensional vectors
void set (T const & x, T const & y);
// Only for 2-dimensional vectors
void set (T const & x, T const & y, T const & z);
// Only for 3-dimensional vectors
void set (T const & x, T const & y, T const & z, T const & w);
// Only for 4-dimensional vectors
T & x ();
// Only for 1- to 4-dimensional vectors
T const & x () const;
// Only for 1- to 4-dimensional vectors
T & y ();
// Only for 2- to 4-dimensional vectors
T const & y () const;
// Only for 2- to 4-dimensional vectors
T & z ();
// Only for 3- to 4-dimensional vectors
T const & z () const;
// Only for 3- to 4-dimensional vectors
T & w ();
// Only for 4-dimensional vectors
T const & w () const;
// Only for 4-dimensional vectors
CVector <T,N> & operator^= (CVector <T,N> const & rhs);
// Cross product, only for 3-dimensional vectors
// ...
private:
// ...
};
And then, a samples of a restricted method implementation:
template <typename T, std::size_t N>
T &
CVector <T,N>::y ()
{
STATIC_ASSERT((N >= 2) && (N <= 4));
return (*this)[1];
}
STATIC_ASSERT could be any implementation of a compile time assertion
mechanism. For now, I'm using the second variant described in the book
"Modern C++ design: generic programming and design patterns applied", by
Andrei Alexandrescu.
I've successfully tested the described approach. Also, I've found it
clearer than the two previous ones which imply partial specializations,
and it doesn't duplicate code nor it requires auxiliar classes.
But I still want to ask here about the goodness of this approach. Is it
correct, from a formal point of view? Does it have any weak spot I've
overlooked? Is there any way of further improving the design?
Thank you in advance for your time and your advice.