[net.lang.c] How about a predefined #FILE, #PATH and #FUNCTION for C?

grace@yale.ARPA (Joseph Grace) (01/27/86)

To explain what I mean...
definition by example:

    #PATH ==> /usr/fred/hello.c
    #FILE ==> hello.c
    #FUNCTION ==> main

    (N.B.: no quotation marks.)

2 problems that these predefined macros would help (me) with:
     
1)
    When debugging, I always like to include in my error messages, the
    name of the file where the error message originated.  However, when
    I rename files with more meaningful names, or simply copy a function
    from one file to another, the old filename still shows up in the
    error (or other) messages.  #FILE would put a stop to this.

2)
    #FILE could also help in writing a header file, e.g., error.h, that
    has standard error messages that will print the filename of the file
    that the message printed from.

Here is an example where these predefined macros would be useful:
(#FILE ==> FILE since compiler complains "illegal macro name" with
 #FILE.)

    error.h:
	#define TEST printf ("FILE: error has occurred\n");

    test.c:
	#define  FILE test1
	#include "test.h"

	main()
	{
	    TEST;

	}

    output of test (on BSD 4.2 Pyramid):
	FILE: error has occurred
	/* not "test: error has occurred" as I would prefer */

#FILE, #PATH and #FUNCTION would solve these problems.

If someone knows a way around these problems, I would appreciate hearing
about it.
Otherwise, maybe these would be good additions to the C standard...

Sincerely,
Joseph R. Grace
...decvax!yale!grace

p.s. #PATH is nice for files that reside in many directories.
     #FUNCTION is nice for precision.

grace@yale.ARPA (Joseph Grace) (01/27/86)

Summary:
Expires:
Sender:
Followup-To:
Distribution:
Keywords:

Sorry, I have one addition to make to my proposal:

	    #PATH ==> /usr/fred/test.c
	    #FILE ==> test.c
    *new*   #BASE ==> test          /* Use this macro in function main of
				     * previous message (instead of #FILE).
				     */
	    #FUNCTION ==> main

and maybe these would be better in CAPS to be consistent with typical
UNIX error messages.

Joe Grace
...decvax!yale!grace

tom@puff.wisc.edu (Thomas Scott Christiansen) (01/27/86)

On most compilers with a CPP, you have the symbols __FILE__ and
__LINE__ defined.  Consider the following error macro:

#define ERROR(function, argument, excuse)                               \
    {                                                                   \
        fprintf(stderr,"Unexpected ERROR at line %d of file \"%s\"\n",  \
            __LINE__, __FILE__ );                                       \
        fprintf(stderr,"function: %s() died (%s) from \"%s\"\n",        \
            function, argument, excuse);                                \
    }

tom

ark@alice.UucP (Andrew Koenig) (01/27/86)

> To explain what I mean...
> definition by example:
> 
>     #PATH ==> /usr/fred/hello.c
>     #FILE ==> hello.c
>     #FUNCTION ==> main
> 
>     (N.B.: no quotation marks.)
> 

Most versions of the C preprocessor have (undocumented) pre-defined
macros called __FILE__ and __LINE__ that do close to what you want.
See /usr/include/assert.h for an example of usage.

augart@h-sc1.UUCP (Steven Augart) (01/28/86)

I cannot claim to speak about other versions of unix, but in Berkeley
4.2 and 4.3, there are two macros automatically defined for you by the
c preprocessor: __FILE__ and __LINE__.

Here's how I use them:
char * foo;
char * malloc();
unsigned room;
[ code .... ]
if ((foo = malloc(room)) == (char *) NULL)
{
    fprintf(stderr, "Out of memory on line %d of file %s!\n", __LINE__, 
			__FILE__);
    exit(1);
}

Could someone on a non-4.2/4.3 BSD machine tell us if this facility is
part of other preprocessors as well?  Thanks.

Steven Augart
swa@borax.lcs.mit.edu
(617) 498-6352

guy@sun.uucp (Guy Harris) (01/28/86)

> I cannot claim to speak about other versions of unix, but in Berkeley
> 4.2 and 4.3, there are two macros automatically defined for you by the
> c preprocessor: __FILE__ and __LINE__. ...
>
> Could someone on a non-4.2/4.3 BSD machine tell us if this facility is
> part of other preprocessors as well?  Thanks.

This facility was added to the C preprocessor by John Reiser at Bell Labs;
it first appeared outside the Labs in V7.  It is, therefore, in V7 and all
post-V7 UNIX systems except those where the supplier decided to "improve" it
by supplying a C compiler which doesn't support it - let's hope there aren't
any such systems.  It's even documented in the System V "cpp" manual page,
along with the "defined()" operator.  I believe that both __FILE__ and
__LINE__, as well as "defined()", are in the ANSI C draft standard.
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy%gorodish@sun.arpa	(yes, really)

jsdy@hadron.UUCP (Joseph S. D. Yao) (01/28/86)

In article <312@yale.ARPA> grace@yale.ARPA (Joseph Grace) writes:
>    error.h:
>	#define TEST printf ("FILE: error has occurred\n");
>...
>    output of test (on BSD 4.2 Pyramid):
>	FILE: error has occurred
>	/* not "test: error has occurred" as I would prefer */
>#FILE, #PATH and #FUNCTION would solve these problems.

First, let's drop the '#', otherwise we'll have to re-define our
concept of "identifier".  If you want uniqueness, let's go for
__FILE__ (which, along with __LINE__ does exist in many C
implementations, including 4.2BSD).

Second, for better compatibility, let's define TEST to be:
	printf("%s: ...\n", __FILE__);
Indeed, this won't work otherwise.

Now, for other things, you can sometimes use your SCCS and RCS
keywords.  Because you mentioned that this is a software project
under ongoing development, I assume you are using SCCS or RCS,
unless this is an undergrad programming project and your teachers
have not showed you SCCS or RCS (and shame on them if so!!!).
I routinely include something like:

#ifndef	lint
# ifdef	SCCS
    static char SCCS_id[] = "%W%";
#  else
    static char RCS_id[] =
	"@(#)$Header:$";
# endif	/*SCCS*/
#endif/*lint*/

ifndef lint so that lint won't complain.  SCCS is defined or not
in <local.h>.  "@(#)" prefix to $Header:$ because RCS doesn't do
it and I want what(1) to recognise the header.  Now, the second
word in each of these is the module name.  In SCCS, you can have
a separate string containing just "%M%" that is the module name.
In RCS, "$Source:$" will be the full path name (also in $Header:$
instead of module name in older RCS's).

BTW, <local.h> is what we're using to include bsd_4, bsd_4_2,
s5, s5r2, s5r2v2, SCCS, RCS, vax_785, m68020, and whatever other
configuration-dependent defines aren't already in the compiler
(like vax, m68k) or other files like <sys/param.h>.

I routinely want to use the command name (rather than the module
name) in my fprintf(stderr,...)'s.  This is especially true when
one command is linked with several names.  I do this:

#define DIRC	'/'
#ifdef	s5
#  define	rindex	strrchr
#  define	index	strchr
#endif/*s5*/
char *myname;
main(argc, argv, envp)
  int argc;
  char **argv;
  char **envp;
{
	extern char *rindex();
	if (argc > 0) {
		myname = rindex(*argv, DIRC);
		if (myname == (char *) NULL)
			myname = *argv;
		  else
			++myname;
	}
	...
}

'myname' is available to the whole program.

I have occasionally wanted to know at all times what function I
was in.  Here is one way:

#ifdef	EBUG
#  define	FUNBEG(fname)	\
			char *last_fnname;\
			extern char *fnname;\
			last_fnname = fnname;\
			fnname = "FUNCTION fname()";
#  define	FUNEND	fnname = last_fnname;
#  define	FUNCHG(fname,newstr)	\
			fnname = "IN FUNCTION fname(): newstr";
# else
#  define	FUNBEG(fname)
#  define	FUNEND
#  define	FUNCHG(fname,newstr)
#endif/*EBUG*/

Then inside a function:
func(a, b, c)
  int a, b, c;
{
	<decls>

	FUNBEG(func)	/* This must be right after decls! */
	...
	FUNCHG(func, this has just happened)
	...
	FUNEND		/* Before any explicit or implicit return */
}

Now, when you compile this -DEBUG you will always have a string
that reflects where you are (you  d i d  put this in main(), too,
didn't you?); while if you drop -DEBUG you get more space!  Note
that this must be re-written for ANSI C:
			fnname = "FUNCTION " fname "()";
			fnname = "IN FUNCTION " fname "(): " newstr;
	FUNBEG("func")	/* This must be right after decls! */
	FUNCHG("func", "this has just happened")

[In some ways, this is preferable.  The old way, you could get:
#define f(s)	printf("%s:" s)
	f(cp); -> printf("%cp", cp);]
-- 

	Joe Yao		hadron!jsdy@seismo.{CSS.GOV,ARPA,UUCP}

bright@dataioDataio.UUCP (Walter Bright) (01/31/86)

In article <887@h-sc1.UUCP> augart@h-sc1.UUCP (Steven Augart) writes:
>Could someone on a non-4.2/4.3 BSD machine tell us if this facility is
>part of other preprocessors as well?  Thanks.

__LINE__ and __FILE__ are supported by the Datalight C compiler for the
IBM PC. In fact, they are supported by most C compilers. They are
primarilly useful to support the following macro:

#define assert(e)	((e) || assert_fail("e",__LINE__,__FILE__))

assert_fail(expression,line,file)
char *expression,*file;
int line;
{
	fprintf(stderr,"expression '%s' failed at line %d in file '%s'\n",
		expression,line,file);
	exit(1);
}

The assert macro generates code that guarantees that expression e evaluates
to true, or else tells you where it failed. Sprinkling your code with
assert()s in the right places can greatly facilitate debugging (especially
on the IBM PC which has no memory protection).
Note the "e" in the macro definition. This won't work on all compilers,
you will have to use a variant that your compiler supports.