Click here to Skip to main content
15,867,749 members
Articles / Programming Languages / C++
Article

An STL-compliant mathematical vector class

Rate me:
Please Sign up or sign in to vote.
3.78/5 (8 votes)
12 May 20056 min read 51.4K   1.1K   25   11
A portable implementation of a templated, STL-compliant math vector class.

Introduction

The header file attached to this article implements the EVector class. In order to remove confusion from the start, this class is not a data structure, or an STL container. It does, however, resemble the std::vector class that's provided as part of STL even though their purposes are quite different.

std::vector is basically a container for objects of type T. This class, however, is an implementation of a vector in the mathematical sense. Therefore, it contains operators like +, -, *, etc.. as well as other functions that are used to manipulate vectors.

Background

This class was written as part of a library I've been writing. I wanted to create some mathematical infrastructure that will be used by other parts of the library. Up till now, I've used this class to write a matrix class and to specialize the case of 2D and 3D vectors for graphics and computational geometry algorithms. Even though I haven't provided those classes (I might in the future, though...) I think it's very easy to use/extend/specialize this class for such purposes by any user.

Implementation

While writing this class, I followed these guidelines:

  • This class is really low-level and will be used almost everywhere. This is why it has to be well written and conform to widely-used notation.
  • Future changes will be addition of functionality and not changes in already existing behavior.
  • Portability.
  • This class should conform to standard behavior in order to be used by other 3rd-party libraries or replace similar classes.
  • Other people might use this class and therefore it must be well documented. This will also facilitate changes in the future.

To adhere to the above, the code is documented using Doxygen. In addition, it is STL-compliant. This means that it has an allocator object and appropriate iterators that can be used to traverse the elements of the vector. The elements of the vector are allocated and deallocated using the allocator.

One of the intentions of this class is to be used for graphics and code that is run-time critical. Any of you who have used OpenGL or Direct3D are familiar with vertex arrays. A vertex array is just what its name implies - an array of (usually) 3D vectors. Such arrays can be cached, or optimized, and for such reasons there's no need to impose the use of an allocator instance for each instance of EVector. In this example, an array of EVectors can be easily used as a vertex array that can be passed to OpenGL and the programmer doesn't even need to calculate the offset between consecutive instances of EVectors (even though it's easy...). Such an array will definitely occupy less memory if there's no allocator instance for each EVector instance. This is why I make use of the E_VECTOR_USE_ALLOCATOR flag.

If you take a look at the code, you'll see the E_VECTOR_USE_ALLOCATOR flag. If this flag is defined, EVector has a template parameter A that determines the type of the allocator (which is by default std::allocator). In addition, each instance of an EVector object will hold an instance of an allocator - just like std::vector (or any other STL container). If, however, it isn't defined, all instances of EVector of the same type (meaning, having the same T and n) will share the same allocator instance. To make a long story short - E_VECTOR_USE_ALLOCATOR determines whether the allocator is a static member of EVector or not. This still allows the flexibility of providing different ways of allocating objects without the overhead of complexity and memory usage that might arise in most cases. It is important to emphasis that whether E_VECTOR_USE_ALLOCATOR is defined or not, EVector can be used identically. This is because if E_VECTOR_USE_ALLOCATOR isn't defined, A becomes a typedef of the class, and get_allocator() simply returns the static member.

Unfortunately, I can't get into the skinny math details, so I assume that most users of this class know some linear algebra. It really isn't complicated. As mentioned above, the class contains the standard functions that manipulate vectors. If anything is missing and should be in - let me know. :-)

As I mentioned above, the class is STL-compliant - which allows it to be used by STL algorithms. I've demonstrated this in the small test program for the case of std::reverse but, of course, it can be used with other algorithms as well. It also includes simple serialization to and from a stream.

An important thing to notice. The size of the vector is static!! It can't be changed at run-time and is determined completely by the template parameter n. This is in contrast to some other classes I've seen. There are several reasons for this. The main two are simplicity and performance. Like I said, this class is intended to be used for performance-critical tasks. If the size was changeable at run-time, simple operations like addition, subtraction, dot product, and so forth would have to be validated at run-time by checking that vectors have the same size and otherwise throwing an exception. Keeping the size static and part of the template parameters removes all such problems because these are simply verified during compilation. If one tries to define:

EVector< float, 5 > v;
EVector< float, 3 > u;

cout << u+v << endl;    // compilation error!

one would get a compilation error because the operators mentioned above are defined for vectors of the same size.

Using the code

This is simple. All you have to do is to include EVector.h in your source code. Then you can define:

EVector< double, 4 > v;    // creates an invalid vector of 4 doubles
EVector< int, 2 > p;    // creates an invalid vector of 2 integers
v.fill(0.1);    // v = (0.1, 0.1, 0.1, 0.1)
p[0] = 1; p[1] = 5;    // p = (1, 5)
v.normalize();    // |v| = v.length() = 1
std::reverse(p.begin(), p.end());    // p = (5, 1)

And start having fun with them...

Please look at the (Doxygen) comments in the code to find out what are the operators and functions that are defined for the class.

Points of Interest

Please notice that the default constructor creates an invalid vector. An invalid vector is a vector one of whose arguments is NaN (Not a Number). To check for the validity of a vector one should use the isValid() member function.

You'll see that for some of the functions there's an optional epsilon argument. This is provided because some computations (actually, most computations...) result in numerical errors. For example, two vectors are considered orthogonal if their dot product is 0. Since the computer has only finite accuracy, two vectors which would be practically orthogonal might not yield an exact 0 when calculating their dot product. The epsilon argument solves this problem by considering vectors whose dot product is less or equal to epsilon orthogonal, and vectors whose dot product is greater than epsilon - not orthogonal. The same principle applies for other kinds of tests.

History

No history yet...

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralLinear line Pin
flippydeflippydebop23-Jul-07 7:35
flippydeflippydebop23-Jul-07 7:35 
QuestionSTL compliant? Pin
WREY13-May-05 10:22
WREY13-May-05 10:22 
AnswerRe: STL compliant? Pin
Ambidextrous Coder13-May-05 10:52
Ambidextrous Coder13-May-05 10:52 
Among other things. These functions (e.g begin(), end(), size(),
get_allocator(), etc..) are typical to STL containers. As I explained,
I wanted the ability to use/manipulate this class using STL algorithms
and just like STL containers (such as std::vector, std::list, etc..).
This required defining such functions with their usual semantics. Another thing
is the iterators. begin() and end() return iterators that can be used to
traverse and change the contents of an EVector object. It also allows using
many STL algorithms on it.

Ambidextrous Coder
GeneralRe: STL compliant? Pin
WREY13-May-05 12:09
WREY13-May-05 12:09 
GeneralRe: STL compliant? Pin
Ambidextrous Coder13-May-05 13:58
Ambidextrous Coder13-May-05 13:58 
GeneralRe: STL compliant? Pin
WREY13-May-05 14:06
WREY13-May-05 14:06 
Questionboost::uBlas? Pin
lxwde12-May-05 16:59
lxwde12-May-05 16:59 
AnswerRe: boost::uBlas? Pin
Ambidextrous Coder12-May-05 19:01
Ambidextrous Coder12-May-05 19:01 
GeneralFiles aren't downloadable Pin
Don Clugston12-May-05 15:19
Don Clugston12-May-05 15:19 
GeneralRe: Files aren't downloadable Pin
Ambidextrous Coder12-May-05 15:32
Ambidextrous Coder12-May-05 15:32 
GeneralRe: Files aren't downloadable Pin
Don Clugston12-May-05 16:05
Don Clugston12-May-05 16:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.