A question about creating macro (preprocessor) voor GNU C

May 11, 2008
22,599
1,473
126
Hello everybody, i have not fully searched (bit short on time & teeny bit tired) this time and i was hoping for a short answer. I asked several people and nobody seemed to know if it was possible or not. I thought, i would be best of when asking the question here.

This is what i want to have, and i would like to know if it is possible :

Is it possible to have conditional preprocessor statements in a macro define ?
Example :

Code:
[COLOR="Magenta"]#define DebugTextOut_m (text, variable) { \
           #ifdef DEBUG_FUNCTION_IS_YES    \
             FunctionNumber1();                    \
             PrintText(text,variable);              \
             FunctionNumber2();                    \
           #endif                                        \
}[/COLOR]

GNU does not seem to have a problem with it.
However, there is one problem when using this macro, see code...

Code:
[COLOR="Blue"]void[/COLOR] test([COLOR="Blue"]void[/COLOR])
{
DebugTextOut_m([COLOR="Red"]"herp derp %d"[/COLOR],1);

}

And the compiler does not except it because it does not know what kind of variable text is.

These are the errors that i get :
src/usb.c:44: error: 'text' undeclared (first use in this function)
src/usb.c:44: error: (Each undeclared identifier is reported only once
src/usb.c:44: error: for each function it appears in.)
src/usb.c:44: error: 'variable' undeclared (first use in this function)
src/usb.c:44: warning: left-hand operand of comma expression has no effect
src/usb.c:44: error: expected ';' before '{' token
src/usb.c:44: error: stray '#' in program
src/usb.c:44: error: stray '#' in program

Normally i would chew myself into it but i am a bit weary at the moment.
What am i doing wrong ?
Am i expecting something impossible from the preprocessor ?
I really want to avoid the use of variables and just use strings stored in flash memory as text.
For example as this way that is often used :
Code:
PrintText([COLOR="Red"]"herp derp"[/COLOR], data);


The idea is to create debug code into the normal code without using any instructions for the debug code when not used. I would have a general debug.h file :

Code:
[COLOR="YellowGreen"]// Debug.h[/COLOR]

[COLOR="YellowGreen"]// Comment out if not needed.[/COLOR]
[COLOR="Magenta"]#define DEBUG_FUNCTION_IS_YES[/COLOR]

#ifndef _DEBUGMESSENGER
  #define _DEBUGMESSENGER 

[COLOR="Magenta"]#define DebugTextOut_m (text, variable) { \
           #ifdef DEBUG_FUNCTION_IS_YES    \
             FunctionNumber1();                    \
             PrintText(text,variable);              \
             FunctionNumber2();                    \
           #endif                                        \
}[/COLOR]
#endif

This way i would only have to add the debug.h header file, include it and use the macro in all c files. When i uncomment the define DEBUG_FUNCTION_IS_YES all debug messages are enabled.

Of course i would have to add prototypes and more but that is not an issue.

Thank you in advance.
 
Last edited:
May 11, 2008
22,599
1,473
126
Yahooo. i found the solution for the text issue but not the solution for the preprocessor command in the define.

To pass multiple variables in GNU in a define , use : variadic.

This works :

Code:
[COLOR="Magenta"]#define DebugTextOut_m(...) { \
   FunctionNumber1();   \
   Printtext(__VA_ARGS__);    \
   FunctionNumber2(); \
}[/COLOR]

This will work :

Code:
[COLOR="Blue"]void[/COLOR] test([COLOR="Blue"]void[/COLOR])
{
DebugTextOut_m([COLOR="Red"]"herp derp %d"[/COLOR],1);

}




http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
A macro can be declared to accept a variable number of arguments much as a function can. The syntax for defining the macro is similar to that of a function. Here is an example:
#define eprintf(...) fprintf (stderr, __VA_ARGS__)

This kind of macro is called variadic. When the macro is invoked, all the tokens in its argument list after the last named argument (this macro has none), including any commas, become the variable argument. This sequence of tokens replaces the identifier __VA_ARGS__ in the macro body wherever it appears. Thus, we have this expansion:
eprintf ("%s:%d: ", input_file, lineno)
==> fprintf (stderr, "%s:%d: ", input_file, lineno)
 
May 11, 2008
22,599
1,473
126
Sigh... If i do not have CPP 3.2 or higher it will not function. No use of preprocessor directives in a macro i guess...

http://gcc.gnu.org/onlinedocs/cpp/D...uments.html#Directives-Within-Macro-Arguments

3.9 Directives Within Macro Arguments

Occasionally it is convenient to use preprocessor directives within the arguments of a macro. The C and C++ standards declare that behavior in these cases is undefined.

Versions of CPP prior to 3.2 would reject such constructs with an error message. This was the only syntactic difference between normal functions and function-like macros, so it seemed attractive to remove this limitation, and people would often be surprised that they could not use macros in this way. Moreover, sometimes people would use conditional compilation in the argument list to a normal library function like `printf', only to find that after a library upgrade `printf' had changed to be a function-like macro, and their code would no longer compile. So from version 3.2 we changed CPP to successfully process arbitrary directives within macro arguments in exactly the same way as it would have processed the directive were the function-like macro invocation not present.

If, within a macro invocation, that macro is redefined, then the new definition takes effect in time for argument pre-expansion, but the original definition is still used for argument replacement. Here is a pathological example:
#define f(x) x x
f (1
#undef f
#define f 2
f)

which expands to
1 2 1 2

with the semantics described above.


:(

Does anybody have a good alternative code trick for this ?
I really do not want the debug code to be compiled along with my general code when i comment out the debug define...

:(
 

dinkumthinkum

Senior member
Jul 3, 2008
203
0
0
You don't need nested preprocessor statements. There's a couple of options.

Code:
#ifdef ENABLE_DEBUG
#define DEBUGPRINT(fmt,...) printf(fmt, ##__VA_ARGS__)
#else
#define DEBUGPRINT(fmt,...) ;
#endif

Notice the ##__VA_ARGS__ usage: it prevents it from breaking when the list is empty.

Alternatively you can skip using variable arguments like this:
Code:
#ifdef ENABLE_DEBUG
#define DEBUGPRINT(x) printf x
#else
#define DEBUGPRINT(x) ;
#endif
and use it like this: "DEBUGPRINT(("%d", blah));"

Or you can count on your optimizing compiler:
Code:
#define DEBUGPRINT if (ENABLE_DEBUG) printf

If for some reason you decide that you need a block of code in there, always remember to wrap it like so:
Code:
#define FOO do { statement1; statement2; } while (0)

I also recommend throwing the notion of debug levels and module names into there if it is a large project, to help you sort things out.

For example:
Code:
#define MODULE "foo"
#include "debug.h"

debug.h:
Code:
#define DEBUGPRINT(lvl,fmt,...) if (ENABLE_DEBUG && current_level >= lvl) printf(MODULE ": " fmt, ##__VA_ARGS__)

or whatever you like.
 

degibson

Golden Member
Mar 21, 2008
1,389
0
0
If you go with something like this:
Code:
#ifdef ENABLE_DEBUG
#define DEBUGPRINT(x) printf(x)
#else
#define DEBUGPRINT(x) ;
#endif

... then be aware that x's side effects won't happen when not in debug mode.

Alternative:
Code:
#ifdef ENABLE_DEBUG
#define DEBUGPRINT(x) printf(x)
#else
#define DEBUGPRINT(x) x;
#endif
 
May 11, 2008
22,599
1,473
126
Ah, thank you both very much. I am going to think about it and try the different solutions to see what is best for me. I will post what i had in mind if it is what i want and when it works(It will end up on sourceforge anyway inside my ARM ide).


P.S.

I am busy with reading through USB because for the purpose of understanding and being able to use that knowledge in the near future for hobby and / or work. My question is : Does anybody know what ZLP or "zero length packet" means ? For me it means a data packet with no data embedded in it, but that seems far to obvious. It must be wrong. I am sifting through pdf files, books and forums to read about it. But have not found a satisfying answer yet to take away the dreaded uncertainty. :mad:

If i find it, i will post the answer as well.
 

dinkumthinkum

Senior member
Jul 3, 2008
203
0
0
degibson is correct. I would go further and say that you should absolutely never put side-effects in your debug print statements. It will only lead to woes down the line.

ZLP is what it sounds like.
 

degibson

Golden Member
Mar 21, 2008
1,389
0
0
I am busy with reading through USB because for the purpose of understanding and being able to use that knowledge in the near future for hobby and / or work. My question is : Does anybody know what ZLP or "zero length packet" means ? For me it means a data packet with no data embedded in it, but that seems far to obvious. It must be wrong. I am sifting through pdf files, books and forums to read about it. But have not found a satisfying answer yet to take away the dreaded uncertainty. :mad:

I wouldn't worry too much about zero-length packets -- a data packet with a zero-length data payload but a fully-intact control payload. I.e.,
SYNC | PID | CRC16 | EOP

They're not subtle, they're just part of some protocols (the concept is not at all exclusive to USB). Think of them as carrying only control information (like an acknowledgment).

E.g., USB uses a zero-length packet as an explicit acknowledgement of a successful transfer completion. http://www.beyondlogic.org/usbnutshell/usb4.shtml
 
May 11, 2008
22,599
1,473
126
degibson is correct. I would go further and say that you should absolutely never put side-effects in your debug print statements. It will only lead to woes down the line.

ZLP is what it sounds like.




I wouldn't worry too much about zero-length packets -- a data packet with a zero-length data payload but a fully-intact control payload. I.e.,
SYNC | PID | CRC16 | EOP

They're not subtle, they're just part of some protocols (the concept is not at all exclusive to USB). Think of them as carrying only control information (like an acknowledgment).

E.g., USB uses a zero-length packet as an explicit acknowledgement of a successful transfer completion. http://www.beyondlogic.org/usbnutshell/usb4.shtml


Thank you.
I am making a little pdf document that describes enough details to understand USB and how i implemented it for my little ARM MCU in hardware (reference design) and software. The website you mentioned is also stated in the prelude. :)

When it is finished, i can implement it in my bootloader and use and present the functionality in my W-ARM distribution at source forge when i am satisfied. It can also be a good starting point or reference booklet for other people( I expect it to be around 60 pages, ). However, since i am still a moving point on the learning curve... There is still much work to do. I had been reading about USB and i understand the purpose :

For full speed bulk transfers i had written this in my document :



  • For interrupt transactions, Maximum data payload size for full-speed devices is 64 bytes. The buffersize of EP3 is 64 bytes.
  • Data must be sent in multiples of bytes.
  • Data must be sent in byte packets(amount of bytes) equal to the field wMaxPacketSize (64 for EP1 & EP2) in the interface descriptors. The last data packet to transmit or receive will hold the remaining bytes. If the amount of bytes to be transmitted is an integer multiple of wMaxPacketSize, the last data packet will be a so called ZLP or zero length packet. This is a data packet without any data. This way the receiver will know that the last data packet has been received because the last data packet always contains no bytes (ZLP) or an amount of bytes where amount is less then wMaxPacketSize(64).
  • For control transfers, there is the wTotalLength field in the configuration descriptor. There should not be any use of the ZLP here.


There seems to be misunderstanding at the forums i have been sifting through if a ZLP should be used for control transfers. Since the maximum amount of bytes is already known, i do not see any use for a ZLP. However, there can be something that i and others have overlooked, how illogical it may be. Some implementations from operating system providers might have a slightly altered USB protocol for reasons unknown.

For me, it is more that the format confused me. How to implement it. But i am getting there... When i can visualize the usb protocol in hardware and software, i am done. ^_^.

I just want to reduce the time spend in trial & error as much as possible by creating a clear picture for myself.
 
Last edited:
May 11, 2008
22,599
1,473
126
I tested a few options :

#ifdef ENABLE_DEBUG
#define DEBUGPRINT(fmt,...) printf(fmt, ##__VA_ARGS__)
#else
#define DEBUGPRINT(fmt,...) ;
#endif
This one only gives me errors : undefined reference to DEBUG_TEXT_OUT
It fails on the #else part when ENABLE_DEBUG is not defined. The empty define causes the error.

if ENABLE_DEBUG is defined, it will fail on the ## in ##__VA_ARGS__.
error: pasting "(" and ""herp derp %d"" does not give a valid preprocessing token

But i looked around on the internet as well and stuffed some options together :
This does not produce any warnings or errors. However, i am still checking to see if it actually does what i want. It is a combination of usage of __VA_ARGS__ and trusting that the compiler will optimize away the debug code in the do while loop because the loop is always taken once and because the if statement inside the do while loop is always not true.

Code:
[COLOR="SeaGreen"]// ************************************************************************************************
// Debug.h
// Version 0.1 
// 19-11-2011 (dd-mm-yyyy)
// This file provides a macro to put out debug messages.
// By using the macro we make sure that the general c code is clean and readable.
// 
// [/COLOR]

[COLOR="Magenta"]
#ifdef _DEBUGMESSENGER
  #endif

#ifndef _DEBUGMESSENGER
  #define _DEBUGMESSENGER 
[/COLOR]



[COLOR="SeaGreen"]// Include file needed for PrintString function.[/COLOR]
[COLOR="Magenta"]#include "semi_stdio.h"[/COLOR]



[COLOR="SeaGreen"]// Comment out if debug messages are not needed.[/COLOR]
[COLOR="Magenta"]#define DEBUG_FUNCTION_IS_YES[/COLOR]





[COLOR="SeaGreen"]// We need an extra flag.[/COLOR]
[COLOR="Magenta"]#ifdef DEBUG_FUNCTION_IS_YES
#define __WARMDEBUGFLAG__	1
#else
#define __WARMDEBUGFLAG__	0
#endif

[COLOR="SeaGreen"]// __WARMDEBUGFLAG__ is defined to 0 when DEBUG_FUNCTION_IS_YES is not defined. [/COLOR]
#ifdef DEBUG_FUNCTION_IS_YES
#define DEBUG_TEXT_OUT(...) PrintString(__VA_ARGS__);
#else
#define DEBUG_TEXT_OUT(...) {\
	do{ if (__WARMDEBUGFLAG__ == 1) PrintString(__VA_ARGS__); \
	} while (__WARMDEBUGFLAG__ == 1); \
}
#endif						



#endif [/COLOR] [COLOR="SeaGreen"]// End of header.[/COLOR]



P.S.

I forgot to mention that i used of course my own print functions. I did not literally copied the example code you both gave above in the thread.
 
Last edited:
May 11, 2008
22,599
1,473
126
I looked around in the list file and tested the code with different optimization levels:

This works with optimization 0. The code is optimized away when DEBUG_FUNCTION_IS_YES is a comment.
This works with optimization 2. The code is optimized away when DEBUG_FUNCTION_IS_YES is a comment.

I am happy ^_^ ^_^ ^_^
 

dinkumthinkum

Senior member
Jul 3, 2008
203
0
0
Two notes: the #else case isn't empty, it's a semi-colon.

The ,##__VA_ARGS__ construct is only needed when there is a comma coming before the __VA_ARGS__. Its sole purpose is to workaround this case:
Code:
#define DEBUGPRINT(fmt, ...) printf(fmt,__VA_ARGS__)    /* broken */
DEBUGPRINT("Hello World") ==> printf("Hello World",);
Code:
#define DEBUGPRINT(fmt, ...) printf(fmt,##__VA_ARGS__)    /* fixed */
DEBUGPRINT("Hello World") ==> printf("Hello World");
 

degibson

Golden Member
Mar 21, 2008
1,389
0
0
Looks like you've figure out what you want, but I went ahead and dug through my old code anyway.

Code:
#ifdef PROJECT_DEBUG_ENABLED

#define DEBUG( rest... ) { _Project_Debug( DEBUG_DOMAIN, __FILE__, __LINE__, rest ); }

#else

#define DEBUG( msg, rest... ) ;

#endif

... where _Project_Debug had prototype:
Code:
void _Project_Debug(const char* domain, const char* fname, int lineno, const char* msg, ... );
Basically shipped the whole thing to vsprintf and thence to a log file.

The above doesn't use the ##__VA_ARGS__ magic and relies on ... instead. It worked as of gcc 4.whatever.
 
Last edited:
May 11, 2008
22,599
1,473
126
Multiple debug levels :
Having multiple debug levels (module based) is indeed much more handy.

I did so before but my code in a module would be less readable because of all the #ifdef #endif statements and various print functions.

Now i have started to make such specific debug macro's for every module.
And replacing all #ifdef and #endif parts.

The code is cleaning up tremendously.

This time have not colored in the text , it is a bit much.
It would be great that if we choose the
Code:
option for posting inside a message, that the text is parsed and comments are colored automatically green. Preprocessor would be magenta. Keywords blue, etcetera...

Code:
// ************************************************************************************************
// Debug.h
// Version 0.2 
// 20-11-2011 (dd-mm-yyyy)
// This file provides a macro to put out debug messages.
// By using the macro we make sure that the general c code is clean and readable.
// 
// 



#ifdef _DEBUGMESSENGER
	#endif

#ifndef _DEBUGMESSENGER
  #define _DEBUGMESSENGER 




// Include file needed for PrintString function.
#include "semi_stdio.h"



// Comment out if debug messages are not needed.
#define DEBUG_FUNCTION_IS_YES						// General debug messages.
#define USB_DEBUG_FUNCTION_IS_YES				// USB specific debug messages.
#define OS_DEBUG_FUNCTION_IS_YES				// General debug messages.






// *** General Debug text messages.
// We need an extra flag.
#ifdef DEBUG_FUNCTION_IS_YES
#define __WARMDEBUGFLAG__	1
#else
#define __WARMDEBUGFLAG__	0
#endif
// __WARMDEBUGFLAG__ is defined to 0 when DEBUG_FUNCTION_IS_YES is not defined.
#ifdef DEBUG_FUNCTION_IS_YES
#define DEBUG_TEXT(...) PrintString(__VA_ARGS__);
#else
#define DEBUG_TEXT(...) {\
					do{ if (__WARMDEBUGFLAG__ == 1) PrintString(__VA_ARGS__); \
						} while (__WARMDEBUGFLAG__ == 1); \
}
#endif						



// *** USB Debug text messages.
// We need an extra flag.
#ifdef USB_DEBUG_FUNCTION_IS_YES
#define __WARMUSBDEBUGFLAG__	1
#else
#define __WARMUSBDEBUGFLAG__	0
#endif
// __WARMUSBDEBUGFLAG__ is defined to 0 when USB_DEBUG_FUNCTION_IS_YES is not defined.
#ifdef USB_DEBUG_FUNCTION_IS_YES
#define USB_DEBUG_TEXT(...) PrintString(__VA_ARGS__);
#else
#define USB_DEBUG_TEXT(...) {\
					do{ if (__WARMUSBDEBUGFLAG__ == 1) PrintString(__VA_ARGS__); \
						} while (__WARMUSBDEBUGFLAG__ == 1); \
}
#endif						



// *** OS Debug text messages.
// We need an extra flag.
#ifdef OS_DEBUG_FUNCTION_IS_YES
#define __WARMOSDEBUGFLAG__	1
#else
#define __WARMOSDEBUGFLAG__	0
#endif
// __WARMOSDEBUGFLAG__ is defined to 0 when OS_DEBUG_FUNCTION_IS_YES is not defined.
#ifdef OS_DEBUG_FUNCTION_IS_YES
#define OS_DEBUG_TEXT(...) PrintString(__VA_ARGS__);
#else
#define OS_DEBUG_TEXT(...) {\
					do{ if (__WARMOSDEBUGFLAG__ == 1) PrintString(__VA_ARGS__); \
						} while (__WARMOSDEBUGFLAG__ == 1); \
}
#endif						




#endif  // End of header.
 
May 11, 2008
22,599
1,473
126
I am starting to get handy with the preprocessor. Before i did a lot of address calculations myself. Now i just use sizeof and base addresses and let the preprocessor do all calculations. I print out the result to verify but that is it. It is a lot more easy to maintain. And i can continue to use structs to make virtual hardware.