P: n/a

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, 3dimensional 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 4dimensional 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 2dimensional vectors
CVector (T const & x, T const & y, T const & z);
// Only for 3dimensional vectors
CVector (T const & x, T const & y, T const & z, T const & w);
// Only for 4dimensional vectors
void set (T const & x, T const & y);
// Only for 2dimensional vectors
void set (T const & x, T const & y, T const & z);
// Only for 3dimensional vectors
void set (T const & x, T const & y, T const & z, T const & w);
// Only for 4dimensional vectors
T & x ();
// Only for 1 to 4dimensional vectors
T const & x () const;
// Only for 1 to 4dimensional vectors
T & y ();
// Only for 2 to 4dimensional vectors
T const & y () const;
// Only for 2 to 4dimensional vectors
T & z ();
// Only for 3 to 4dimensional vectors
T const & z () const;
// Only for 3 to 4dimensional vectors
T & w ();
// Only for 4dimensional vectors
T const & w () const;
// Only for 4dimensional vectors
CVector <T,N> & operator^= (CVector <T,N> const & rhs);
// Cross product, only for 3dimensional 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.  
Share this Question
P: n/a

Ruben Campos wrote: Greetings.
[...] 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]; }
Hi Rubén,
I think you can do this in a cleaner way using
boost::enable_if: http://boost.org/libs/utility/enable_if.html
Hope this helps,
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo  
P: n/a

Joaquín M López Muñoz wrote: Ruben Campos wrote:
Greetings.
[...]
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]; }
Hi Rubén,
I think you can do this in a cleaner way using boost::enable_if:
http://boost.org/libs/utility/enable_if.html
Hope this helps,
Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
First of all, thank you for your diligent answer.
I've immediately read the boost::enable_if documentation, I've test it
(not in depth, for obvious time reasons) and my conclussion is as follows.
The only way boost::enable_if represents a change from the STATIC_ASSERT
implementation (in the case I described in my first mail) is by moving
the restriction check from method's implementation to method's
declaration. I think this carries two disadvantages: a) first, it makes
the class declaration a bit harder to read; and second, b)
boost::enable_if must be typed twice, both in function's declaration and
in function's implementation (I'm currently placing each of them into
separate header and source files, also for template classes), and so
must be done with the restriction.
The only advantage I see is that boost::enable_if provides information
about the restriction directly in the class declaration itself, saving a
user to be addressed to the implementation (which should be kept
hidden). But the same can be made through comments and/or class
documentation (in fact, I've currently included the restriction
description into the comments attached to the function's implementation,
from which Doxygen constructs the class documentation).
So I'm not able to see the way in which boost::enable_if can help in my
case. Please, correct me if I am wrong, or explain me advantages of
boost::enable_if that I've not seen.
Thank you for your time.  
P: n/a

Ruben Campos ha escrito: The only way boost::enable_if represents a change from the STATIC_ASSERT implementation (in the case I described in my first mail) is by moving the restriction check from method's implementation to method's declaration. I think this carries two disadvantages: a) first, it makes the class declaration a bit harder to read; and second, b) boost::enable_if must be typed twice, both in function's declaration and in function's implementation (I'm currently placing each of them into separate header and source files, also for template classes), and so must be done with the restriction.
The only advantage I see is that boost::enable_if provides information about the restriction directly in the class declaration itself, saving a user to be addressed to the implementation (which should be kept hidden). But the same can be made through comments and/or class documentation (in fact, I've currently included the restriction description into the comments attached to the function's implementation, from which Doxygen constructs the class documentation).
So I'm not able to see the way in which boost::enable_if can help in my case. Please, correct me if I am wrong, or explain me advantages of boost::enable_if that I've not seen.
Hello again,
Well, I've thought twice and now I realize that boost::enable_if
is not applicable to your case, as it can only be used with
(member) function templates.
In the case of (member) function templates, the advantage
of boost::enable_if is that the template, if disabled
for a particuar type T, does not even enter into the
associated overload set: with the static assert technique,
such a template could be selected by the overload resolution
rules only to trigger a compile time fail. But then again,
your case is different as your restricted member functions
are not templates. So, sorry for providing you a misguided
hint.
Best,
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo   This discussion thread is closed Replies have been disabled for this discussion.   Question stats  viewed: 1229
 replies: 3
 date asked: Jul 23 '05
