Never use built in types
This sounds really daft, but it is not. A lot of programmer errors are caused by confusion about values. For example, assigning an address to a name. Was the value in minutes or seconds? To make matters worse, C++ actually encourages implicit conversion between types, and the programmer needs to go out of his/her way to make constructors explicit to prevent this conversion.
Another problem is documentation. If a function takes an "int", there is little clue about the semantics of the value. The meaning must be elaborated in the function name, parameter name or documentation.
A large class of error is caused by programmers passing the wrong kind of value.
Another problem is the premature commitment and viscosity (to use CD terms) of one particular build-in type. Suppose you write an application to use char, then realise that you really wanted a wchar_t. You would need to change every instance. Not a problem but a hassle.
The solution
You would think that using a typedef would aleviate some of these problems. e.g.
class Seconds
{
public:
explicit Seconds(int value);
int operator*() const;
};
This is fine but is a lot of work to introduce a new type. Instead I propose to tag each value with a type, as follows
Examples
This is turning into a units library. We should see how we can extend this basic implementation to deal with multiples and conversions.
This sounds really daft, but it is not. A lot of programmer errors are caused by confusion about values. For example, assigning an address to a name. Was the value in minutes or seconds? To make matters worse, C++ actually encourages implicit conversion between types, and the programmer needs to go out of his/her way to make constructors explicit to prevent this conversion.
Another problem is documentation. If a function takes an "int", there is little clue about the semantics of the value. The meaning must be elaborated in the function name, parameter name or documentation.
A large class of error is caused by programmers passing the wrong kind of value.
Another problem is the premature commitment and viscosity (to use CD terms) of one particular build-in type. Suppose you write an application to use char, then realise that you really wanted a wchar_t. You would need to change every instance. Not a problem but a hassle.
The solution
You would think that using a typedef would aleviate some of these problems. e.g.
typedef int seconds;This helps a lot but does not prevent mistakes when converting between the two. So you create a class
typedef int minutes;
class Seconds
{
public:
explicit Seconds(int value);
int operator*() const;
};
This is fine but is a lot of work to introduce a new type. Instead I propose to tag each value with a type, as follows
template
class tag_t
{
public:
explicit tag_t(const Prim value &= Prim() ) : m_value(value) { }
template
tag_t(const tag_t&v) : m_value(*t) { }
const Primitive &operator*() const { return m_value; }
private:
Primitive m_value;
}
templatetag_t tag(const Prim &p)
{
return tag_t(p);
}
Examples
struct SecondsTag; struct MinutesTag;Next
void setTime(tag_tmins,
tag_tsecs);
setTime(tag(12), tag (0) );
This is turning into a units library. We should see how we can extend this basic implementation to deal with multiples and conversions.
0 Comments:
Post a Comment
<< Home