riceman0
asked on
How to make my debug macros very concise
Hi, I have a debug routine that I call if certain define statements are enabled, for example:
#define TEST_ECHO_OUTPUT 1
void my_main_code()
{
int a;
a =1;
#if TEST_ECHO_OUTPUT
my_debug_out(a);
#endif
}
#if TEST_ECHO_OUTPUT
void my_debug_out(int x)
{
...
}
#endif
This is all well and good, but those calls are ugly, is there a way to tighten up my macro "footprint" to one line, perhaps by defining a multiline macro? For example:
#define TEST_ECHO_OUTPUT 1
#begin_multi_line_define TEST_ECHO_MACRO(x) // made-up, but you see what I'm going for
#if TEST_ECHO_OUTPUT
my_debug_out(#x);
#endif
#end_multi_line_define
void my_main_code()
{
int a;
a =1;
TEST_ECHO_MACRO(a)
}
This would be sweet, I have these test statements all over the place (by requirement) and if I could divide the footprint by 3 it would greatly beautify my code. Note that when my test code is #defined away, I don't even want extra procedure calls (and stack-pushing), even if they don't do anything (which rules out the possibility of #defining out the guts of a real procedure call, I want to #define out the call itself...)
thought I'd ask in case you gurus have any tricks. Thanks!
#define TEST_ECHO_OUTPUT 1
void my_main_code()
{
int a;
a =1;
#if TEST_ECHO_OUTPUT
my_debug_out(a);
#endif
}
#if TEST_ECHO_OUTPUT
void my_debug_out(int x)
{
...
}
#endif
This is all well and good, but those calls are ugly, is there a way to tighten up my macro "footprint" to one line, perhaps by defining a multiline macro? For example:
#define TEST_ECHO_OUTPUT 1
#begin_multi_line_define TEST_ECHO_MACRO(x) // made-up, but you see what I'm going for
#if TEST_ECHO_OUTPUT
my_debug_out(#x);
#endif
#end_multi_line_define
void my_main_code()
{
int a;
a =1;
TEST_ECHO_MACRO(a)
}
This would be sweet, I have these test statements all over the place (by requirement) and if I could divide the footprint by 3 it would greatly beautify my code. Note that when my test code is #defined away, I don't even want extra procedure calls (and stack-pushing), even if they don't do anything (which rules out the possibility of #defining out the guts of a real procedure call, I want to #define out the call itself...)
thought I'd ask in case you gurus have any tricks. Thanks!
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Sorry, what do you mean "expands to nothing" exactly? Literally, blank space?
(Being sure because I will need to defend my decision to superiors.)
>>Sorry, what do you mean "expands to nothing" exactly? Literally, blank
>>space?
Yes, exactly. The preprocessor will just remove the line if TEST_ECHO_OUTPUT is not set.
>>space?
Yes, exactly. The preprocessor will just remove the line if TEST_ECHO_OUTPUT is not set.
ASKER
Kdo: yeah I thought about using a function that does nothing if test mode not #defined (see my last parenthetical), but that is an extra procedure call that I don't want to do.
And for #define MY_DEBUG_OUT(a) {if (TEST_ECHO_OUTPUT) my_debug_out(a);}, I think that's a logical test that is left over if test mode not #defined, isn't it? Don't want that either, my superiors would tell me to live with the three-line calls.
And for #define MY_DEBUG_OUT(a) {if (TEST_ECHO_OUTPUT) my_debug_out(a);}, I think that's a logical test that is left over if test mode not #defined, isn't it? Don't want that either, my superiors would tell me to live with the three-line calls.
Actually
#define MY_DEBUG_OUT(a) {if (TEST_ECHO_OUTPUT) my_debug_out(a);}
will cause a aompiler error if TEST_ECHO_OUTPUT is not set, I am not sure that this is what you want...
#define MY_DEBUG_OUT(a) {if (TEST_ECHO_OUTPUT) my_debug_out(a);}
will cause a aompiler error if TEST_ECHO_OUTPUT is not set, I am not sure that this is what you want...
ASKER
jkr: true, although I was planning on setting TEST_ECHO_OUTPUT to either 1 or 0.
But your comment makes me wonder... a more experienced coworker does it differently:
#define TEST_ECHO_OUTPUT // comments this out to disable
#ifdef TEST_ECHO_OUTPUT
whereas I do this:
#define TEST_ECHO_OUTPUT 1 // change to 0 to disable
#if TEST_ECHO_OUTPUT
Is his approach more traditional? Does it result in tighter code? Is there a disadvantage to my approach, other than the danger of omitting a #define altogether and breaking the code (as you point out)?
(Thanks. Sorry to piggy back this question, but I seem to have expert attention.)
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
>>but why did you use the stringization operator ??
Because it was used in the Q in
my_debug_out(#x);
and would be required for
my_debug_out(a);
I'd also rather not use it.
Because it was used in the Q in
my_debug_out(#x);
and would be required for
my_debug_out(a);
I'd also rather not use it.
ASKER
Do you mean the "#"? My compiler documentation tells me that's how you pass parameters to a macro. If I have that wrong, I will figure that out momentarily...
ASKER
Re-reading the compiler doc, I shouldn't be using the "#". Thanks for the heads-up.
>>My compiler documentation tells me that's how you pass parameters to a
>>macro.
No, not really:
The number-sign or stringizing operator (#) converts macro parameters (after expansion) to string constants. It is used only with macros that take arguments. If it precedes a formal parameter in the macro definition, the actual argument passed by the macro invocation is enclosed in quotation marks and treated as a string literal. The string literal then replaces each occurrence of a combination of the stringizing operator and formal parameter within the macro definition.
White space preceding the first token of the actual argument and following the last token of the actual argument is ignored. Any white space between the tokens in the actual argument is reduced to a single white space in the resulting string literal. Thus, if a comment occurs between two tokens in the actual argument, it is reduced to a single white space. The resulting string literal is automatically concatenated with any adjacent string literals from which it is separated only by white space
>>macro.
No, not really:
The number-sign or stringizing operator (#) converts macro parameters (after expansion) to string constants. It is used only with macros that take arguments. If it precedes a formal parameter in the macro definition, the actual argument passed by the macro invocation is enclosed in quotation marks and treated as a string literal. The string literal then replaces each occurrence of a combination of the stringizing operator and formal parameter within the macro definition.
White space preceding the first token of the actual argument and following the last token of the actual argument is ignored. Any white space between the tokens in the actual argument is reduced to a single white space in the resulting string literal. Thus, if a comment occurs between two tokens in the actual argument, it is reduced to a single white space. The resulting string literal is automatically concatenated with any adjacent string literals from which it is separated only by white space
BTW, then you'll be fine with
#ifdef TEST_ECHO_OUTPUT
#define TEST_ECHO_MACRO(x) my_debug_out(x); // expands to call
#else
#define TEST_ECHO_MACRO(x) // expands to nothing
#endif
TEST_ECHO_MACRO("test output")
>> note that I removed the ; at the end of the macro too
I've known some compilers to get ratty if you put a ; at the end of the usage of the macro , e.g
TEST_ECHO_MACRO(a); // In release this is a noop but leaves a rogue semi-colon behind
The release build is a no-op so you end up with a line that just contains a ; and whilst this shouldn't be an issue I've known some compilers to generate a warning. To get around this I normally use a do/while noop, like below. It'll get optimized away in release build but prevent the potential warning.
I've known some compilers to get ratty if you put a ; at the end of the usage of the macro , e.g
TEST_ECHO_MACRO(a); // In release this is a noop but leaves a rogue semi-colon behind
The release build is a no-op so you end up with a line that just contains a ; and whilst this shouldn't be an issue I've known some compilers to generate a warning. To get around this I normally use a do/while noop, like below. It'll get optimized away in release build but prevent the potential warning.
ifdef TEST_ECHO_OUTPUT
#define TEST_ECHO_MACRO(x) my_debug_out(x) // Will expect a semi-colon
#else
#define TEST_ECHO_MACRO(x) do {} while(false) // Will expect a semi-colon also, but will be optimized away
#endif
>> and would be required for
Not really since my_debug_out takes an int as parameter.
>> BTW, then you'll be fine with
Except for the fact that my_debug_out doesn't take a string as parameter, I still prefer not having a ; at the end of a macro ... It's so counter-intuitive this way :
#define TEST_ECHO_MACRO(x) my_debug_out(x);
int a = 0;
TEST_ECHO_MACRO(a) /* <--- no ; at the end of this line */
some_more_code();
that I prefer "forcing" the user of my macro to add the ;
#define TEST_ECHO_MACRO(x) my_debug_out(x)
int a = 0;
TEST_ECHO_MACRO(a); /* <--- now the ; HAS to be there at the end of this line */
some_more_code();
Not really since my_debug_out takes an int as parameter.
>> BTW, then you'll be fine with
Except for the fact that my_debug_out doesn't take a string as parameter, I still prefer not having a ; at the end of a macro ... It's so counter-intuitive this way :
#define TEST_ECHO_MACRO(x) my_debug_out(x);
int a = 0;
TEST_ECHO_MACRO(a) /* <--- no ; at the end of this line */
some_more_code();
that I prefer "forcing" the user of my macro to add the ;
#define TEST_ECHO_MACRO(x) my_debug_out(x)
int a = 0;
TEST_ECHO_MACRO(a); /* <--- now the ; HAS to be there at the end of this line */
some_more_code();
ASKER
evilrix, good catch with not including the ";" in the macro leaving a rogue semicolon, isn't the solution then just to leave the semicolon in the macro? Why do you need a loop in the no-test-macro?
ASKER
Infinity08, agree it's inconsistent to leave the semi-colon in that macro, but I think preferable to leaving stray semicolons around. Make sense?
>> isn't the solution then just to leave the semicolon in the macro
IMO, no. The way I suggest forces the semi-colon to be added at the end when you use it so it looks more naturally like a function call.
>> I prefer "forcing" the user of my macro to add the ;
Me too, hence my suggestion above :)
IMO, no. The way I suggest forces the semi-colon to be added at the end when you use it so it looks more naturally like a function call.
>> I prefer "forcing" the user of my macro to add the ;
Me too, hence my suggestion above :)
>>good catch with not including the ";" in the macro leaving a rogue semicolon
Well, I saw it, but a semicolon is not 'rogut' at all. It would expand to
void my_main_code()
{
int a;
a =1;
;
}
which is legal C/C++-.
Well, I saw it, but a semicolon is not 'rogut' at all. It would expand to
void my_main_code()
{
int a;
a =1;
;
}
which is legal C/C++-.
>> which is legal C/C++-.
Agreed, I never said it wasn't... it's just that some (mainly older) compilers will issue a warning if you build with high warning levels. It also forces the caller to provide it so it makes the code more consistent (IMO). I'm not saying do it, I'm just pointing it out as a consideration.
Agreed, I never said it wasn't... it's just that some (mainly older) compilers will issue a warning if you build with high warning levels. It also forces the caller to provide it so it makes the code more consistent (IMO). I'm not saying do it, I'm just pointing it out as a consideration.
>> I've known some compilers to generate a warning.
Ah, never seen that ... The C standard explicitly allows empty statements :
(6.8.3) expression-statement:
expression(opt) ;
(the opt meaning optional of course)
What the C standard doesn't allow however, is a { } block followed by a ;
To avoid that, you DO need macro's like these if they involve { } blocks :
#define SOME_BLOCK_MACRO do { \
int i = 0; \
fun(i); \
} while(0)
Ah, never seen that ... The C standard explicitly allows empty statements :
(6.8.3) expression-statement:
expression(opt) ;
(the opt meaning optional of course)
What the C standard doesn't allow however, is a { } block followed by a ;
To avoid that, you DO need macro's like these if they involve { } blocks :
#define SOME_BLOCK_MACRO do { \
int i = 0; \
fun(i); \
} while(0)
ASKER
evilrx and Infinity08:
Interesting perspective on the need for consistency of semicolon use in main code, I see your point. But should be no technical problems with leaving the semicolon in the macro, right? Just requires extra care by the reviewer?
I would have trouble convincing people of the evilrx loop approach, and I don't want a stray semicolon, so that might be my best route.....
Interesting perspective on the need for consistency of semicolon use in main code, I see your point. But should be no technical problems with leaving the semicolon in the macro, right? Just requires extra care by the reviewer?
I would have trouble convincing people of the evilrx loop approach, and I don't want a stray semicolon, so that might be my best route.....
>> But should be no technical problems with leaving the semicolon in the macro, right?
Technically, it's ok, yes. I just think not having it in the macro is more consistent.
>> and I don't want a stray semicolon
Don't worry about a stray semicolon (empty statement) ... It's no problem at all. It's perfectly legal.
The loop approach evilrix showed was just to avoid warnings on certain old compilers.
Technically, it's ok, yes. I just think not having it in the macro is more consistent.
>> and I don't want a stray semicolon
Don't worry about a stray semicolon (empty statement) ... It's no problem at all. It's perfectly legal.
The loop approach evilrix showed was just to avoid warnings on certain old compilers.
ASKER
jkr, your solution (including ; in macro) does *not* leave rogue semicolon, correct? The way I saw it, evilrx's comment encouraged me back to your original suggestion.
I have seen warnings with rogue semicolons too, so I'm prejudiced against them.
I have seen warnings with rogue semicolons too, so I'm prejudiced against them.
ASKER
By the way, really appreciate all the thoughts...
>> jkr, your solution (including ; in macro) does *not* leave rogue semicolon, correct?
Unless you actually use the macro like this :
TEST_ECHO_MACRO(a);
>> I have seen warnings with rogue semicolons too, so I'm prejudiced against them.
Odd, I've never seen them for empty statements. Oh well ;)
Unless you actually use the macro like this :
TEST_ECHO_MACRO(a);
>> I have seen warnings with rogue semicolons too, so I'm prejudiced against them.
Odd, I've never seen them for empty statements. Oh well ;)
Just one more thought about semi-colons and macros ti support I8 and my assertion. Consider the really contrived code below. See how the behavior changes if trace is on compared to when it's off? Enforcing consistent semi-colon termination for all functions and macros (and I do mean force and not rely on other programmers to follow a convention) will offer some protection against this kind of hard to track down bug.
#include <stdio.h>
#define DO_TRACE // Comment me to change behavior
#ifdef DO_TRACE
#define TRACE(x) printf("trace: %d\n", x);
#else
#define TRACE(x)
#endif
void foo()
{
printf ("Part2\n");
}
int main()
{
int n = 0;
if (n < 10)
printf ("Part1\n");
else
TRACE(n) // When DO_TRACE is disabled the semi-colon goes with it so foo() becomes part of this if/else
foo();
return 0;
}
>> >> I've known some compilers to generate a warning.
>> Ah, never seen that ... The C standard explicitly allows empty statements :
evil's right .. i'm currently working with a nintendo-ds compiler .. and we get warnings for those *lonesome* semicolons .. since we have set option "warning to errors" this became an issue ..
however, you're right jkr .. its legal anyway ..
ike
>> Ah, never seen that ... The C standard explicitly allows empty statements :
evil's right .. i'm currently working with a nintendo-ds compiler .. and we get warnings for those *lonesome* semicolons .. since we have set option "warning to errors" this became an issue ..
however, you're right jkr .. its legal anyway ..
ike
ASKER
One last follow-on, then I'll close this out. Given the defines
#ifdef TEST_ENABLE_OUTPUT
#define TEST_OUTPUT(x) print_test_message(x); // expands to call; note semicolon is in macro
#else
#define TEST_OUTPUT(x) // expands to nothing
#endif
and the calls
int aa; // = 45;
aa = 45;
print_test_message("Hello1
TEST_OUTPUT("Hello2 %i,%i\r", aa, aa)
why would the outputs be:
?Hello1 45, 45
Hello2 7387, 2552
crazy stuff. My function is below, just puts the characters out UART0. Don't want to make this another draw on your time, just wanted to know any initial thoughts, then I'll start a new question if it gets involved...
char s_buf [100];
void print_test_message( flash char *format, ...)
{
va_list ap;
va_start(ap,format);
vsprintf(s_buf, format, ap);
va_end(ap);
putstr0(s_buf);
}
ASKER
Okay got some help from
http://en.wikipedia.org/wiki/Variadic_macro
This has some improvement, but still need to figure out that "?"
#ifdef TEST_ENABLE_OUTPUT
#define TEST_OUTPUT(...) print_test_message( __VA_ARGS__);
#else
#define TEST_OUTPUT(x) // expands to nothing
#endif
You're passing more parameters to the macro than it expects.
>> but still need to figure out that "?"
Can you show the complete code ?
Can you show the complete code ?
ASKER
Didn't I give you all the working parts?
Turns out the question mark follows the first call, so it seems like that's some left over crap in the buffer or something. I'll need to talk to the guys who wrote that function...
Thanks a ton, guys, for all the help.
Are you using gcc? If not, variable arguments to a macro aren't possible, they aren't part of the standard.
ASKER
This is the CodeVision compiler for Atmel microprocessors. Apparently it accepts the variable-list macros, do you see any danger with using it?
>>Apparently it accepts the variable-list macros, do you see any danger with
>>using it?
If it works, that's fine, yet it won't be portable - that's the downside...
>>using it?
If it works, that's fine, yet it won't be portable - that's the downside...
>> Turns out the question mark follows the first call, so it seems like that's some left over crap in the buffer or something.
That's why I was asking to see the complete code ;) To find where the question mark comes from.
That's why I was asking to see the complete code ;) To find where the question mark comes from.
ASKER
Oh, I would never inflict all that on you. That;s a 5,000 point question. Will throw that question to a coworker, at least I have the macro working so I'm happy.
Not sure if I am covering old ground here but there is a good reason for the dangling semi.
I think the code below should demonstrate.
Paul
I think the code below should demonstrate.
Paul
#ifdef DO_TRACE
#define TRACE1(x) printf("trace: %d\n", x)
#else
#define TRACE1(x)
#endif
#ifdef DO_TRACE
#define TRACE2(x) printf("trace: %d\n", x);
#else
#define TRACE2(x) ;
#endif
// Check the code below with trace both on and off.
if ( a ) TRACE1(x)
else TRACE1(y)
if ( a ) TRACE1(x);
else TRACE1(y);
if ( a ) TRACE2(x)
else TRACE2(y)
if ( a ) TRACE2(x);
else TRACE2(y);
>> Not sure if I am covering old ground here but there is a good reason for the dangling semi
You mean, like this http:#20844363 ?
You mean, like this http:#20844363 ?
Forgive me ER. You already covered that one.
Paul
Paul
It's been a while, Paul. What have you been up to ?
It has indeed. :)
Not sure I'm back yet but I do have a browse in 'C' most days. You guys hold the fort so well :)
I've been working on a private project but that's coming to an end soon. My next is a technical one in C so I hope to be visiting often.
Paul
Not sure I'm back yet but I do have a browse in 'C' most days. You guys hold the fort so well :)
I've been working on a private project but that's coming to an end soon. My next is a technical one in C so I hope to be visiting often.
Paul
Great to hear from you. The C zone is not the same without you ;)
The macros involved are a bit cumbersome. Perhaps better is to call the function and let it do an immediate return if debugging is disabled.
Another solution is to encapsulate the function call
#define MY_DEBUG_OUT(a) {if (TEST_ECHO_OUTPUT) my_debug_out(a);}
Good Luck,
Kent