|
|
|
Advertisement
|
The first concerns the choice of unsigned values for the
width and height data members of the second
version of the Rectangle class. It may seem sensible to use
unsigned values here for fields which should not become
negative but it means we will have to take extra care with our arithmetic.
Consider a member function to grow a rectangle:
/**
* Grow the linear dimensions of the rectangle.
*
* @note Supply a negative value to shrink the rectangle.
*/
void Rectangle::grow(int pixels)
{
// Take care we don't shrink too much!
width = std::max(width + pixels, 0);
height = std::max(height + pixels, 0);
}
Despite the comment, not enough care has been taken. This is the
signalUpdate() problem all over again.
The second potential defect concerns the third parameter to
textRender(), the boolean which defaults to false:
textRender(text,
full_screen,
true); // wrap text
The comment here indicates one problem. We need this comment in order to
understand what the boolean actually means. An enumerated value would
allow the code to express its intent directly. A second problem is to do
with the upgrade path we have started on for textRender():
i.e., adding defaultable parameters at the end of the function. This has
the somewhat dubious advantage of not requiring existing users of the
function to have to change—I have already suggested that some
interface changes should not be made backwards compatible. In time, we
may end up with a function declaration like this:
void
textRender(std::string const & text,
Rectangle const & region,
bool wrap = false,
bool bold = false,
bool justify = false,
int first_line_indent = 0);
Here, we are almost inviting clients to call this function with parameters in the
wrong order. It would be better to pack the arguments relating to text display
into a structure:
void
textRender(std::string const & text,
Rectangle const & region,
TextControls const & controls);
A more radical approach would be to enlist the Boost Parameters library
[8], which uses some astonishing
metaprogramming techniques to provide C++ with keyword arguments, allowing
us to call our new function as follows (for example):
boostTextRender(text = "Built in Type Safety?",
bold = true,
region = full_screen);
or, equivalently:
boostTextRender(region = full_screen,
bold = true,
text = "Built in Type Safety?");
There are other points of weakness in the C/C++ type system. Enumerated values and booleans get mistaken for integral types all too easily. And what's worse, in a mixed language system, such as the one deployed on the Talk-To-Me, they can change size and alignment when passing from C to C++.
This article has offered a few survival tips already. I would like to conclude by adding a few more. There's nothing here which hasn't been said before, but I think these bear repeating in the light of the preceding.
Rectangle's constructor. This makes it harder to submit
parameters in the wrong order.
The source code presented in this article has been considerably simplified for expositional purposes. I do not think it a coincidence that the actual defective code was buried in the middle of rather more complicated functions.
This article has focused on some C++ techniques to circumvent a couple of
simple defects. Our best protection, is, however, language independent.
It's down to the way in which we approach software development: and that
will have to remain the subject of another article.
Discuss this article in the Articles Forum topic, Built-in Type Safety?.
Thomas Guest is an enthusiastic and experienced computer programmer. During the past 20 years he has played a part in all stages of the software development lifecycle—and indeed of the product development lifecycle. He's still not sure how to get it right. His website can be found at: http://homepage.ntlworld.com/thomas.guest
|
Sponsored Links
|