C++ Template Ideas

On this page you can find some C++ code which I wrote to try out ideas pertaining to templates.

A Class for Implementing 'Smart Properies'

This set of classes allows the declaration of a member which intercepts any setting or getting of the contained data. The main class of the framework is Property which has two formal type parameters, T and D. The parameter T is the type of the contained data, and D is an optional delegate class which provides the functions to be called when the property data is retrieved or modified. D defaults to Delegate which is intended to have specializations which perform generic getting and setting operations typical of a particular data type.

The intention of this framework is to make any required special operations when getting or setting a member transparent, relieving the outside user of the member from explicit boilerplate code. It also saves the user from the possibility of mistakenly missing out or incorrectly calling required boilerplate functions. Furthermore, the user code may be statically polymorphic if Property is used as a type parameter.

// Generic Delegate type. Any usage of this template
// should correspond to a specialization.
template<class T> struct Delegate
{
};


// This is the Property type which calls upon the Delegate
// class when contained data is accessed. The Delegate class D essentially
// intercepts any setting or getting. T is the contained datatype.
template<class T, class D = Delegate<T> > class Property
{
   // These functions need to access the internals of Property. They
   // are also required to be implemented by D.
   friend int  D::get(Property<T, D>&);
   friend void D::set(Property<T, D>&, T);

   public:
      // Public transparent getter
      operator T()
      {
         return get();
      }

      // Public transparent setter
      Property(const T& t)
      {
         set(t);
      }

      // Default constructor
      Property()
      {
      }

   private:
      // Private contained data
      T t;

      // Private convenience getter
      T get()
      {
         return D::get(*this);
      }

      // Private convenience setter
      void set(const T& u)
      {
         D::set(*this, u);
      }
};




// Template specialization of Delegate for 'int'. This is really
// part of the example, and not part of the framework.
template<> struct Delegate<int>
{
   typedef Property<int> P;

   static int get(P& p)
   {
      printf("* result of getting int property: %d\n", p.t);
      return p.t;
   }

   static void set(P& p, int i)
   {
      printf("* int property being set to: %d\n", i);
      p.t = i;
   }
};

// This Delegate class is specified explicity in one of
// the example usages.
struct ExampleDelegate
{
   typedef Property<int, ExampleDelegate> P;

   static int get(P& p)
   {
      printf("* (ExampleDelegate getter)\n");
      return p.t;
   }

   static void set(P& p, int i)
   {
      printf("* (ExampleDelegate setter)\n");
      p.t = i;
   }
};


// An example of using Property in a concrete class definition
struct ExampleClass
{
   // This property uses a generic delegate
   Property<int> property0;

   // This property uses a 'custom' delegate, which is allowed
   // to be specific to this class member
   Property<int, ExampleDelegate> property1;
};



// An example of using the example class
int main(int argc, char** argv)
{
   ExampleClass test;

   // Set the property as would be done if the property
   // were a plain datatype
   test.property0 = 56;
   test.property0 = 100;

   printf("performing sum of properties...\n");

   // I thought that a cast may be needed for the first term here,
   // but it seems to work okay.
   int blah = test.property0 + test.property0;

   printf("result of sum is: %d\n", blah);

   test.property1 = blah;

   printf("setting var...\n");
  
   // Get the property in the same way. Note that this is receiving a float, not an int. This is okay since an implicit cast occurs.
   float var = test.property0;

   printf("var is now set. using property to make temporary...\n");

   // A cast is required here! It would be better with C++ streams, but oh well.
   printf("%f, %d\n", var, (int)test.property0);

   printf("done.\n");

   return 0;
}


Bezier Curves with Template Metaprogramming

This set of classes can be used to evaluate points on a Bezier curve of any degree using the Bernstein polynomial method. The coefficients for the curve are pre-computed and the loops unrolled through use of templates and template specialization. The control points of the curve can be of any type so long as they support some basic arithmetic operations.

typedef unsigned int uint;
typedef float Real;

// Compute value as the factorial of i
template<uint i>
struct Factorial
{
   static const uint value = i * Factorial<i - 1>::value;
};

// Compute value as 1, which the factorial of zero is defined as
// This terminates the template-metaprogrammed loop
template<> struct Factorial<0>
{
   static const uint value = 1;
};

// The binomial coefficient, for control point i and degree n
template<uint n, uint i>
struct BinomialCoefficient
{
   static const uint value = Factorial<n>::value / (Factorial<i>::value * Factorial<n - i>::value);
};

// The bernstein polynomial, for control point i and degree n
template<uint n, uint i>
struct BernsteinPolynomial
{
   static float value(const Real t)
   {
      return Real(BinomialCoefficient<n, i>::value) * std::pow(Real(1) - t, int(n - i)) * std::pow(t, int(i));
   }
};

// Evaluation of the Bezier curve, control points of type T, degree n, for control point i
template<typename T, uint n, uint i>
struct BezierCurveEvaluate
{
    // Evaluate the curve at t. The start of the curve is returned for t = 0 and the end for t = 1
   static T evaluate(const Real t, const T *const control_points)
   {
      return control_points[i] * BernsteinPolynomial<n, i>::value(t) +
                  BezierCurveEvaluate<T, n, i - 1>::evaluate(t, control_points);
   }
};


// Evaluation of the Bezier curve, control points of type T, degree n, for control point zero
// This terminates the template-metaprogrammed loop
template<typename T, uint n>
struct BezierCurveEvaluate<T, n, 0>
{
    // Evaluate the curve at t. The start of the curve is returned for t = 0 and the end for t = 1
   static T evaluate(const Real t, const T *const control_points)
   {
      return control_points[0] * BernsteinPolynomial<n, 0>::value(t);
   }
};

// The Bezier curve class. Control points are objects of type T, and the curve has degree n.
// To set up the curve, the control_points array must be updated. The ControlPoint type member can be
// used to determine the type of data required by the curve.
template<typename T, uint n>
struct BezierCurve
{
   typedef T ControlPoint;
   static const uint control_point_count = n + 1;

   T control_points[control_point_count];

   T operator()(const Real t) const
   {
      return BezierCurveEvaluate<T, n, n>::evaluate(t, control_points);
   }
};



Comments