Vectors and matrices are stored as dense arrays of values. All basic types are supported for the underlying storage type, and by default, the library includes instantiations for float, double, int and unsigned int in two, three and four dimensions.
src/generate.cpp. Please note that Vec?.h and Mat?.h are now generated files, and should not be edited directly. Edit VecTemplate.* and MatTemplate.* instead and re-run generate.
Note that this means that each individual header (Vec2.h, Vec3.h, etc.) should be smaller and faster to compile. Currently the headers only pull in <iosfwd> and <string>, the latter could be removed if the description() member is not needed.
Convenience.h and its build() functions has been removed in favor of using each classes' constructor directly. Similarily, the convert() methods (for converting from a class of different underlying type) have been replaced by the constructors.
LINALG_DEFAULT_INITIALIZE_TO_ZERO. If this macro is not defined then vectors and matrices are not initialized in the default constructor. If this macro is defined, then they are intialized to zero.
On Unix-type systems, link against the system-supplied LAPACK library, ususally called liblapack or, on OS X, the vecLib framework. Under Windows, the easiest way to get LAPACK is to download Intel's MKL library, which includes LAPACK. Unfortunately, the MKL is commercial software, though it does have a free evaluation.
If you don't care about computing eigenvalues and eigenvectors for dimensions four and above, then you can define the macro LINALG_NO_LAPACK, which will remove the requirement to link against LAPACK and make LinAlg completely self-contained.
ide/XCode 3. This should be compatible with XCode 2.x, but I can't verify this.ide/VisualStudio8.0.src/run_tests.cpp that tests most aspects of the library and a tiny example in src/example.cpp that demonstrates "normal" usage. You should be able to build and run these to verify that everything is working. The included IDE project files include targets to do this for you.Vec and Mat for the types float, double, int and unsigned int in two, three and four dimensions. If this is all you need, then you can skip this section. If you need LinAlg to not generate these default types, or if you need to use a custom type, then you should read this section.C++ template libraries are different from standard libraries. A C++ template does not actually generate code; it's a pattern that tells the compiler how to generate code when the user actually creates an instantiation of the class.
A normal class usage might look like this:
// In the header List.h class List { public: void print(); ... }; // In the implementation List.cpp #include "List.h" void List::print() { ... } // In some program #include "List.h" int main() { List list; list.print(); }
print() is stashed away into an object file List.o. When the main program is compiled, then the compiler generates a reference to the body of List::print(), then the linker puts it all together into a runnable program. It works because the linker can find the implementation of List::print() in List.o. The important point is that when the compiler is compiling the main program, it doesn't have the implementation of List::print() (it's only read List.h), but it can generate a reference to it and trust that the linker will find it.
If we change List to depend on a template parameter T, we might start with this:
// In the header List.h template <typename T> class List { public: void print(); ... }; // In the implementation List.cpp #include "List.h" template <typename T> void List<T>::print() { ... } // In some program #include "List.h" int main() { List<int> list; list.print(); }
List<int>::print(). When the compiler compiles the main program, it reads only List.h, not List.cpp. So when it sees the declaration List<int> list, it does its copy and paste trick replacing T for int in the List class. However, since it doesn't have the implementation of print(), all it can do is generate references to List<int>::print(), just like in the non-template version. So far, so good. However, the problem is that when the compiler compiles List.cpp, it has no clue what types might get substituted for the type T, and so it generates no actual object code at all. So List.o has no object code for List<int>::print() and the linker will fail to produce a working program.A related problem is inline functions: for the compiler to insert the body of an inlined function into its caller, it must have the entire definition at hand when the function is called. Hence inline functions must always be placed in full in the header. Most of LinAlg functions are very simple (generally consisting of a single loop over the elements) and are inlined and thus defined fully in the header. However, some significantly-large ones are not: for example, input/output and those that call the standard numerical library LAPACK. The rest of this discussion pertains to these non-inlined functions.
In our simple example, there are currently two solutions: the first, and most common, is to simply append the definitions from List.cpp at the end of List.h as inlined functions and forget about List.cpp. Then, when the compiler compiles the main program, List.h contains the complete code for List and the declaration List<int> list generates object code for all of List<int>, including List<int>::print(). This is a "header-only" library, but it has one major drawback: the header file can get enormous. Every file that includes List.h has to read and parse the entire implementation of List! You are in effect not just compiling List.cpp once, but every time you compile a file that uses List. Remember also that while header files can often forward-declare or otherwise avoid including other headers, the actual implementation always needs full definitions and headers for everything it uses. This is a major reason why many C++ libraries are so horribly slow to compile against. Slow compilation times mean low programmer productivity.
The other solution is to force the compiler to generate object code for List<int> while it is compiling List.cpp. C++ has a mechanism for this, it is called explicit instantiation. With explicit instantiation, our example becomes:
// In the header List.h template <typename T> class List { public: void print(); ... }; // In the implementation List.cpp #include "List.h" template <typename T> void List<T>::print() { ... } template class List<int>; // Explicit instantiation // In some program #include "List.h" int main() { List<int> list; list.print(); }
List.cpp and reads the explicit instantiation, it generates object code for all of List<int> and places them in List.o, since it has all the definitions it needs at that point. The linker will end up finding the object code for List<int>::print() in List.o and will generate a working program. The upside of this solution is that the implementation is only compiled once, not multiple times and the header gets to stay as small as possible. The downside is that you somehow have to magically know what types you will be using in your program and insert the explicit instantions into the implementation code. For general libraries, this is tricky or impossible. However, for a relatively well-characterized library like a linear algebra library, however, we can probably guess.
LinAlg uses this second solution to keep the header files as small as possible. By default we instantiate the templates for the float, double, int, and unsigned types. When compiling the library, you can control the explicit instantiations in two ways:
LINALG_SKIP_DEFAULT_INSTANTIATIONS.LINALG_INSTANTIATE_USER_TYPE. For example, define LINALG_INSTANTIATE_USER_TYPE=long double
long double's as storage. If you need more than a single type, edit VectorTemplate.cpp and MatrixTemplate.cpp, add your types to the end, and re-run generate.http://support.microsoft.com/default.aspx/kb/930198
You should only apply this hotfix if you are actually getting the C2244 error.
generate to make changes to any of Vec?.h or Mat?.h.operator[] from the vector classes.run_tests.cpp Vector::unit() for creating unit vectorsVector::proj() to project one vector onto anotherVector::rand(), since random number generation is very application-specific and shouldn't be in a library.buildVec() and buildMat() routines and replaced with the unified build() routines in Convenience.h.© 2005-2008 Adrian Secord.