tags:

views:

290

answers:

8

I'd like to write function interfaces that force the user to acknowledge the semantic meaning of built-in constants. For example, I'd like to take

void rotate(float angle); // Rotate the world by an angle in radians.

and change it to

void rotate(Radians angle);

Am I right in believing that the problem with making a Radians class is that it adds code and makes the program slower. Is there a better way to do this?

+5  A: 

This is the perfect way to do this. Thats what makes C into C++. Using classes where you need them and encapsulate data. It does not make program any notable slower, since compiler does all the optimizations.

alemjerus
With a thin, inline class, there isn't any real overhead. And, yes, wrapping primitive values this way is a great idea.
Steven Sudit
I mention boost.units because it will do a lot of the work for you.
Omnifarious
A: 
Joe
Why should the radians class be any bigger than a primitive float?
UncleBens
How is a class that has one `float` member bigger than a `float` in practical usage?
Bill
+1 for the rotate(float radians) it is simple, it doesn't introduce unnecessary type as Radians is which may raise new questions, and the name of parameter could not make the function clearer and more self-descriptive - it's obvious that radians mean angle. KISS principle applied.
mloskot
Assuming there's nothing else to it, yes, a Radians class won't be any bigger than a float.
Joe
@mloskot: Problem is, `rotate()` itself doesn't imply radians, and the programmer isn't going to look at the prototype before coding any use. The idea here is to catch incorrect code (like `rotate(90);`) with the compiler, not when things look bad on the screen in a test.
David Thornley
@David Thornley Yes, given the more assumptions you specified, I agree the Radians make the interface, so the contract, less ambiguous. Perhaps I didn't catch enough details from OP's original question and assume there is only one rotate function which accepts radians. Yes, it would be good to validate input in the function and signal invalid arguments, perhaps with boolean return value or exception (it is C++).
mloskot
`void rotate(const Radians ` will most likely be **slower** than `void rotate(Radians angle);`. Modern compilers will pass small objects with a single member in a register.
MSalters
Some good comments here. I like other answers better.
Joe
+14  A: 

No, it is possible to make a Radians class that should be optimized by most decent compilers into something that's no slower than a plain float. You might be interested in boost.units.

In fact, with boost.units you can even set it up so that if someone wants to pass in an angle in degrees it will automatically be converted to radians before being passed to the function. That will possibly slow things down a bit, but it arranges it so you can change what units a function wants without having to go back and edit a whole ton of code.

And, when you finally do want to go and edit all the code, you can temporarily fix it so the conversion doesn't work and let the compiler find it for you.

Being able to make the compiler enforce constraints like this for you with no runtime penalty and possibly even write all the conversion code for you (at a very tiny runtime penalty) is one of the really neat things about C++ and makes it worth the added complexity over C.

Here is a really simple version of what this class might look like if you hand coded it:

#include <cmath>

// This class is tiny enough because it has no virtual functions and only one
// data member that it's likely more efficient to pass by value than by
// reference.
class Radians {
 public:
   // If you don't put in explicit, the compiler will automatically convert a
   // float to a Radians for you and undo all of the hard work you did to make
   // sure callers express their intentions.
   explicit Radians(float radians) : value_(radians) {}

   float getValue() const { return value_; }
   void setValue(float radians)  { value_ = radians; }

   float asDegrees() const { return value_ * 180 / M_PI; }

   // This allows you to say Radians r = Radians::fromDegrees(something);
   static Radians fromDegrees(float degrees) {
      return Radians(degrees * M_PI / 180);
   }

 private:
   float value_;
};

Notice how all of the functions are declared in the class body. This makes them all implicitly have the inline keyword. A good compiler will optimize all of those functions out of existence. Of course, the conversion functions will generate the code to do the conversion, but otherwise it'll be the same as having a bare float.

Omnifarious
@Nikolai N Fetissov, Oops. Thanks for the edit. :-)
Omnifarious
+1 for not having forgotten to comment about explicit constructor.
Ugo
+1  A: 

With the simplest versions (a typedef, a class with one member attribute and inlined accessors) the compiler can make it just as fast the plain float version.

Also consider using some metaprogramming library like boost.units.

David Rodríguez - dribeas
Typedef isn't type safe.
Steven Sudit
True, but it provides the reader with context on what the variable means or the range it has.
David Rodríguez - dribeas
+2  A: 

It does not need to be slower.

The Ogre3d library have a Radian class. The assembly generated is exactly the same as that of using a float directly(atleast when I last tested under gcc with optimization enabled)

nos
+4  A: 

The key to this constraint is to declare your constructor explicit:

class Radians
{
public:
  explicit Radians(float value) : m_value(value) {}
private:
  float m_value;
};

That way your user can't type rotate(4.5). They'll have to type rotate(Radians(4.5)).

Bill
A: 

You could use a struct instead of a class. Functions in Structs

JadziaMD
Um, and how is a struct more efficient than a class? A struct *is* a class in C++ but with public fields by default!
Kornel Kisielewicz
What would the difference be? In C++, class and struct differ only in whether their members default to public or private.
Steven Sudit
There's also a more subjective difference between a struct and a class: a struct is usually seen more as a collection of related data items, while a class is usually seen more as a data type. We're going for the latter here.
David Thornley
+1  A: 

It is absolutely possible to write OOP code that is as efficient as the procedural code. Even more, sometimes you can gain surprising speedups if you take advantage of the added semantics or class-based code. One of the great examples of such an advantage is std::swap. Yes, use templates ;).

What makes it possible to write an effective Radian class is inlining. However, use it wisely!

Checklist:

  • read about efficient inlining
  • take care to have an explicit constructor
  • take care to properly use the keyword const where applicable
  • read about Optimizing C++
  • finally, if you do a lot of expressions involving your class, google up about Expression Templates
Kornel Kisielewicz