0

I would like to implement a debug printing mechanism that allows both run-time and compile time filtering. Filtering is accomplished via a one-hot encoded mask.

Run-time filtering is done by checking the mask within a function. If the level is not currently set it returns. Compile-time actually removes the call to the debug print.

Compile time can be implemented easily enough.

#include <stdio.h>

#define PRINT_LEVEL_0 (0x0)
#define PRINT_LEVEL_1 (0x1)
#define PRINT_LEVEL_2 (0x2)
#define PRINT_LEVEL_3 (0x4)
#define PRINT_LEVEL_4 (0x8)

#define PRINT_COMPILE_LEVEL (PRINT_LEVEL_1 | PRINT_LEVEL_3 | PRINT_LEVEL_4)

#define PRINT( printLevel, ... ) \
    printLevel ## _FILTER(__VA_ARGS__)

#define PRINT_LEVEL_0_FILTER( ... )         // Compile out

#if (PRINT_COMPILE_LEVEL & PRINT_LEVEL_1)
    #define PRINT_LEVEL_1_FILTER(...)       printf(__VA_ARGS__)
#else
    #define PRINT_LEVEL_1_FILTER(...)       // Compile out
#endif

#if (PRINT_COMPILE_LEVEL & PRINT_LEVEL_2)
    #define PRINT_LEVEL_2_FILTER(...)       printf(__VA_ARGS__)
#else
    #define PRINT_LEVEL_2_FILTER(...)       // Compile out
#endif

#if (PRINT_COMPILE_LEVEL & PRINT_LEVEL_3)
    #define PRINT_LEVEL_3_FILTER(...)       printf(__VA_ARGS__)
#else
    #define PRINT_LEVEL_3_FILTER(...)       // Compile out
#endif

#if (PRINT_COMPILE_LEVEL & PRINT_LEVEL_4)
    #define PRINT_LEVEL_4_FILTER(...)       printf(__VA_ARGS__)
#else
    #define PRINT_LEVEL_4_FILTER(...)       // Compile out
#endif


int main(void)
{
    PRINT( PRINT_LEVEL_3,
           "PRINT_TEST NO ARGS\n" );

    PRINT( PRINT_LEVEL_3,
           "PRINT_TEST ONE ARGS %u\n", 1 );

    PRINT( PRINT_LEVEL_3,
           "PRINT_TEST TWO ARGS %u %u\n", 1, 2 );

    PRINT( PRINT_LEVEL_3,
           "PRINT_TEST THREE ARGS %u %u %u\n", 1, 2, 3 );

    PRINT( PRINT_LEVEL_3,
           "PRINT_TEST FOUR ARGS %u %u %u %u\n", 1, 2, 3, 4 );

    PRINT( PRINT_LEVEL_3,
           "PRINT_TEST ONE ARGS %s\n", "Garfield" );

    PRINT( PRINT_LEVEL_3,
           "PRINT_TEST TWO ARGS %s %s\n", "Garfield", "likes" );

    PRINT( PRINT_LEVEL_3,
           "PRINT_TEST THREE ARGs %s %s %s\n", "Garfield", "likes", "eating" );

    PRINT( PRINT_LEVEL_3,
           "PRINT_TEST FOUR ARGS %s %s %s %s\n", "Garfield", "likes", "eating", "lasagna" );

    return 0;
}

This only works if a valid PRINT_LEVEL_# pre-processor is provided as the first input argument to the PRINT macro.

I am interested in finding a way to call a different macro (valid for run-time filtering) if printLevel ## _FILTER doesn't exist. The exact case would be something like:

void foo()
{
    uint32_t level = PRINT_LEVEL_3;

    PRINT( level, "VARIABLE LEVEL TEST" );
}

Thanks for your help!

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
  • To be clear, the reason that the example within the foo() functino doesn't work is because the "level_FILTER" macro does not exist. – trialUnplugged Mar 08 '16 at 14:52
  • It seems impossible *this* way. – Eugene Sh. Mar 08 '16 at 15:01
  • Any reason why you can't use a function like for example `void print (int levels, const char* str [levels])`? Too type safe and too readable? You could call it like this, for example: `print(3, &(const char* str[3]) {"Garfield", "hates", "macros"} );` – Lundin Mar 08 '16 at 15:01

1 Answers1

1

This would be much simpler if you trusted the compiler to detect constant expressions. The you could just use a "run-time" if in the comfort that if the condition has a value known at compile time, the compiler will avoid doing the test (and furthermore eliminate the conditionally-executed code if the condition is known to be false).

Since almost all modern compilers have no trouble with this optimization, it should not be difficult to assume it will happen.

Then you just need

#define PRINT(level, ...) \
  do if (level & LEVEL_MASK) \
  printf(__VA_ARGS__); while (0)
rici
  • 234,347
  • 28
  • 237
  • 341