[net.sources.bugs] C-Debug package, possible problems depending on program structure

ksbszabo@wateng.UUCP (Kevin Szabo) (01/21/85)

This bug affects users of Fred Fish's C Debugging Package.

You may note that all of Fred's macros of the DBUG_X variety (x=1,2,3,4..)
have the form:

#define	DBUG_2( keyword, format ) \
	if(_db_on) {_dp_printf_( __LINE__, keyword, format );}


In the very rare case of the following usage, the `else' clause will incorrectly
bind to the `if(_db_on)' provided in the macro.

	if( some_thing_worth_noting_happens )
		DBUG_2( "interesting", "info" );
	else
		do the normal thing;

Admittedly this program structure should really never occur, but it could
surprise someone someday. The fix is to enclose the macro's in another
set of {}.

#define	DBUG_2( keyword, format ) \
	{if(_db_on) {_dp_printf_( __LINE__, keyword, format );};}


I like many of the idea's in Fred's package and I will probably incorporate
them into my own runtime macro's. One thing I would like to know is:
Does anybody have a nice way of handling variable length argument lists
to CPP?  Since my variable names are usually meaningful I would like
a macro that uses the variable names to create the format string. 
Thus I could do the following...

	int	blood, sweat; float tears;
	......
	DBUG( LEVEL, "churchill", "%d,%d,%f", blood, sweat, tears );

Ideally the macro would change all this to

	if( debug & LEVEL ) { if( keyword("churchill) )
				printf(" blood =%d, sweat= %d, tears= %f",....);


	(I use the bit vector LEVEL to select various areas of the package
	 to be elegible for debugging).

	The only thing I have been able to come up with is the following HACK(!)

	DBUG( LEVEL, "keyword", "fmt", ( variable, argument, list ));

#define	DBUG( level, key, fmt, args ) {\
		extern DebugLevel;\
		if( DebugLevel & level ) {\
			DebugStoreStrings( key, fmt,_LINE_, _FILE_,"args" );\
			DebugPassValues args;\
		}\
	}

	Of course DebugPassValues() has to look at the format strings
	and use VARARGS stuff to decode its argument list.

	Does the extra set of parantheses used to fool CPP into thinking that
	there is only one set of arguments really gross anybody out?
	Is it portable at all?  Especially the call to DebugPassValues,
	which requires CPP to retain the parantheses and thus create a valid
	function call?

					Kevin
-- 
Kevin Szabo  watmath!wateng!ksbszabo (U of Waterloo VLSI Group, Waterloo Ont.)

hugh@hcrvx1.UUCP (Hugh Redelmeier) (01/22/85)

In article <1885@wateng.UUCP> ksbszabo@wateng.UUCP (Kevin Szabo) writes:
>You may note that all of Fred's macros ...
>have the form:
>
>#define	DBUG_2( keyword, format ) \
>	if(_db_on) {_dp_printf_( __LINE__, keyword, format );}
>
>In the very rare case of the following usage, the `else' clause will
>incorrectly bind to the `if(_db_on)' provided in the macro.
>
>	if( some_thing_worth_noting_happens )
>		DBUG_2( "interesting", "info" );
>	else
>		do the normal thing;
>
>... The fix is to enclose the macro's in another
>set of {}.
>
>#define	DBUG_2( keyword, format ) \
>	{if(_db_on) {_dp_printf_( __LINE__, keyword, format );};}
>
Whenever I define a macro to be an expression, I put parens around it
to avoid surprises.  Similarly, whenever I define a macro to be a
(compound) statement, I put braces around it.  There are two places
where braces cause problems: IF statements with ELSE clauses and
DO statements:

void proc() { ... }
#define mac()	{ ... }

	if (...) mac(); else ...;  <---- wrong!
	if (...) mac()  else ...;  <---- right
	if (...) proc(); else ...; <---- right
	if (...) proc()  else ...; <---- wrong!

	if (...) { proc_or_mac(); } while (...); <---- best?

The same examples apply to DO.  In fact, it applies even
if the macro definition ends in semicolon, without the braces:
#define mac()	...;

I think this shows how the following three features of C
don't combine perfectly:
- semicolons as terminators (as opposed to separators)
- semicolons DON'T terminate blocks
- statement composition is not done with bracketing
  constructs (except for {})

PS: How often have you forgotten to put the semicolon after
    an initializer contained in braces?
int primes[] = {2, 3, 5, 7, };

PPS: But thank goodness we can use the commas in an initializer
     as terminators (e.g. last comma above).