Make Graphics operations templates

Graphics::drawLine() variations should be a template instead of being restricted to float. They are all one line calls to context.drawLine() anyway so its no big deal.

Ditto for all the other one liners in the Graphics class.

Hmm. I’m not sure I agree about that, I can imagine lots of places where I’ve made small mistakes that would have compiled without warning if those methods were templated. I prefer the strictness of forcing all the arguments to be either float or int, since in this context those are the only types that make sense.

Just for fun:

template<class X> struct FloatOrInt { enum { Result = 0 }; };
template<> struct FloatOrInt<float> 
{ 
  enum { Result = 1 }; 
  float value; 
  operator float() const { return value; }
  FloatOrInt(float value) : value(value) {}   
};
template<> struct FloatOrInt<int> 
{ 
  enum { Result = 1 }; 
  float value; 
  operator float() const { return value; }
  FloatOrInt(int value) : value((float)value) {}   
};
 
template <class T, class U, class V, class W>
Graphics::drawLine(FloatOrInt<T> x1, FloatOrInt<U> y1, FloatOrInt<V> w, FloatOrInt<W> h)
{
    // You should pass in something that's convertible to float
    CompileTimeAssert(FloatOrInt<T>::Result && FloatOrInt<U>::Result && FloatOrInt<V>::Result && FloatOrInt<W>::Result);
 
   drawLineInternal((float)x1, (float)y1, (float)w, (float)h); // Calls operator float of the member    
}

Well in fact, the bit I like about avoiding templates for these methods is that it forces all the params to be the same type, so you don’t accidentally mix up ints and floats in the calling code…

Yeah, I’m not saying to allow mixing and matching. But there’s no drawLine that takes ints. There’s no fillEllipse that takes ints (etc…). So:

template <class T>
void drawLine (T startX, T startY, T endX, T endY) const
{
  context.drawLine (Line <T> (startX, startY, endX, endY));
}

But there’s no LowLevelGraphicsContext that takes an Line anyway so the template would have to call toFloat() anyway e.g.

template <class T> void drawLine (T startX, T startY, T endX, T endY) const { context.drawLine (Line <T> (startX, startY, endX, endY).toFloat()); }

But then the cast is hidden from the user. You might as well make the caller perform the cast, at least then they know what is actually going on and that the drawLine etc. is sub-pixel positioned over the drawHorizontalLine equivalents as they behave very differently.