Go to content Go to navigation

Simple vector class in C++ · 2009-12-12 14:44 by Black in

Mathematical vectors are often used in C++, but no classes for it exist in the std, the standard library. C++‘s Vector classes are heavy weight containers that offer a rich function set but are unsuitable for mathematics.

For ExaminationRoom I created a set of small classes to be able to easily pass vectors around in my code and perform simple operations on them. The implementation makes use of templates and operator overloading to offer an easy interface for users and still keep it flexible. It is not intended as a competitor to Boost’s uBLAS classes or similar rich mathematics libraries.

vec.h [7.47 kB]

  1. /**
  2. A small helper object, that is a 2 element vector. It can be treated as point
  3. (with x, y accessors) or an array (with operator[] accessor).
  4. */
  5. template <typename T>
  6. union Vec2
  7. {
  8.   enum { dim = 2 };
  9.  
  10.   struct
  11.   {
  12.     T x;
  13.     T y;
  14.   };
  15.  
  16.   T vec[dim];
  17.  
  18.   Vec2() {x = y = 0; };
  19.   Vec2(const Vec2<T>& v)
  20.   {
  21.     x = v.x;
  22.     y = v.y;
  23.   };
  24.   Vec2(T a, T b)
  25.   {
  26.     x = a;
  27.     y = b;
  28.   };

The code above is the start of the declaration of the Vec2 type. There are three base types, Vec2, Vec3 and Vec4, which are intentionally incompatible. Conversion with a creator is only possible when no data is lost. An interesting detail is that the class is not declared as normal class but as union, all members are at the same location in memory. That way, vector values can be accessed by their names or their location.

vec.h [7.47 kB]

  1. template <typename T>
  2. inline Vec2<T> & operator/=(Vec2<T> &v1, const T s1)
  3. {
  4.   v1.x /= s1;
  5.   v1.y /= s1;
  6.   return v1;
  7. }
  8.  
  9. template <typename T>
  10. inline const Vec2<T> operator+(const Vec2<T> &v1, const Vec2<T> &v2)
  11. {
  12.   Vec2<T> v = v1;
  13.   return v += v2;
  14. }

The operators are defined globally as inline functions since they are rather simple. When ever possible, the definition of an operator is built up on a previously defined one to minimize code duplication.
Some special methods were defined for normalization of the vectors, conversion to homogenous vectors as well as cross products of 3-element vectors. Often used types are defined with usable names. In ExaminationRoom, those were the types I used:

vec.h [7.47 kB]

  1. typedef Vec3<float> Vec3f;
  2. typedef Vec4<float> Vec4f;
  3. typedef Vec3f Point;
  4. typedef Vec3f Vector;
  5. typedef Vec3f Color3;
  6. typedef Vec4f Color4;

Lua Interface

An implementation of marshaling to Lua in the form of a simple table with luabridge was also written:

lua interfacing

  1. template <typename V>
  2. inline void pushVector(lua_State *L, V v)
  3. {
  4.   const int n = V::dim;
  5.   lua_createtable(L, n, 0);
  6.   for (int i = 0; i < n; i++)
  7.   {
  8.     lua_pushnumber(L, i+1);
  9.     lua_pushnumber(L, v[i]);
  10.     lua_settable(L, -3);
  11.   }
  12. }
  13. template <typename V>
  14. inline V toVector(lua_State *L, int idx)
  15. {
  16.   const int n = V::dim;
  17.   V v;
  18.   luaL_checktype(L, idx, LUA_TTABLE);
  19.   for (int i = 0; i < n; i++)
  20.   {
  21.     lua_pushnumber(L, i+1);
  22.     lua_gettable(L, idx);
  23.     v[i] = lua_tonumber(L, -1);
  24.     lua_pop(L, 1);
  25.   }
  26.   return v;
  27. }
  28. template <>
  29. struct tdstack <Tool::Vec2f>
  30. {
  31.   static void push (lua_State *L, const Tool::Vec2f & data)
  32.   {
  33.     pushVector<Tool::Vec2f>(L, data);
  34.   }
  35.   static Tool::Vec2f get (lua_State *L, int index)
  36.   {
  37.     return toVector<Tool::Vec2f>(L, index);
  38.   }
  39. };

From C++ to lua, a table is created and all elements are put into the table in order. This requires the type of the vector to be compatible with Lua’s number representation, which is usually a floating point number. Back converts tables that contain numbers back to a vector type of the suitable size. Missing or wrong table contents lead to lua errors that get caught with lua_pcall, all other table contents get ignored. (Here the reason for the existance of the enum “dim” is seen: A type variable that can be evaluated at compile time, which unlike static const variables do not take up space therefore can be defined and declared in a header.)

  Textile help