Reputation: 5257
I have this macro in my C code:
#define ASSERT(ret, num) \ // make sure ret === num
if (ret != num) { \
fprintf(stderr, "Error [%d] at line [%d] in function [%s]. Date: [%s] Time: [%s]\n", \
ret, __LINE__, __FUNCTION__, __DATE__, __TIME__); \
exit(ret); \
}
Then i call it like so (all arguments are integers):
ASSERT(errNo, MPI_SUCCESS);
ASSERT(aliveNeighbors, 8);
ASSERT(newGrid, !NULL);
I get errors like (GCC v5.4):
expected identifier or ‘(’ before ‘if’
if (ret != num) { \
error: stray ‘\’ in program
ASSERT(errNo, MPI_SUCCESS);
error: stray ‘\’ in program
ASSERT(aliveNeighbors, 8);
stray ‘\’ in program
ASSERT(newGrid, !NULL);
What is wrong here?
Upvotes: 0
Views: 619
Reputation: 753695
Given the macro definition:
#define ASSERT(ret, num) \ // make sure ret === num
if (ret != num) { \
fprintf(stderr, "Error [%d] at line [%d] in function [%s]. Date: [%s] Time: [%s]\n", \
ret, __LINE__, __FUNCTION__, __DATE__, __TIME__); \
exit(ret); \
}
you have a number of problems, many of them visible in your error messages.
The backslash-newline line splicing occurs in phase 2 of the processing defined in the standard (ISO/IEC 9899:2011) at §5.1.1.2 Translation phases:
Physical source file multibyte characters are mapped, in an implementation-defined manner, to the source character set (introducing new-line characters for end-of-line indicators) if necessary. Trigraph sequences are replaced by corresponding single-character internal representations.
Each instance of a backslash character (
\
) immediately followed by a new-line character is deleted, splicing physical source lines to form logical source lines. Only the last backslash on any physical source line shall be eligible for being part of such a splice. A source file that is not empty shall end in a new-line character, which shall not be immediately preceded by a backslash character before any such splicing takes place.The source file is decomposed into preprocessing tokens7) and sequences of white-space characters (including comments). A source file shall not end in a partial preprocessing token or in a partial comment. Each comment is replaced by one space character. New-line characters are retained. Whether each nonempty sequence of white-space characters other than new-line is retained or replaced by one space character is implementation-defined.
Preprocessing directives are executed, macro invocations are expanded, and
_Pragma
unary operator expressions are executed. If a character sequence that matches the syntax of a universal character name is produced by token concatenation (6.10.3.3), the behavior is undefined. A#include
preprocessing directive causes the named header or source file to be processed from phase 1 through phase 4, recursively. All preprocessing directives are then deleted.
(Phases 5-8 complete the processing but are not germane to this discussion.)
Note that comments are removed in phase 3, and the preprocessing proper occurs in phase 4, on tokenized input.
The first line of your macro definition is:
#define ASSERT(ret, num) \ // make sure ret === num
This defines the body of the macro as \
. You cannot place comments after the backslash that is to continue a macro, and you cannot use // … <eol>
style comments in the body of a macro. If you added a backslash after the comment, it would continue the comment onto the next line, and the macro body would still be empty. You can use /* … */
comments with care:
#define ASSERT(ret, num) /* make sure ret === num */ \
This would work correctly. Because the original macro has the trailing // … <eol>
comment, that completes the macro. The first error message is:
expected identifier or ‘(’ before ‘if’
if (ret != num) { \
This is because the if
line in the 'macro definition' is actually not a part of the macro, and the if
is not expected in the context where it appears.
The other three errors are essentially the same:
error: stray ‘\’ in program
ASSERT(errNo, MPI_SUCCESS);
The macro expanded to just a backslash, but you can't have stray backslashes in the text of a valid C program, hence the error message. When backslashes appear, they're always in stylized contexts with specific other characters following (digits, certain letters, certain punctuation symbols, newline).
You also have issues with the body of the macro:
__DATE__
and __TIME__
are the date and time when the code was compiled (preprocessed), not the date and time when the code was run. They're seldom useful, and not useful in this context. If you want date and time information for when the program is run, you'll define and call a function which determines and formats the current date/time.__FUNCTION__
is not standard; __func__
is the standard predefined identifier.As written, the macro cannot be safely used in some contexts, such as:
if (alpha == omega)
ASSERT(retcode, 0);
else if (gamma == delta)
gastronomic_delight(retcode, gamma, alpha);
The else
clause is incorrectly associated with the if
in the (semi-corrected) ASSERT
and not with the alpha == omega
test.
Assembling these changes into a workable solution:
#define ASSERT(ret, num) \
do { \
if ((ret) != (num)) err_report(ret, __LINE__, __func__); \
} while (0)
where you have:
extern _Noreturn void err_report(int err, int line, const char *func);
This function formats the message and reports it on standard error, determining time and date if appropriate. It also exits (hence the _Noreturn
attribute — a feature of C11. You might prefer to #include <stdnoreturn.h>
and use noreturn
instead of _Noreturn
. You might or might not decide to add __FILE__
as a value passed to the function; it would need another parameter.
Note, too, that the invocation:
ASSERT(newGrid, !NULL);
is suspect. It translates to if ((newGrid) == (!0))
which is not what you intended. You have to work a bit harder to test for not-null pointers:
ASSERT(newGrid != NULL, 1);
This compares the value in newGrid
with NULL
, generating 1
if the value is non-null, and 0
if it is null, and that is compared with the 1
argument. Note that the error number for this test would not be helpful — it would be 0
.
If I've missed any crucial points in the comments, please let me know.
Upvotes: 3