views:

104

answers:

2

I find myself defining classes like:

struct AngleSize {
    explicit AngleSize(double radians) : size(radians) {}
    double size;
};

This has a bunch of advantages over storing anglesizes as plain doubles.

Advantage 1, it makes it possible to define these 2 distinct constructors:

Vec2::Vec2(double x, double y);
Vec2::Vec2(double length, AngleSize direction);

... the latter one unambigously called as Vec2(1, AngleSize(5));
(though in this case a static factory function like Vec2::fromPolar(1, 5) might have been just as good?)

Advantage 2, it's more typesafe, the following mistake is caught:

double temperature = air.getTemperature();
Vec2 v(someLength, temperature);

Yet all is not fine.

Disadvantage 1, verbose and unexpected syntax

Vec2::Vec2(double length, AngleSize direction) {
    x = length * cos(direction.size);
    y = length * sin(direction.size);
}

Ugh, this should really say cos(direction) yet without allowing an unsafe implicit conversion, I need to verbosely access the actual value.

Disadvantage 2, too many classes for every single thing, where do you draw the line?

struct Direction { // angle between line and horizontal axis
    explicit Direction(AngleSize angleSize);
    AngleSize angleSize;
};

struct Angle { // defined by "starting and ending angle" unlike AngleSize
    Angle(Direction startingDir, Direction endingDir);
    Angle(Direction startingDir, AngleSize size);
    Angle(Line line1, Line line2);
};

struct PositionedAngle {
    // angle defined by two rays. this is getting just silly
    PositionedAngle(Ray firstRay, AngleSize angle);
    PositionedAngle(Point center, AngleSize fromAngle, AngleSize toAngle);
    // and so on, and so forth.
};
// each of those being a legit math concept with
// distinct operations possible on it.

What do you do in such cases, yourself?

Also where can I read about this problem? I think boost might have something related?

Note that this isn't just about geometry, it's applicable everywhere. Think posix socklen_t...

A: 

I don’t do this, I’d find it overly verbose and complicated for the tiny advantages it brings. I sometimes do a simple typedef to make things easier to read, but that is all.

zoul
+3  A: 

I do this whenever it makes sense. Compare with How do you make wrong code look wrong? What patterns do you use to avoid semantic errors?.

In the example of Joel's article he discusses two cases:

  • The case of a web application that needs to sanitize strings before output. He proposes type prefixes to distinguish the types. However, I argue that it's much better to simply define two different string types. The easiest way to do this is just to introduce a SafeString type. Only this type can be directly sent to the client. The normal string type has to be converted (either explicitly or implicitly; it doesn't matter in this case, because the implicit conversion can still sanitize the string).

  • The example of Microsoft Word where the programmers needed to distinguish between screen coordinates and document coordinates. This is the classical case where two distinct types offer a lot of advantages, even though these types are nearly identical (they both describe 2D points).

To address your disadvantages:

Disadvantage 1, verbose and unexpected syntax

Most languages actually allow to overload functions/operators in a way that make classes use expected syntax. In C++, this is even best practice: “make your own types behave like ints.” Usually, this doesn't necessitate implicit casts in the presence of appropriate overloads.

Disadvantage 2, too many classes for every single thing, where do you draw the line?

Make as many custom types as you need (but not more). Some languages, such as Haskell, actually encourage the proliferation of types. The only thing that makes us hesitate in languages like C++ is lazyness because we've got to write a lot of boilerplate code. C++0x makes this slightly easier by introducing type-safe type aliases.

Of course, there's always a trade-off involved. However, I believe that as soon as any one of the advantages apply for you, this disadvantage is simply balanced.

Konrad Rudolph