Rosario 3D

My developer blog

Monthly Archives: March 2010

CppUnit + Hudson = Epic win

Finally I came with a good test driven pipeline for my code. It’s easy to write a test, I don’t have to write a lot of code for the tests and everything is tested automatically.

CppUnit

CppUnit is a test framework inspired by JUnit, writing test in C++ is more difficult then Java cause C++ don’t have language support to navigate trough classes, but again, a good design can solve a lot of programming issue.

With CppUnit all you have to write to run your test is

int main(int argc, char *argv[])
{
  CppUnit::TextUi::TestRunner runner;
  CppUnit::TestResultCollector  collector;
  CppUnit::TestResult result;
  result.addListener(&collector);

  CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
  runner.addTest(registry.makeTest());
  runner.run(result);
 // writing result on a XML file
  std::ofstream xmlFileOut("testresults.xml");
  CppUnit::XmlOutputter xmlOut(&collector, xmlFileOut);
  xmlOut.write();
  return 0;
}

This code create all the structure needed to get all the tests case, collect the results and write everything on a XML file. The file is then analyzed by Hudson.
To create a test case you have to create a class derived by TestFixture and write a methods for each test case. A fixture is a set of test, you can put all your test into one fixture, but is better do subdivide the fixture in different classes for compilation speed and clarity sake. In the test methods you can use some macros to make assertions, if an assertion fail the test fail.
For example here the dot product test to my vector class:

void testDotProduct(){
  CPPUNIT_ASSERT(dot(Vec3f::xAxis, Vec3f::yAxis) == 0.0f);
  CPPUNIT_ASSERT(dot(Vec3f::xAxis, Vec3f::zAxis) == 0.0f);
  CPPUNIT_ASSERT(dot(Vec3f::yAxis, Vec3f::zAxis) == 0.0f);
  CPPUNIT_ASSERT_DOUBLES_EQUAL(dot(op1, op1),
        op1.length()*op1.length(), tollerance);
  CPPUNIT_ASSERT(dot(op1, op2) == dot(op2, op1));  // commutative
  CPPUNIT_ASSERT_DOUBLES_EQUAL(dot(7.0f*op1, 5.0f*op2),
        (7.0f*5.0f)*dot(op1, op2), tollerance);  // distributive
  CPPUNIT_ASSERT(dot(op1, op2+op3) == dot(op1, op2) + dot(op1, op3));
}

To help the test runner to collect (and run) all the test the class should declare a static method called suite, it’s a very boring and repetitive method where you have to list all the test. To make it less boring CppUnit have some macros.

CPPUNIT_TEST_SUITE(VecTest);
   CPPUNIT_TEST(testLinearity);
   CPPUNIT_TEST(testNormalization);
//...
   CPPUNIT_TEST(testCrossProduct);
   CPPUNIT_TEST_EXCEPTION(testDivideByZero, MathError);
   CPPUNIT_TEST(testDotProduct);
CPPUNIT_TEST_SUITE_END();

In the main, you should add all the test suite to the runner, another boring work, also you have to include all the file test in the main. This create a lot of dependence, and you have to recompile the main every time you change a test. To avoid these problems you can register your suite into a test factory using the macro

CPPUNIT_TEST_SUITE_REGISTRATION(VecTest);

, and get the test with the factory.
You can find the whole code in the repository. You can find a guide to learn how to use CppUnit here.

Hudson

Hudson is a wonderful tools to automatically compile your code and make a report in case of errors, and also analyze the CppUnit test report (if you install the appropriate plug-in), that’s why I write a XML file in the test case. It’s designed to work with java but I can run any script to compile (so you can invoke make or any other tool chain you use). It can pool for changes in your repository and check if something is changed. But you can also trigger the build process on commit with a simple hook script in your svn repository. Hudson for every commit will check if something is changed in the project and compile if it’s necessary and will also send you a mail if something is wrong. 🙂

I still have to find a good (easy and free) code coverage analyzer, any suggestion?

Advertisements

Earth day

Today is the earth day, I’ll save some watt turning off the server (no commit today).
See you tomorrow.

Let's do optimization

A lot of programmer start with the idea of optimizing everything, they want to save the last clock cycle to make their program faster even if this cost some readability of the code. Making optimized code make you feel smart and sometime you can say… “WOW!! These are the smartest five line of code I ever wrote”

Read more of this post

Vector class

Difficulty: medium (a lot of template)

I want to write an article to speak  about quaternion and how beautiful they are to implement orientation and  keyframe interpolation. But before speaking about quaternion is better to start from the base and create a class for vector.

Vector class

When I speak about vector I refer to maths vector, not an array.  We will develop general version of a vector class that can be used to record our geometry data or other multidimensional data.
This implementation is inspired by the nVidia vector class that you can find with the nVidia SDK, is one of the best Vector class implementation I found. Let’s try to make it better.

Requirement:

  1. Speed: Vector class should be very fast, in a 3d program we will use a lot of vector maths and the speed of this class is crucial for the whole program.
  2. Small size: I also want my class to be compact, I will use the vector class to store the vertex attribute, I don’t want any other data in the middle.
  3. Readability: I want to access to the y coordinate with the .y notation

For the first two reason it’s not wise to use virtual function in our vector class. Virtual function are (slightly) slower, but the most important is that using virtual function will insert an hidden pointer that will increase the size considerably.

Let’s start with the first part of the class, first of all I’ll not create a class but a struct, cause I want all my data to be public, yep I can make the data private and then use getX, getY, setX, setY… but why? This will make only code less readable. A vector is a very simple data that don’t have anything to hide.

template<typename T>
struct Vec4Container
{
   static unsigned const len = 4;
   union{
      struct { T x, y, z, w; };
      struct { T r, g, b, a; };
      struct { T s, t, u, v; };
      T data_[len];
   };
};

I let you immagine how the Vec3Container and Vec2Container is be made.
I have used a template so my class is type independent, now I can create a vector of float, int and so on. I have used union, so I can access to my value with the .x .y .z notation or if my vec represent a color I can use .r .g .b .a or stuv for texture coordinate. OpenGL use s, t, r, q but we already used r in rgb.

Here the real vector class, the one that will perform the computation

template<class container, class T>
struct Vec : public container
{
public:
  static size_t size() { return container::len; }
  Vec() { }
  explicit Vec(const T *v) { for(size_t i = 0; i < size(); i++) container::data_[i]=v[i]; }
  explicit Vec(const T &v) { for(size_t i = 0; i < size(); i++) container::data_[i]=v; }
  Vec(const Vec &vv) { for(size_t i = 0; i < size(); i++) container::data_[i]=vv.data_[i]; }
...

This struct derive from container, so we can access the same data. I also added some constructor,  the first one is the default constructor, I don’t want to initialize my vector if I don’t explicitly needed, then I have a constructor from an array, a constructor from a value. Notice that I have added the explicit keyword, cause I don’t want that a single number can be interpreted as a Vec, this can lead to strange behaviors and confusion. I have added a size operator that return the length of the vector, useful for cycles. Note that this struct don’t add any overhead. The only problem is that cause we are using template C++ don’t know where data_ came from, and we must specify it every time.

We need one more constructor, is very useful to specify a vector like this b(4, 3) or w(1, 3, 5), we need some other classes

template<class T>
struct Vec3 : public Vec<Vec3Container<T> >
{
   Vec3() {};
   explicit Vec3(const T &v) : Vec<Vec3Container<T>, T>(v){ }
   explicit Vec3(const T *v) : Vec<Vec3Container<T>, T>(v){ }
   Vec3(const T& v0, const T& v1, const T& v2){
      Vec<Vec3Container<T>, T>::x = v0;
      Vec<Vec3Container<T>, T>::y = v1;
      Vec<Vec3Container<T>, T>::z = v2;
   }
};

this class will declare only the constructors. The Vec2 and Vec4 are very similar.

Now we can define some basic operation for Vec:

...
   T& operator[] (int i){
      return container::data_[i];
   }
   const T& operator[] (int i) const {
      return container::data_[i];
   }
   Vec& operator +=(const Vec &rop){
      for(size_t i = 0; i < size(); i++)
         container::data_[i]+= rop.data_[i];
      return *this;
   }
   Vec& operator -=(const Vec &rop){
      for(size_t i = 0; i < size(); i++)
         container::data_[i]-= rop.data_[i];
      return *this;
   }
...

There is no problem with sum or subtraction, but what we have to do with multiplication?
We can define different type of multiplication for vector, the dot product, the cross product, pairwise multiplication or scalar multiplication, every operation deserve a * operator. For clarity sake I’ll use a named method for dot and cross product and I’ll reserve the * operator for pairwise and scalar product.

...
   Vec& operator *=(const T &rop){
      for(size_t i = 0; i < size(); i++)
         container::data_[i]*= rop;
      return *this;
   }
   Vec& operator *=(const Vec &rop){
      for(size_t i = 0; i < size(); i++)
         container::data_[i]*= rop.data_[i];
      return *this;
   }
...

Dot product is a binary operation, so it’s better to make it extern to the class, same thing for cross product.

template<class container, typename T>
T dot(const Vec<container, T>& rOp, const Vec<container, T>& lOp)
{
  T ret(0);
  for(size_t i = 0; i < container::len; i++)
     ret += rOp[i] * lOp[i];
  return ret;
}

We can specialize the cross funtion to make is more efficient

template<typename T>
Vec3<T> cross(const Vec3<T>& rOp, const Vec3<T&>& lOp)
{
    Vec3 res;
    res.x = rOp.y * lOp.z - rOp.z * lOp.y;
    res.y = rOp.x * lOp.z - rOp.z * lOp.x;
    res.z = rOp.x * lOp.y - rOp.y * lOp.x;
    return res;
}

Other function like normalize, length and binary operator are trivial, you can implement them as exercise. 😛
Final touch:

typedef Vec3 Vec3f;
typedef Vec3 Vec3i;
typedef Vec3 Vec3ui;
typedef Vec2 Vec2f;
typedef Vec2 Vec2i;
...

Edit: I notice that a lot of compiler don’t like template of template, so I modify the code a little bit.