Reputation: 4677
In graphics all geometric datatypes are very similar to each other. To avoid code duplication but still have some sense about what you are doing the Graphics Codex advises to use a Vector
class with multiple typedefs for Points
, Normals
and Directions
.
So based on the signature (as it is written) of the Normalize
function, one knows he is dealing with Directions
and not Points
or Normals
. Of course, nothing prevents you to pass a Point
or Normal
since they are all Vectors
(just aliases).
I wonder if there is a non-overhead way of forcing the programmer (i.e. compiler warning level 4) to pass something which is explicitly declared Direction
(without duplicating the Vector
struct multiple times in different structures or inheriting multiple times from the Vector
struct) and if not the programmer needs to do do an explicit cast (which is just a noop)? (Haskell for instance has this functionality)
struct Vector {
// lots of methods
float x, y, z;
};
typedef Vector Point;
typedef Vector Direction;
typedef Vector Normal;
Normal Normalize(const Direction &) {
...
}
Upvotes: 0
Views: 96
Reputation: 6066
You could do that also with templates, with the advantage not having to reimplement all the constructors:
template<typename tag>
struct Vector {
...
};
namespace tags {
struct point;
struct direction;
struct normal;
}
typedef Vector<tags::point> Point;
typedef Vector<tags::direction> Direction;
typedef Vector<tags::normal> Normal;
Normal Normalize(const Direction &) {
...
}
Note however, that although it doesn't have runtime overhead, it has code size overheard (the template instantiated with every different tag), as well as compile time overhead (consolidating all the weak symbols coming from instantiations in every translation unit into a single instance).
You can mitigate that to some extent by providing a non-templated base class which does the "real work".
Upvotes: 2
Reputation: 2014
Of course you can do that. You can use inheritance for that. As long as you dont use virtual methods you dont get runtime overhead.
The code would look like this:
struct Vector3d {
float x, y, z;
};
struct Direction :
public Vector3d {
};
If you have special constructos, that you want to use, you can include them into the childs with using declarations.
Upvotes: 1
Reputation: 249374
The only way is one that you imply you don't like, but here it is:
struct Point : Vector {};
struct Direction : Vector {};
struct Normal : Vector{};
There's not much downside, apart from needing to define and delegate any constructors you might have.
For C and using GCC specifically, here's a previous question on this topic: Warn if another typedef'd name of a type is used in an argument list
Upvotes: 1