Specify in the code when you can
When feasible, it is more expressive, ensures correct usage, and doesn't get out of sync.
- use narrower types (reference vs pointer, unsigned vs signed, enumeration vs integer, Derived* vs Base*):
/// \pre color is one of color_red, color_yellow, color_green
/// \pre p != 0
void f(int color, MyClass* p);
// better
void f(Color c, MyClass& r);
- capture implicit preconditions in classes -- especially if they form meaningful domain abstraction, and can be reused:
void process(int y, int m, int d);
// better
void process(const Date& d);
- choose good, idiomatic names:
std::ostream& operator<<(const A&, std::ostream&);
Point::Point(int x, int y);
bool MyPredicate::operator()(const C& lhs, const C& rhs) const;
virtual BasePtr Base::clone() const = 0;
void SomeValue::set_field(const S&);
size_t Container::size() const; // needs comment if it is not constant time!
Specify in the documentation if you have to
Unfortunately not everything can be expressed in such a straightforward way.
In that case, please check my list and provide meaningful description of how the code should be used and what does it do in different cases.
Without it your client is left alone with its implementation, and s/he never knows what part of it relates to the specification, what is just accidental detail of implementation, and what is simply a bug.
My favorite beast is std::lower_bound:
How many things about this function can you name?
Or, do you know what sqrt() does when negative argument passed in? Go and check its specification!
As an example of extremely good quality documentation, I strongly recommend to read through the C++ Standard sections describing Standard Library. Other good examples are POSIX and Boost libraries.
In that case, please check my list and provide meaningful description of how the code should be used and what does it do in different cases.
Without it your client is left alone with its implementation, and s/he never knows what part of it relates to the specification, what is just accidental detail of implementation, and what is simply a bug.
My favorite beast is std::lower_bound:
template <class ForwardIterator, class T, class Compare>
ForwardIterator lower_bound(ForwardIterator start,
ForwardIterator finish,
const T& value,
Compare comp);
How many things about this function can you name?
- ForwardIterator should model ForwardIterator concept;
- start and finish should be from the same sequence;
- that sequence should be sorted;
- sorting criterion should be the same as specified by comp;
- Compare should be a predicate;
- it returns an iterator to the first element which is "greater" that value according to comp;
- its complexity is logarithmic: performs at most log(finish-start) + 1 comparisons.
Or, do you know what sqrt() does when negative argument passed in? Go and check its specification!
As an example of extremely good quality documentation, I strongly recommend to read through the C++ Standard sections describing Standard Library. Other good examples are POSIX and Boost libraries.
No comments:
Post a Comment