10. C Preprocessor

comp.lang.c FAQ list · Question 10.1

Q: I'm trying to define a few simple little function-like macros such as

	#define square(x) x * x
but they're not always working.


A: There are three important rules to remember when defining function-like macros:

  1. The macro expansion must always be parenthesized to protect any lower-precedence operators from the surrounding expression. Given the (incorrect) square() macro above, the invocation
    	1 / square(n)
    
    would expand to
    	1 / n * n
    
    (which evaluates as (1 / n) * n), while what you want is
    	1 / (n * n)
    
    (In this case, the problem is one of associativity rather than precedence, but the effect is the same.)
  2. Within the macro definition, all occurrences of the parameters must be parenthesized to protect any low-precedence operators in the actual arguments from the rest of the macro expansion. Again given the square() macro above, the invocation
    	square(n + 1)
    
    would expand to
    	n + 1 * n + 1
    
    But what you want is
    	(n + 1) * (n + 1)
    
  3. If a parameter appears several times in the expansion, the macro may not work properly if the actual argument is an expression with side effects. Yet again given the square() macro above, the invocation
    	square(i++)
    
    would expand to
    	i++ * i++
    
    which is undefined (see question 3.2).

The proper definition of a square macro, to comply with rules 1 and 2 above, is

	#define square(x) ((x) * (x))
Complying with rule 3 is harder. Sometimes, careful exploitation of the short-circuiting behavior of the &&, ||, or ?: operators (see question 3.6) can arrange that a parameter which appears several times is guaranteed to be evaluated exactly once. Sometimes, the macro is just documented as being unsafe, and callers must remember not to use it on arguments with side effects. Other times, it may be advisable not to compose a function-like macro if it can't be made safe.

(As a stylistic convention, macros are often defined with capitalized or all-upper-case names, to make it obvious that they are macros. It may be acceptable to define a function-like macro with an all-lower-case name, if it truly simulates a function, but only if it complies with all three rules above. Since the squaring macro we've been discussing does not, it should be defined as something like

	#define Square(x) ((x) * (x))	/* UNSAFE */
if it is to be used at all.)

References: K&R1 Sec. 4.11 p. 87
K&R2 Sec. 4.11.2 p. 90
H&S Secs. 3.3.6,3.3.7 pp. 49-50
CT&P Sec. 6.2 pp. 78-80




comp.lang.c FAQ list · Question 10.2

Q: Here are some cute preprocessor macros:

	#define begin	{
	#define end	}
With these, I can write C code that looks more like Pascal. What do y'all think?


A: Use of macros like these, though perhaps superficially attractive, is generally discouraged; in severe cases the practice is called ``preprocessor abuse.''

There is little to be gained in trying to redefine the syntax of a language to fit your own predilections, or to match some other language. Your predilections are unlikely to be shared by later readers or maintainers of the code, and any simulation of another language is most unlikely to be perfect (so any alleged convenience or utility will probably be outweighed by the nuisance of remembering the imperfections).

As a general rule, it's a good idea if the use of preprocessor macros follows the syntax of the C language. Macros without arguments should look like variables or other identifiers; macros with arguments should look like function calls. Ask yourself: ``If I somehow presented this code to the compiler without running it through the preprocessor, how many syntax errors would I get?'' (Of course, you'd get plenty of undefined symbols and non-constant array dimensions, but those aren't syntax errors.) This rule means that C code, plus macro invocations, still looks like C code. So-called nonsyntactic macros like begin and end or CTRL(D) (see question 10.21) can make C look like gobbledygook (see also question 20.36).

This is of course largely a style issue; see also section 17.




comp.lang.c FAQ list · Question 10.3

Q: How can I write a generic macro to swap two values?


A: There is no good answer to this question. If the values are integers, a well-known trick using exclusive-OR could perhaps be used, but it will not work for floating-point values or pointers, or if the two values are the same variable. (See questions 3.3b and 20.15c.) If the macro is intended to be used on values of arbitrary type (the usual goal), any solution involving a temporary variable is problematical, because:

The best all-around solution is probably to forget about using a macro, unless you're willing to pass in the type as a third argument. (Also, if you're trying to swap entire structures or arrays, you probably want to exchange pointers instead.) If you're worried about the use of an ugly temporary, and know that your machine provides an efficient exchange instruction, convince your compiler vendor to recognize the standard three-assignment swap idiom in the optimization phase.

If you're consumed by a passionate desire to solve this problem once and for all, please reconsider; there are better problems worthier of your energies.

Additional links: so you think you have a solution




comp.lang.c FAQ list · Question 10.4

Q: What's the best way to write a multi-statement macro?


A: The usual goal is to be able to invoke the macro as if it were an expression statement consisting of a function call:

	MACRO(arg1, arg2);
This means that the ``caller'' will be supplying the final semicolon, so the macro body should not. The macro body cannot therefore be a simple brace-enclosed compound statement, because of the possibility that the macro could be used as the if branch of an if/else statement with an explicit else clause:
	if(cond)
		MACRO(arg1, arg2);
	else	/* some other code */
If the macro expanded to a simple compound statement, the final, caller-supplied semicolon would be a syntax error:
	if(cond)
		{stmt1; stmt2;};
	else	/* some other code */

The traditional solution, therefore, is to use

	#define MACRO(arg1, arg2) do {	\
		/* declarations */	\
		stmt1;			\
		stmt2;			\
		/* ... */		\
		} while(0)	/* (no trailing ; ) */
When the caller appends a semicolon, this expansion becomes a single statement regardless of context. (An optimizing compiler will remove any ``dead'' tests or branches on the constant condition 0, although lint may complain.)

(Another possibility might be

	#define MACRO(arg1, arg2) if(1) { \
		stmt1; \
		stmt2; \
		} else
but it is inferior, since it quietly breaks the surrounding code if the caller happens to forget to append the semicolon upon invocation.)

If all of the statements in the intended macro are simple expressions, with no declarations or loops, another technique is to write a single, parenthesized expression using one or more comma operators:

	#define FUNC(arg1, arg2) (expr1, expr2, expr3)
(For an example, see the first DEBUG() macro in question 10.26.) This technique also allows a value (in this case, expr3) to be ``returned.''

Some compilers, e.g. gcc, are also able to expand compact functions in-line, either automatically or at the programmer's request (perhaps with a nonstandard ``inline'' keyword or other extension).

References: H&S Sec. 3.3.2 p. 45
CT&P Sec. 6.3 pp. 82-3




comp.lang.c FAQ list · Question 10.5

Q: What's the difference between using a typedef or a #define for a user-defined type?


A: See question 1.13.




comp.lang.c FAQ list · Question 10.5b

Q: What's the difference between

	const MAXSIZE = 100;
and
	#define MAXSIZE 100


A: A preprocessor #define gives you a true compile-time constant. In C, const gives you a run-time object which you're not supposed to try to modify; ``const'' really means ``readonly''. See also the question 11.8. (But in C++, const is closer to #define.)

Additional links: longer explanation




comp.lang.c FAQ list · Question 10.6

Q: I'm splitting up a program into multiple source files for the first time, and I'm wondering what to put in .c files and what to put in .h files. (What does ``.h'' mean, anyway?)


A: As a general rule, you should put these things in header (.h) files:

macro definitions (preprocessor #defines)
structure, union, and enumeration declarations
typedef declarations
external function declarations (see also question 1.11)
global variable declarations

It's especially important to put a declaration or definition in a header file when it will be shared between several other files. Don't repeat a declaration or macro definition at the top of two or more source files; do put it in a header file and #include it wherever needed. The reason is not just to save typing: you want to set things up so that whenever a declaration or definition changes, you only have to change it in one place, with the update propagated to all source files consistently. (In particular, never put external function prototypes in .c files. See also question 1.7.)

On the other hand, when a definition or declaration should remain private to one .c file, it's fine to leave it there. (Private file-scope functions and variables should also be declared static. See also question 2.4.)

Finally, you should not put actual code (i.e. function bodies) or global variable definitions (that is, defining or initializing instances) in header files. Also, when you are building a project out of multiple source files, you should compile each of them separately (using a compiler option to request compilation only), and use the linker to link the resultant object files together. (In an integrated development environment, all of this may be taken care of for you.) Don't try to ``link'' all of your source files together with #include; the #include directive should be used to pull in header files, not other .c files.

See also questions 1.7, 10.7, and 17.2.

Additional links: more explanation

References: K&R2 Sec. 4.5 pp. 81-2
H&S Sec. 9.2.3 p. 267
CT&P Sec. 4.6 pp. 66-7




comp.lang.c FAQ list · Question 10.7

Q: Is it acceptable for one header file to #include another?


A: It's a question of style, and thus receives considerable debate. Many people believe that ``nested #include files'' are to be avoided: the prestigious Indian Hill Style Guide (see question 17.9) disparages them; they can make it harder to find relevant definitions; they can lead to multiple-definition errors if a file is #included twice; they can lead to increased compilation time; and they make manual Makefile maintenance very difficult. On the other hand, they make it possible to use header files in a modular way (a header file can #include what it needs itself, rather than requiring each #includer to do so); a tool like grep (or a tags file) makes it easy to find definitions no matter where they are; a popular trick along the lines of:

	#ifndef HFILENAME_USED
	#define HFILENAME_USED
	...header file contents...
	#endif
(where a different bracketing macro name is used for each header file) makes a header file ``idempotent'' so that it can safely be #included multiple times; a clever compiler can avoid expending any more time on later instances of an already-included header; and automated Makefile maintenance tools (which are a virtual necessity in large projects anyway; see question 18.1) handle dependency generation in the face of nested #include files easily. See also question 17.10.

References: Rationale Sec. 4.1.2




comp.lang.c FAQ list · Question 10.8a

Q: What's the difference between #include <> and #include "" ?


A: The <> syntax is typically used with Standard or system-supplied headers, while "" is typically used for a program's own header files. See also question 10.8b.

Additional links:

nice explanation by Kaz Kylheku

An article and a longer followup I posted in 1997 exploring some of the finer points which sometimes arise in deciding between #include ""and #include <>. (Warning: among the possibilities explored is that of using #include <> for your own header files under certain circumstances, a practice which is not standard and is not condoned by the readership of comp.lang.c.)




comp.lang.c FAQ list · Question 10.8b

Q: What are the complete rules for header file searching?


A: The exact behavior is implementation-defined (which means that it is supposed to be documented; see question 11.33). Typically, headers named with <> syntax are searched for in one or more standard places. [footnote] Header files named with "" syntax are first searched for in the ``current directory,'' then (if not found) in the same standard places. (This last rule, that "" files are additionally searched for as if they were <> files, is the only rule specified by the Standard.)

Another distinction is the definition of ``current directory'' for "" files. Traditionally (especially under Unix compilers), the current directory is taken to be the directory containing the file containing the #include directive. Under other compilers, however, the current directory is the directory in which the compiler was initially invoked. (Compilers running on systems without directories or without the notion of a current directory may of course use still different rules.)

It is also common for there to be a way (usually a command line option involving capital I, or maybe an environment variable) to add additional directories to the list of standard places to search. Check your compiler documentation.

Additional links:

further elaboration by Dennis Ritchie

A long article of mine exploring some of the bore subtle implications of the search rules

References: K&R2 Sec. A12.4 p. 231
ISO Sec. 6.8.2
H&S Sec. 3.4 p. 55




comp.lang.c FAQ list · Question 10.9

Q: I'm getting strange syntax errors on the very first declaration in a file, but it looks fine.


A: Perhaps there's a missing semicolon at the end of the last declaration in the last header file you're #including. See also questions 2.18, 11.29a, 16.1, and 16.1b.




comp.lang.c FAQ list · Question 10.10

Q: I'm using header files which accompany two different third-party libraries, and they are ``helpfully'' defining common macros such as TRUE, FALSE, Min(), and Max(), but the definitions clash with each other and with definitions I'd already established in my own header files. What can I do?


A: This is indeed an annoying situation. It's a classic namespace problem; see questions 1.9 and 1.29. Ideally, third-party vendors would be conscientious when defining symbols (both preprocessor #defines and global variable and function names) to assure that namespace collisions were unlikely. The best solution is to get the vendor(s) to fix their header files.

As a workaround, you can sometimes undefine or redefine the offending macros between the conflicting #include directives.




comp.lang.c FAQ list · Question 10.10b

Q: I'm #including the right header file for the library function I'm using, but the linker keeps saying it's undefined.


A: See question 13.25.




comp.lang.c FAQ list · Question 10.11

Q: I'm compiling a program, and I seem to be missing one of the header files it requires. Can someone send me a copy?


A: There are several situations, depending on what sort of header file it is that's ``missing''.

If the missing header file is truly a standard one (that is, one defined by the ANSI C Standard, such as <stdio.h>), there's a problem with your compiler. Either the compiler wasn't installed properly, or your project is somehow not configured to find the standard header files. You'll need to contact your vendor, or someone knowledgeable about your particular compiler, for help.

In the case of nonstandard headers, the situation is considerably more complicated. Some headers (such as <dos.h>) are completely system- or compiler-specific. Some are completely unnecessary, and should be replaced by their Standard equivalents. (For example, instead of the nonstandard <malloc.h> and <memory.h>, portable source code should instead #include <stdlib.h> and <string.h>, respectively.) Other headers, such as those associated with popular add-on libraries, may be reasonably portable.

If the missing header file is an OS-specific one, such as <sgtty.h>, <sys/stat.h>, <netinet/in.h>, or <dos.h>, it may be that the program you're compiling was tailored to an operating system other than the one you're using. (It's also possible that some conditional compilation settings need to be adjusted.) It likely won't be possible to get the program working without rewriting its system-dependent portions. Simply getting copies of the missing header files wouldn't help--they typically contain declarations of the interfaces to the various system calls and libraries. Obtaining copies of the header files wouldn't get you copies of the libraries, and the libraries wouldn't be of any use (that is, they wouldn't work) without the underlying system calls, anyway. See also questions 19.1, 19.4, 19.12b, and 19.40c.

If the missing header file is for some external, add-on, third-party library, look for the header where you got the library. If you have some source code which #includes what appears to be an add-on header, but which you don't have, you probably don't have the library, either, and you'll need both. See question 13.25; see also question 18.16.

If the header file is unique to (that is, part of) the program you're trying to compile, your search will obviously begin at the spot where you found the program. (Again, see question 18.16.)

In general, however, asking if someone can ``send you a copy'' of a missing header file is not likely to be productive. Standard headers exist in part so that definitions appropriate to your compiler, operating system, and processor can be supplied. You cannot just pick up a copy of someone else's header file and expect it to work, unless that person is using exactly the same environment. Nonstandard headers--such as those specific to a particular operating system or third-party library--aren't generally any more portable; the OS-specific ones are likely to be very specific to a particular version and release of the OS, and the third-party ones are likely to be equally tied to a particular version of the library.

The bottom line is that a random person on the net is unlikely to be able to send you a working copy of the header file you (seem to) need. You may actually have a portability problem (see section 19), or a compiler problem (in which case you might be able to ask your compiler vendor why the file was not provided, or to send a replacement copy). Otherwise (if the header is third-party or application-specific), see question 18.16.




comp.lang.c FAQ list · Question 10.12

Q: How can I construct preprocessor #if expressions which compare strings?


A: You can't do it directly; preprocessor #if arithmetic uses only integers. An alternative is to #define several macros with symbolic names and distinct integer values, and implement conditionals on those:

	#define RED	1
	#define BLUE	2
	#define GREEN	3

	#if COLOR == RED
	/* red case */
	#else
	#if COLOR == BLUE
	/* blue case */
	#else
	#if COLOR == GREEN
	/* green case */
	#else
	/* default case */
	#endif
	#endif
	#endif
(Standard C specifies a new #elif directive which makes if/else chains like these a bit cleaner.)

See also question 20.17.

References: K&R2 Sec. 4.11.3 p. 91
ISO Sec. 6.8.1
H&S Sec. 7.11.1 p. 225




comp.lang.c FAQ list · Question 10.13

Q: Does the sizeof operator work in preprocessor #if directives?


A: No. Preprocessing happens during an earlier phase of compilation, before type names have been parsed. Instead of sizeof, consider using the predefined constants in ANSI's <limits.h>, if applicable, or perhaps a ``configure'' script. (Better yet, try to write code which is inherently insensitive to type sizes; see also questions 1.1 and 1.3.)

Additional links: nice explanation by Chris Torek

References: ISO Sec. 5.1.1.2, Sec. 6.8.1
H&S Sec. 7.11.1 p. 225




comp.lang.c FAQ list · Question 10.14

Q: Can I use an #ifdef in a #define line, to define something two different ways, like this?

	#define a b \
	#ifdef whatever
		c d
	#else
		e f g
	#endif


A: No. You can't ``run the preprocessor on itself,'' so to speak. What you can do is use one of two completely separate #define lines, depending on the #ifdef setting:

	#ifdef whatever
	#define a b c d
	#else
	#define a b e f g
	#endif

References: ISO Sec. 6.8.3, Sec. 6.8.3.4
H&S Sec. 3.2 pp. 40-1




comp.lang.c FAQ list · Question 10.15

Q: Is there anything like an #ifdef for typedefs?


A: Unfortunately, no. (There can't be, because types and typedefs haven't been parsed at preprocessing time.) You may have to keep sets of preprocessor macros (e.g. MY_TYPE_DEFINED) recording whether certain typedefs have been declared.

See also questions 1.13 and 10.13.

References: ISO Sec. 5.1.1.2, Sec. 6.8.1
H&S Sec. 7.11.1 p. 225




comp.lang.c FAQ list · Question 10.16

Q: How can I use a preprocessor #if expression to tell whether a machine's byte order is big-endian or little-endian?


A: You probably can't. The usual techniques for detecting endianness involve pointers or arrays of char, or maybe unions, but preprocessor arithmetic uses only long integers, and there is no concept of addressing. Another tempting possibility is something like

	#if 'ABCD' == 0x41424344
but this isn't reliable, either. At any rate, the integer formats used in preprocessor #if expressions are not necessarily the same as those that will be used at run time.

Are you sure you need to know the machine's endianness explicitly? Usually it's better to write code which doesn't care (see for example the code fragment in question 12.42). See also question 20.9.

References: ISO Sec. 6.8.1
H&S Sec. 7.11.1 p. 225




comp.lang.c FAQ list · Question 10.17

Q: I'm getting strange syntax errors inside lines I've #ifdeffed out.


A: See question 11.19.




comp.lang.c FAQ list · Question 10.18

Q: I inherited some code which contains far too many #ifdef's for my taste. How can I preprocess the code to leave only one conditional compilation set, without running it through the preprocessor and expanding all of the #include's and #define's as well?


A: There are programs floating around called unifdef, rmifdef, and scpp (``selective C preprocessor'') which do exactly this. See question 18.16.




comp.lang.c FAQ list · Question 10.19

Q: How can I list all of the predefined identifiers?


A: There's no standard way, although it is a common need. gcc provides a -dM option which works with -E, and other compilers may provide something similar. If the compiler documentation is unhelpful, the most expedient way is probably to extract printable strings from the compiler or preprocessor executable with something like the Unix strings utility. Beware that many traditional system-specific predefined identifiers (e.g. ``unix'') are non-Standard (because they clash with the user's namespace) and are being removed or renamed. (In any case, as a general rule, it's considered wise to keep conditional compilation to a minimum.)

Additional links: sources!




comp.lang.c FAQ list · Question 10.20

Q: I have some old code that tries to construct identifiers with a macro like

#define Paste(a, b) a/**/b
but it doesn't work any more.


A: It was an undocumented feature of some early preprocessor implementations (notably Reiser's) that comments disappeared entirely and could therefore be used for token pasting. ANSI affirms (as did K&R1) that comments are replaced with white space, so they cannot portably be used in a Paste() macro. However, since the need for pasting tokens was demonstrated and real, ANSI introduced a well-defined token-pasting operator, ##, which can be used like this:

	#define Paste(a, b) a##b

Here is one other method you could try for pasting tokens under a pre-ANSI compiler:

	#define	XPaste(s)	s
	#define	Paste(a, b)	XPaste(a)b
See also question 11.17.

References: ISO Sec. 6.8.3.3
Rationale Sec. 3.8.3.3
H&S Sec. 3.3.9 p. 52




comp.lang.c FAQ list · Question 10.21

Q: I have an old macro

#define CTRL(c) ('c' & 037)
that doesn't seem to work any more.


A: The intended use of this macro is in code like

	tchars.t_eofc = CTRL(D);
which is expected to expand to
	tchars.t_eofc = ('D' & 037);
based on the assumption that the actual value of the parameter c will be substituted even inside the single quotes of a character constant. Preprocessing was never supposed to work this way; it was somewhat of an accident that a CTRL() macro like this ever worked.

ANSI C defines a new ``stringizing'' operator (see question 11.17), but there is no corresponding ``charizing'' operator.

The best solution to the problem is probably to move the single quotes from the definition to the invocation, by rewriting the macro as

	#define CTRL(c) ((c) & 037)
and invoking it as
	CTRL('D')
(Doing so also makes the macro ``syntactic''; see question 10.2.)

It may also be possible to use the stringizing operator and some indirection:

	#define CTRL(c) (*#c & 037)
or
	#define CTRL(c) (#c[0] & 037)
but neither of these would work as well as the original, since they wouldn't be valid in case labels or as global variable initializers. (Global variable initializers and case labels require various flavors of constant expressions, and string literals and indirection aren't allowed.)

See also question 11.18.

References: ISO Sec. 6.8.3
H&S Secs. 7.11.2,7.11.3 pp. 226-7




comp.lang.c FAQ list · Question 10.22

Q: Why is the macro

	#define TRACE(n) printf("TRACE: %d\n", n)
giving me the warning ``macro replacement within a string literal''? It seems to be expanding
	TRACE(count);
as
	printf("TRACE: %d\count", count);


A: See question 11.18.




comp.lang.c FAQ list · Question 10.23

Q: How can I use a macro argument inside a string literal in the macro expansion?


A: See question 11.18.




comp.lang.c FAQ list · Question 10.24

Q: I'm trying to use the ANSI ``stringizing'' preprocessing operator `#' to insert the value of a symbolic constant into a message, but it keeps stringizing the macro's name rather than its value.


A: See question 11.17.




comp.lang.c FAQ list · Question 10.25

Q: I've got this tricky preprocessing I want to do and I can't figure out a way to do it.


A: C's preprocessor is not intended as a general-purpose tool. (Note also that it is not guaranteed to be available as a separate program.) Rather than forcing it to do something inappropriate, you might want to write your own little special-purpose preprocessing tool, instead. You can easily get a utility like make(1) to run it for you automatically.

If you are trying to preprocess something other than C, consider using a general-purpose preprocessor. (One older one available on most Unix systems is m4.)




comp.lang.c FAQ list · Question 10.26

Q: How can I write a macro which takes a variable number of arguments, or use the preprocessor to ``turn off'' a function call with a variable number of arguments?


A: One popular trick is to define and invoke the macro with a single, parenthesized ``argument'' which in the macro expansion becomes the entire argument list, parentheses and all, for a function such as printf:

	#define DEBUG(args) (printf("DEBUG: "), printf args)

	if(n != 0) DEBUG(("n is %d\n", n));
The obvious disadvantage is that the caller must always remember to use the extra parentheses. Another problem is that the macro expansion cannot insert any additional arguments (that is, DEBUG() couldn't expand to something like fprintf(debugfd, ...)).

gcc has an extension which allows a function-like macro to accept a variable number of arguments, but it's not standard. Other possible solutions are:

(These all require care on the part of the user, and all of them are rather ugly.)

C99 introduces formal support for function-like macros with variable-length argument lists. The notation ... can appear at the end of the macro ``prototype'' (just as it does for varargs functions), and the pseudomacro __VA_ARGS__ in the macro definition is replaced by the variable arguments during invocation.

Finally, you can always use a bona-fide function, which can take a variable number of arguments in a well-defined way. See questions 15.4 and 15.5. (If you needed a macro replacement, try using a function plus a non-function-like macro, e.g. #define printf myprintf .)

When you want to turn the debugging printouts off, you can either use a different version of your debug macro:

	#define DEBUG(args) /* empty */
or, if you're using real function calls, use still more preprocessor tricks to remove the function name but not the arguments, such as
	#define DEBUG (void)
or
#define DEBUG if(1) {} else printf or
#define DEBUG 1 ? 0 : (void)
(These tricks are predicated on the assumption that a good optimizer will remove any ``dead'' printf calls or degenerate cast-to-void parenthesized comma expressions.) See also question 10.14.

Additional links: more ideas

References: C9X Sec. 6.8.3, Sec. 6.8.3.1




comp.lang.c FAQ list · Question 10.27

Q: How can I include expansions of the __FILE__ and __LINE__ macros in a general-purpose debugging macro?


A: This question tends to reduce to question 10.26. One solution involves writing your debug macro in terms of a varargs function (see questions 15.4 and 15.5), and an auxiliary function which stashes the values of __FILE__ and __LINE__ away in static variables, as in:

#include <stdio.h>
#include <stdarg.h>

void debug(const char *, ...);
void dbginfo(int, const char *);
#define DEBUG dbginfo(__LINE__, __FILE__), debug

static const char *dbgfile;
static int dbgline;

void dbginfo(int line, const char *file)
{
	dbgfile = file;
	dbgline = line;
}

void debug(const char *fmt, ...)
{
	va_list argp;
	fprintf(stderr, "DEBUG: \"%s\", line %d: ", dbgfile, dbgline);
	va_start(argp, fmt);
	vfprintf(stderr, fmt, argp);
	va_end(argp);
	fprintf(stderr, "\n");
}
With this machinery in place, a call to
	DEBUG("i is %d", i);
expands to
	dbginfo(__LINE__, __FILE__), debug("i is %d", i);
and prints something like
	DEBUG: "x.c", line 10: i is 42

A cunning improvement is the idea of having the stashing function return a pointer to the bona-fide varargs function:

void debug(const char *, ...);
void (*dbginfo(int, const char *))(const char *, ...);
#define DEBUG (*dbginfo(__LINE__, __FILE__))

void (*dbginfo(int line, const char *file))(const char *, ...)
{
	dbgfile = file;
	dbgline = line;
	return debug;
}
With these definitions,
	DEBUG("i is %d", i);
gets expanded to
	(*dbginfo(__LINE__, __FILE__))("i is %d", i);

Another, perhaps easier way might simply be to

	#define DEBUG printf("DEBUG: \"%s\", line %d: ", \
		__FILE__,__LINE__),printf
Now,
	DEBUG("i is %d", i);
simply expands to
	printf("DEBUG: \"%s\", line %d: ",
		__FILE__,__LINE__),printf("i is %d", i);

Finally, you may be able to use the

	#define _ ,
trick from question 10.26.

Additional links: another idea





Read sequentially: prev next up



about this FAQ list   about eskimo   search   feedback   copyright

Hosted by Eskimo North