REVIEW - Designing Components with the C++ STL - A New Approach to Programming

Title:

Designing Components with the C++ STL - A New Approach to Programming

Author:

Ulrich Breymann

ISBN:

0201674882

Publisher:

Addison-Wesley Professional (2000)

Pages:

300pp

Reviewer:

Martin Fabian

Reviewed:

August 2000

Rating:

★☆☆☆☆

The following extensive review is for the revised edition of a book whose 1st edition I reviewed relatively favourably. I recollect that I was not entirely happy with my review. In the context of the year 2000 I would have written something different.
I am grateful to Martin Fabian (he is not a member of ACCU) for taking time out to write and donate this review for publication.
- Francis Glassborow
The title of this book is promising. So is the (description of) the author. From the back cover:

"Dr Ulrich Breymann is Professor of Technical Computer Science and Managing Director of the Institute for Computer Science and Automation at the Hochschule, Bremen. He has nearly 20 years of experience working in industrial systems analysis and software development, and is a member of the German DIN Working Group for the standardization of C++. He has written two best- selling German books on C++, edited another and is a regular contributor of C++-related articles to numerous journals and magazines."

Unfortunately, this book does not live up to the promise. Only 50 pages into the book I was thinking of demanding my money back. Continuing reading, and trying out the downloadable examples (http://www.informatik.hs-bremen.de/~brey/stlbe.html) did not alleviate my aversion. The numerous errors, bad practice, non-standard terminology, dubious comments, non-portable (even non-compilable!) and seriously exception-unsafe code, etc. spoiled my reading.

The first half of the book explains the stl and describes its contents. The main part of this is basically a repackaging of the publicly available documentation, perhaps slightly more accessible for the beginner. The second half of the book (

Part III, Beyond the STL: components and applications ) presents set operations on non-sorted containers (such as associative containers) in Chapter 6. In Chapter 7 a fast associative container is described; a hashed map implementation. For some reason overloaded operators are considered necessary (Section 7.4), '+' for union, '*' for intersection, '-' for difference and '^' for symmetric difference. (Why '<=' is not regarded as useful for expressing inclusion, I do not understand.) Chapter 8 shows a number of applications using the stl containers (why not the component developed in Chapter 7?), such as cross-referencing, indexing and a thesaurus implementation. Vectors and matrices are described in Chapter 9. For sparse matrices (Section 9.4) a comparison is made between using
std::map
and the hash-map from Chapter 7. Chapter 10 describes external sorting, both with practically no usage of internal memory and with using the available memory (again, std::containers are used). Finally, Chapter 11 shows how std::containers can be used to implement graphs, and give some basic algorithms over such graphs (Dijkstra's shortest path, and topological sorting). There is an appendix with various code snippets that the author has found useful now and then.

I lack a clear explanation of what this book really is meant to be. "Desgning components" the title says, but I do not see that much of component design. I guess the hashed associative container, and matrices and graphs qualify as components, but there is not too much depth with respect to designing them. Their implementations are merely presented, and I hardly think this is enough for a book claiming to teach/explain how todesign components using the stl. What has Chapter 8 and the Appendix to do with designing components? Chapters 7, 9, 10 and 11 are the most "nutritious" chapters, however, they only encompass a small part of the entire book, definitely too little, and definitely too little of "designing components". Is this a book for the novice or the expert? I would have to answer "neither". It contains too many errors and bad practice to be useful for the novice. The example files are not straightforwardly compilable, for instance. As for the expert, well, the book still contains too many errors and bad practice to be enjoyably reading. In addition, the presented algorithms are well-known and straightforward.

I think that std::c++ programming is beginning to come to a mature state-of-the-art, as documented, investigated and explored by people like Stroustrup, Lakos, Meyers, Sutter and others. It seems that in the latter years our knowledge about how to use what std::c++ offers has grown and matured. We, as a community, and that includes authors and publishers of c++ books, have a responsibility to teach and present examples of good coding practices and standards. This book does not contain the type of mature high-quality std::c++ code that could be expected in the year 2000.

Nonstandard terminology:

Why is the default constructor referred to as the "standard constructor"? Also, the author goes a long way in trying to make a distinction between "abstract data types" and "implicit data types". I never did understand the distinction or why it has to be made (or what an "implicit data type" is.) It has to do with what are generally known as "adaptors", a term which is briefly mentioned in the introduction to Chapter 4.

Nonportable code: :

In the second example of the book (Section 1.3.4) the author fearlessly assumes that
std::vector::iterator
is equivalent to
int*
. Admittedly, a remark that this is not guaranteed is made in Section 3.1, but that is much too late.

Bad practice: :

using namespace std;
is routinely added to all header files. I understand that for the sake of brevity, it might make sense, at least in small examples in a book. Almost all books on std::c++ do this to save on space. However, most of those books also include some comment about this ("do as we say, not as we do"), as they should. Not so here. Furthermore, the downloadable files, which would represent ready-to-use "components" also include a
using namespace std;
directive, and this is truly bad. Note that even files that do not include anything from std:: have a
using namespace std;
directive at their top (for instance,
setalgo.h
, see below). Obviously these are not compilable unless some assumption about the inclusion order is made.

Plain errors: :

In the description of the containers,
const_iterator
is claimed to be returned by "container with constant elements". Of course, since constant elements are not assignable, such std::containers cannot exist, this is common knowledge. Furthermore, many of the presented functions take containers by
const
ref (as they should) but still try to get an
iterator
(as opposed to a
const_iterator
) from
begin()
. Of course, this cannot and should not compile (though it seems that MSVC++6 compiles them! Other compilers issue errors).

Dubious comments:

It is claimed that container assignment can in some cases be optimised away by the compiler inserting a swap instead. The std::c++ community has recently been discussing this question on comp.lang.std.c++.moderated, and the consensus was that the claim is dubious at best, and probably incorrect.

Inconsistent variable naming:

In some places, for no obvious reason, single letters are used for variables, in some cases single capitals. This makes for tough reading. Mostly, more reasonable naming schemes are adopted, though.

Hard-to-grasp code:

Defining two elements in a single expression, in some cases leads to unnecessarily difficult-to-read code. Especially when one of the elements is (explicitly) initialised and the other is not (see below). This problem is emphasised by extra typename keywords (and a lack of syntax colouring, of course).

Unncessary use of post-increment:

It is now well known that for iteration pre-increment usually provides better performance than post-increment. Still, the book uses post-increment throughout all the examples, thereby creating and destroying an unnecessary temporary. A good compiler may be able to optimise away that temporary, still it is not good nor common practice. In fact, in other places avoiding temporaries is explicitly a concern (Section 7.4.1, for example).

Improper use of #include:

The example code makes no difference between
#include
'ing system-headers (
#include
) and #include'ing user headers (
#include "header"
). The examples only use the #include
directive. Of course, this requires that some compiler switch for include directories is set to point to the user directory with those files.

Seriously exception-unsafe code:

For the
HMap
(a fast associative container developed)
operator=
first removes everything from the assigned-to and then constructs a copy from the assigned-from. Obviously, if the construction of the copy throws, the resulting container is unnecessarily destroyed. This is not they way assignment is supposed to be done.

Strange advice:

Section 5.6 makes a strong argument against stl's set operations, such as
set_union
,
set_intersection
and the likes. The argument being that the output iterator passed to these algorithms "must refer to a container that already has enough space. When there is insufficient space, using an insert iterator does not always make sense." (Section 5.6.6). This argument rings hollow to me, though. If I pass an insert iterator for a non-empty container, then I am saying that I want the result of the operation appended to that container. If this is not what I want, I pass an insert iterator for an empty container. I see no problem with this.

Let us look at the initial part of setalgo.h, some of the problems noted above turn up there. The //** comments are mine.

#ifndef SETALGO_H #define SETALGO_H using namespace std; //** 1) Bad practice, don't do //** this 2) Does not make //** sense, nothing from std //** is included template bool Includes(const set_type&s1, const set_type&s2) { // Is s2 contained in s1? 	if(&s1 ==&s2) // save time if the sets are // identical return true; 	typename set_type::iterator i2 = s2.begin(), i1; //** 3) How readable is this? //** 4) must be const_iterator 	while(i2 !=3D s2.end()) { 		i1 =3D s1.find(*i2++); //** 5) unnecessary use of //** postincrement 		if(i1 == s1.end()) // not found 		return false; 	} 	return true; }
I also had problems with the files downloaded fromhttp://www.informatik.hs-bremen.de/~brey/stlbe.html. Opening the project workspace with MSVC++6sp3 opened an empty workspace. Trying to open the project k6.dsp, msvc++ brought up a dialog saying "This makefile was not generated by Developer Studio. Continuing will create a new Developer Studio project to wrap this makefile... Do you want to continue?" In the end, I had to create my own project files, and then files such as
setalgo.h
above were not directly compilable when included into my own files due to the spurious using namespace std;. Well, at least they get the "
int main
" right...

Book cover image courtesy of Open Library.