Path: utzoo!utgpu!watmath!clyde!att!westmark!mole-end!mat From: mat@mole-end.UUCP (Mark A Terribile) Newsgroups: comp.lang.c++ Subject: Re: Improved switch statement (was Re: goodbye cpp ???) Summary: Bright-eyed and bushy-coded. Also, trivial cases Message-ID: <125@mole-end.UUCP> Date: 19 Dec 88 00:43:33 GMT References: <6590072@hplsla.HP.COM> <1106@etive.ed.ac.uk> <33528@bbn.COM> <574@redsox.UUCP> Organization: mole-end--private system. admin: mole-end!newtnews Lines: 110 > I think [the BLISS switch statement] is much clearer and neater and far > less prone to error during maintenance than the current alternative: > > bool flag1, flag2, flag3; > if (flag1) > then > // stuff > else > if (flag2) > then > // stuff > else > if (flag3) > then Indeed, but it's not clearer than the linearized form preferred by Kernighan and Plaugher: if( ... ) ... else if( ... ) ... else if( ... ) ... else if( ... ) ... else ... I prefer to go a step further and break the else-if across two lines. I'm not afraid of white space and I like to be able to see the shapes of the program text and know what's happening without having to read the individual statements. Oh, and I use an editor that makes it easy to slide up and down a couple of lines rather than whole pages. One very useful programming technique I call ``reduction by trivial case.'' Consider a function that must operate on a linked list; its job is to ensure that the current node is preceeded by a ``marker node,'' creating it if necessary and in any case returning a pointer to the marker node. If we do this without allowing early return and without ordering our cases, we get: if( nodep ) { if( node_p->previous ) { if( node_p->previous->type == MARKER ) result = node_p->previous; else { ins_node( node_p, new_marker() ); result = node_p->previous; } } else { node_p->previous = new_marker(); result = node_p->previous; } } else mark_p = 0; return mark_p; Among this fragment's various faults is that the compiler will have to check very carefully to ensure that ``result'' is set in every path through. It's not impossible, and not even ``difficult'' but it needlessly forces the to rely on a sophisticated checking mechanism where a simple one would do. Omnidijkstratization doesn't always simplify things. This really ought to be a member function, but member functions shouldn't be in the business of checking that their >this< pointers are null. Assuming though that is *is* a seperate function, we write it as below. By using early returns, we reduce the checking that the compiler has to do from ``used before set/multipath'' to ``return .vs. return e'' . It's easier for the compiler to check, and easier for the reader to check as well. node* marknode( node* node_p ) { if( ! node_p ) // Note that this test and return removes the return 0; // need for a level of nesting. It also // introduces an assumption that can safely // be made by subsequent code--the pointer is // non-null and should be presumed valid. // By removing this trivial case, we have // changed the problem and exposed the next // trivial case. if( ! node_p->previous ) return ( node_p->previous = new_marker() ); // We've stripped off another troublesome but // trivial case, and changed the problem to // expose the next trivial case. if( node_p->previous->type != MARKER ) insnode( node_p, new_marker() ); // We've now reduced the two remaining cases // to one, introducing the safe assumption // that the previous instruction is a marker // without needlessly introducing extra // marker noded. return node_p->previous; } -- (This man's opinions are his own.) From mole-end Mark Terribile