Dizio@udel.edu (03/12/87)
I found this macro for assert in <assert.h> under ULTRIX 1.2
under the SYSTEM_FIVE #ifdef
#define assert(EX) if (EX) ; else _assert("EX", __FILE__, __LINE__)
^culprit
It fails when given given an expression containing '"'.
eg. assert ((fp = fopen("filename","r")) != NULL)
My question is this. Is this a proper implementation of this
functionality. I'm not sure why it wants to pass the EX to
_assert except perhaps to print the expression out, but in reality
the line number and file name is all I ever need to know.
This is especially true since assert is mainly a debugging tool
and not anything I would put into release software. Mainly
because one generally would want more control over the action
to take, and the message printed to the user.
The non-system define is
#define _assert(ex) \
{if (!(ex)) {\
fprintf(stderr,"Assertion failed: file %s, line %d\n",\
__FILE__, __LINE__);\
exit(1);\
}}
and of coarse if has no problem with any valid expression.
I realize this is a minor point as I could simply write my own macro,
but I just wondered if the proposed standard has specified whether
any valid expression should be able to be used inside the assert,
Or only expressions which don't contain '"'.
Dave
gwyn@brl-smoke.ARPA (Doug Gwyn ) (03/14/87)
In article <4907@brl-adm.ARPA> Dizio@udel.edu writes: >#define assert(EX) if (EX) ; else _assert("EX", __FILE__, __LINE__) > ^culprit >It fails when given given an expression containing '"'. > eg. assert ((fp = fopen("filename","r")) != NULL) > >My question is this. Is this a proper implementation of this >functionality. I'm not sure why it wants to pass the EX to >_assert except perhaps to print the expression out, but in reality >the line number and file name is all I ever need to know. The problem is that there is no way in K&R C to construct a string literal with a macro argument embedded in it. When Reiser implemented his preprocessor (used by every version of UNIX that I have seen so far), he supported this so that macros such as assert() would work (except for cases such as the one you stumbled across). The CTRL() macro one often encounters uses a similar trick to construct a character constant. X3J11 recognized the need for some such facility, but could not bring themselves to bless this Reiserism, which conflicts with much existing practice on non-UNIX systems. They therefore introduced a "stringize" operator (but not a "charize" one, alas). I don't recall if the Draft Standard points out the need to escape " characters in the macro argument when stringizing, but I think it does (I left my copy at home).
drw@cullvax.UUCP (Dale Worley) (03/19/87)
Dizio@udel.edu writes: > I found this macro for assert in <assert.h> under ULTRIX 1.2 > under the SYSTEM_FIVE #ifdef > > #define assert(EX) if (EX) ; else _assert("EX", __FILE__, __LINE__) > ^culprit > > It fails when given given an expression containing '"'. > eg. assert ((fp = fopen("filename","r")) != NULL) > > I realize this is a minor point as I could simply write my own macro, > but I just wondered if the proposed standard has specified whether > any valid expression should be able to be used inside the assert, > Or only expressions which don't contain '"'. The standard requires that assert() work for any expression. But, if you want to write your own assert(), you can do things like #define assert(EX) ( (EX) ? _assert(#EX, __FILE__, __LINE__) ANSI C won't let you substitute parameters inside quoted strings, but it will let you convert a parameter into a string literal via #. # is also required to handle things containing "s correctly. E.g., #define stringify(EX) #EX stringify(printf("Foo\n")) yields "printf(\"Foo\\n\")" Dale -- Dale Worley Cullinet Software UUCP: ...!seismo!harvard!mit-eddie!cullvax!drw ARPA: cullvax!drw@eddie.mit.edu Un*x (a generic name for a class of OS's) != Unix (AT&T's brand of such)
u096000@lanl.gov (Roger A. Cole) (04/12/90)
This article presents some information about using assert() in developing C software. This information is based in part on responses from comp.software-eng (see end of article for credits). ** The perspective presented below is one of improving code quality, not of program proving. ** assert(expr) is a macro which checks the 'expr'. If 'expr' is false, an error message is printed and 'evasive action' (often just exit(1); ) is taken. The assert() macro typically is disabled if the preprocessor symbol NDEBUG is defined. Guidelines for using assert() include: 1. assert()'s should be enabled only in development code, and never in production versions. 2. Do not use assert() for checking user input or other 'unpredictable' data. Use other checking mechanisms for these types of conditions. 3. Use assert() for checking conditions which, if violated, would result in program malfunctions. Valuable aspects of using assert() include: 1. assert() provides a compact method for checking important conditions at run time. 2. assert()'s are not present in production code, so they don't affect size and speed of production code. 3. Since the assert()'s aren't actually removed from the source code, they a. provide a method for trouble-shooting when a problem occurs; and b. provide 'documentation' within the code of important conditions. Weaknesses of using assert() include: 1. Some conditions are difficult to check. For example, the desirable check for a pointer argument would be "it's a valid pointer"; generally, a subset of this condition is checked--"it's not a NULL pointer". 2. assert() operates at run time, and thus has limited capability for doing compile time checks. 3. assert() can't be used to actually validate most algorithms--just conditions during the execution of an algorithm. ****************************************************************************** A simple example. Assume 'pStruct' points to a structure containing several items, with items tied in some way to a number. Based on a user-supplied item number, obtain in 'pItem' a pointer to the corresponding item. If the user supplies an invalid item number, keep retrying until a valid item number is obtained; if an invalid item number is supplied to the look-up routine, abort. (This example is focused on using assert(), not on "proper" C--no flames please :-) .) while (menuItem < 0 && menuItem > maxItem) { code to get a menu item number from a user; if (menuItem < 0 || menuItem > maxItem) /* don't use assert() here */ printf("illegal item number\n" } pItem = lookUp(pStruct, menuItem); ITEM * lookUp(pStruct, itemNum) STRUCT *pStruct; int itemNum; { ITEM *pItem; assert(pStruct != NULL); assert(itemNum >= 0); assert(itemNum <= maxItem); code to get address of desired item; assert(pItem != NULL); return pItem; } ****************************************************************************** My goals for implementing assert(): 1. The default method of 'make'ing the code for production must disable the checks. 2. Even during development, minimize the impact on program size of using assert(). 3. Allow easily setting breakpoints to catch assertion failures. 4. Impose as few restrictions as possible on the use of assert(). With these goals in mind, I have settled on the following form for assert(), which I'm using with the Sun3 C compiler. There is a xxx.h part and a xxx.c part. #ifdef DEBUG /*---------------------------------------------------------------------------- * assert() * * DESCRIPTION * assert() evaulates an expression. If the expression is non-zero * (i.e., "true"), then no action is taken. If the expression is zero, * then the file name and line number are printed to * stderr and an exit(1); is done. If a #define DEBUG hasn't been * done, then assert() does nothing. * * EXAMPLES * assert(pBuf != NULL); * assert(strlen(name) < 20), printf("%s\n", name); * * NOTES * 1. this uses a C 'trick'--if 'expr' is TRUE, then C doesn't execute what * follows the || . * 2. this macro definition comes from "C Traps and Pitfalls", Andrew * Koenig, Addison-Wesley, 1989, page 82. *---------------------------------------------------------------------------*/ # define assert(expr) \ ((void)((expr) || assertFail(__FILE__, __LINE__))) #else # define assert(expr) #endif assertFail(fileName, lineNum) char *fileName; int lineNum; { (void)fprintf(stderr, "assertFail: in file %s line%d\n", fileName, lineNum); exit(1); } ****************************************************************************** Some discussion: One suggestion was to print the expression which failed. Although this can be done in a relatively portable way, for either 'old-timey' or ANSI C pre-processors, a potential pitfall made me decide not to implement this: If 'expr' contains a "string", then macro expansion could get confusing. My normal development and testing scheme uses -DDEBUG, so I generate checks only if that symbol is defined. I prefer this to the common strategy of having using -DNDEBUG to disable generating checks. Many other uses exist for using assert(). Some possible conditions are: "Milestones" during a complicated algorithm elapsed time for an algorithm doesn't violate a time constraint ****************************************************************************** My thanks to the following for their helpful input: bobtl%toolbox.wv.tek.com@RELAY.CS.NET D. Richard Hipp <drh@cs.duke.edu> Peter Montgomery <pmontgom@math.ucla.edu> john@jupiter.nmt.edu (John Shipman) madd@world.std.com (jim frost) joshua@Atherton.COM (Flame Bait) cox@stpstn.UUCP (Brad Cox) ****************************************************************************** Roger Cole, Los Alamos National Laboratory Internet: cole@luke.lanl.gov
mcdaniel@amara.uucp (Tim McDaniel) (04/13/90)
u096000@lanl.gov (Roger A. Cole) wrote a good article about assert(). assert() doesn't get enough respect: it can be a BIG lifesaver, and a significant help in documentation! (As Roger pointed out.) In the article, he gave a definition #ifdef DEBUG # define assert(expr) ((void)((expr) || assertFail(__FILE__, __LINE__))) #else # define assert(expr) #endif However, one of the examples given was assert(strlen(name) < 20), printf("%s\n", name); If DEBUG were not defined, this would expand to , printf("%s\n", name); which is not syntactically valid. Making the second choice be # define assert(expr) ((void) 0) would suffice. (Personally, I prefer to print the expression that failed, which could help if I wrote two assertions on one line. I also like to give a constant string as an explanation argument, for non-obvious checks: assert(frobozz(queue) != xyzzy(reg), "pipeline lost a datum"); If, instead, you put an explanatory comment there, the effect would be more-or-less identical, but requiring a second argument forces the programmer to provide an explanation. Personal style.) -- Tim McDaniel Applied Dynamics International, Ann Arbor, MI Internet: mcdaniel%amara.uucp@mailgw.cc.umich.edu UUCP: {uunet,sharkey}!amara!mcdaniel