FreeRTOS_debug_printf double quotes

friesen wrote on Tuesday, June 05, 2018:

What is the purpose of wrapping all the FreeRTOS_debug_printf params in double quotes in freertos+tcp?

This makes it rather awkward to port to an existing output.

In other words, everything is like FreeRTOS_debug_printf((“Hello %i\r\n”,123)); which looks like one param, at least to xc32

rtel wrote on Tuesday, June 05, 2018:

It is because it is a macro rather than a function, and having the
double quotes allows a variable number of parameters to be passed as a
single parameter.

heinbali01 wrote on Wednesday, June 06, 2018:

FreeRTOS_debug_printf double quotes

Not double quotes but double parentheses. This way of logging is not uncommon in public libraries.

In every FreeRTOSIPConfig.h you will see this code that should explain it:

extern int lUDPLoggingPrintf( const char *pcFormatString, ... );

/* Set to 1 to print out debug messages.  If ipconfigHAS_DEBUG_PRINTF
is set to 1 then FreeRTOS_debug_printf should be defined to the
function used to print out the debugging messages. */

#ifndef ipconfigHAS_DEBUG_PRINTF
    #define ipconfigHAS_DEBUG_PRINTF    1
#endif

#if( ipconfigHAS_DEBUG_PRINTF == 1 )
    #define FreeRTOS_debug_printf(X)    lUDPLoggingPrintf X
#else
    /* Define it as an empty statement so that a semicolon can be placed safely after it. */
    #define FreeRTOS_debug_printf(X)    do {} while( 0 )
#endif

/* Set to 1 to print out non debugging messages, for example the
output of the FreeRTOS_netstat() command, and ping replies.  If ipconfigHAS_PRINTF is set to 1 then FreeRTOS_printf should be
set to the function used to print out the messages. */

#ifndef ipconfigHAS_PRINTF
    #define ipconfigHAS_PRINTF            1
#endif

#if( ipconfigHAS_PRINTF == 1 )
    #define FreeRTOS_printf(X)            lUDPLoggingPrintf X
#else
    #define FreeRTOS_printf(X)            do {} while( 0 )
#endif

The FreeRTOS_debug_printf() is quite detailed, whereas FreeRTOS_printf() will only log essential events and problems. Once you release your device, you may want to disable all logging.

The GNU compiler has ellipses in its macro syntax, so we could wwrite:

#define FreeRTOS_printf( format, ... )

but many other compilers get stuck on the three dots.

We could also demand that a user defines a function:

void FreeRTOS_printf( const char *pcFormat, ... )

and use single parenthesis.

You will get used to the double parentheses:

FreeRTOS_printf( ( "Hello world\n" ) );

davidbrown wrote on Wednesday, June 06, 2018:

Hi,

Note that it is not gcc that has the variadic macro support - it is C99.
Any C99 compiler will support it (though gcc has a couple of neat
extensions to make it better). But FreeRTOS, AFAIU, is C90 compatible
and thus does not use any C99 features.

(Personally, I’d like to see C90 support dropped in future versions of
FreeRTOS - compilers and C programmers have had about 20 years to catch
up, and have no excuse for not supporting C99. C99 would bring a lot of
benefits to FreeRTOS, such as in this case. Of course, changing to a
better syntax here would mean backwards incompatible changes - these
things are never pain-free.)

An alternative way to handle this sort of thing would have been to write:

#if( ipconfigHAS_DEBUG_PRINTF == 1 )
#define FreeRTOS_debug_printf lUDPLoggingPrintf
#else
/* Define it as an empty statement so that a semicolon can be placed
safely after it. */
#define FreeRTOS_debug_printf(X) do {} while( 0 )
#endif

Then the FreeRTOS_debug_printf macro would have taken exactly the same
arguments as lUDPLoggingPrintf.

mvh.,

David

friesen wrote on Wednesday, June 06, 2018:

Thanks for that explanation, and yes, I mean parenthesis, not quotes. What is the proper way to define this then?

I did it like

void PrintUart(const char* format, ...) {
//My print code here
}

In FreeRTOSIPConfig.h

#define ipconfigHAS_DEBUG_PRINTF    1
#if ( ipconfigHAS_DEBUG_PRINTF == 1 )
    #define FreeRTOS_debug_printf( X,... )    PrintUart( X "\r",##__VA_ARGS__)
#endif

Which fails unless I remove the extra parenthesis.

heinbali01 wrote on Wednesday, June 06, 2018:

#define FreeRTOS_debug_printf( X )   PrintUart X

friesen wrote on Thursday, June 07, 2018:

Thank you.

heinbali01 wrote on Thursday, June 07, 2018:

Hi David, thanks for this information again.

#define FreeRTOS_debug_printf     lUDPLoggingPrintf
#define FreeRTOS_debug_printf(X)  do {} while( 0 )

… and the above would have been a nicer solution.

davidbrown wrote on Thursday, June 07, 2018:

Hi,

The way you are defining it is the C99 way. It will then look like a
normal function or function-like macro - you don’t need double
parenthesis. But it also means that you /can’t/ have double
parenthesis, and it is thus inconsistent with the current FreeRTOS way
(which is designed to work with C90).

It would be nice if FreeRTOS could take advantage of the improvements in
modern C, but consistency and backwards compatibility are vital - so my
advice is to use Hein’s recommendation.

Don’t forget that your version added a “\r”, while Hein’s does not.

Incidentally, if your code is going to be compiled with gcc, then I
strongly recommend a gcc extension here:

extern void PrintUart(const char* format, …)
attribute((format(printf, 1, 2)));

This well let gcc check that your format string and the parameters match up.

mvh.,

David

On 06/06/2018 15:38, Erik wrote:

Thanks for that explanation, and yes, I mean parenthesis, not quotes. What is the proper way to define this then?

I did it like

void PrintUart(const char* format, ...) {
//My print code here
}

In FreeRTOSIPConfig.h

#define ipconfigHAS_DEBUG_PRINTF    1
#if ( ipconfigHAS_DEBUG_PRINTF == 1 )
     #define FreeRTOS_debug_printf( X,... )    PrintUart( X "\r",##__VA_ARGS__)
#endif

Which fails unless I remove the extra parenthesis.