What Helpful Macros Do You Use?

I recently came up with a handful of small macros that have been helpful in debugging, enough to warrant me creating a small 'MacroTools.h' file I've been including in my projects.

It's only small but I intend to add to it and was inspired by the start of a video I saw on competitive programming. This guy was #define'ing and typedef'ing all sorts like vectors of vectors of pairs of ints to be like vvpii() and all sorts.

Do you have any macro / #define tools or utilities you like to use often from program to program?

#define ever (;;)
1 Like

:smiley: you could also do #define forever while(1)

I use this for easy removing of debug traces

#define DEBUG 1    // SET TO 0 OUT TO REMOVE TRACES

#if DEBUG
#define D_SerialBegin(...) Serial.begin(__VA_ARGS__)
#define D_print(...)    Serial.print(__VA_ARGS__)
#define D_write(...)    Serial.print(__VA_ARGS__)
#define D_println(...)  Serial.println(__VA_ARGS__)
#define D_printf(...)  Serial.printf(__VA_ARGS__)
#else
#define D_SerialBegin(...)
#define D_print(...)
#define D_write(...)
#define D_println(...)
#define D_printf(...)
#endif
1 Like

Nice! I've used something similar in the past too. Sure beats writing this over and over :joy:

#ifdef DEBUG_MODE
Serial.println(x);
#endif

Which is what I used to do.

This is my macro for debugging, I hate macros but sometimes it can't be done without :slight_smile:

#define DEBUG Serial

#ifdef DEBUG
  #define printd( variable, ... ) \
  {\
    DEBUG.print( F( "[DEBUG] in \"" ) );\
    DEBUG.print( __PRETTY_FUNCTION__ );\
    DEBUG.print( F( "\" at line " ) );\
    DEBUG.print( __LINE__ );\
    DEBUG.print( F( " : " ) );\
    DEBUG.print( F( #variable ) );\
    DEBUG.print( F( " = " ) );\
    DEBUG.println( variable, ##__VA_ARGS__ );\
  }
#else
  #define printd( ... )
#endif

Example

void setup()
{
  Serial.begin( 115200 );
  printd( LED_BUILTIN );
}
void loop()
{
  printd( millis(), BIN );
}

Edit: I didn't know it was possible to use variadic arguments in macros! So I added it, and also used F() as suggested

1 Like

I'd make better use of F() - don't want the building to collapse because of the weight of the scaffolding!

1 Like

Cool to know that what I've been using is quite similar to what other people use too.

This is how it's looking so far (I just changed to add in the F() - thanks AWOL!)
'MacroTools.h'

#ifndef MacroTools_h
#define MacroTools_h

#define arrSize(x) sizeof(x) / sizeof(x[0])

#ifdef DEBUG_MODE
  #define S_BEGIN(...) Serial.begin(__VA_ARGS__)
  #define S_PRINT(...) Serial.print(__VA_ARGS__)
  #define S_PRINT_F(x) Serial.print(F(x))
  #define S_PRINTLN(...) Serial.println(__VA_ARGS__)
  #define S_PRINTLN_F(x) Serial.println(F(x)))
#else
  #define S_BEGIN(...)
  #define S_PRINT(...)
  #define S_PRINT_F(x)
  #define S_PRINTLN(...)
  #define S_PRINTLN_F(x)
#endif

#define DEBUG(x)                \
  S_PRINT_F("Debug: ");         \
  S_PRINT(#x);                  \
  S_PRINT_F(" = ");             \
  S_PRINT(x);                   \
  S_PRINT_F(" | Line: ");       \
  S_PRINT(__LINE__);            \
  S_PRINT_F(" | ");             \
  S_PRINT(__PRETTY_FUNCTION__); \
  S_PRINT_F(" | ");             \
  S_PRINTLN(__FILE__);

#define TRACE()                 \
  S_PRINT_F("Trace: [Line: ");  \
  S_PRINT(__LINE__);            \
  S_PRINT_F("] | ");            \
  S_PRINT(__PRETTY_FUNCTION__); \
  S_PRINT_F(" | ");             \
  S_PRINTLN(__FILE__);

#define SINGLETON(X)    \
 public:                \
  static X& get() {     \
    static X singleton; \
    return singleton;   \
  }

#endif

The trace... I quite often find it very helpful when debugging to check that the program flow has reached a certain place or not (function, constructor etc). I used to litter the code with

Serial.println("Debug 1");
Serial.println("Debug 2"); 
Serial.println("Debug 3");

And use the numbers to see what happens when/where.

Here's a use case of the above header (so far!):

#define DEBUG_MODE

#include "MacroTools.h"

void otherFunction() {
  TRACE();
}

struct Foo {
  int fooValue = 420;
  Foo() { TRACE(); }
};

void setup()
{
  Serial.begin( 115200 );
  TRACE();
  Foo foo;
  otherFunction();
  DEBUG(foo.fooValue);
}
void loop() { }

Of course, I'll likely add to it as time goes on.
OUTPUT:

Trace: [Line: 65] | void setup() | /tmp/build-Q593wg/sketch/sketch.ino
Trace: [Line: 59] | Foo::Foo() | /tmp/build-Q593wg/sketch/sketch.ino
Trace: [Line: 54] | void otherFunction() | /tmp/build-Q593wg/sketch/sketch.ino
Debug: foo.fooValue = 420 | Line: 68 | void setup() | /tmp/build-Q593wg/sketch/sketch.ino

I make very little use of macros. You will note the use of I, me and mine when ppl talk about them and

it can make code hard to read if they aren’t entirely obvious. I read more than I write.

I’ve had to work some code where the original author, a not-C programmer, had rigged up all kindsa macros so his code looked like Pascal or whatever not-C language he’d rather have been using.

It was impressive the extent to which he could…. but very annoying and rude.

With tiny programs on small systems I prefer to write in C and leave libraries and frameworks and RTOSs and macros of any complexity for places where they are more like essential.

Debugging macros is probably the one place where I bend the rule. Since code I write never has any bugs, I have found it unnecessary to roll my own set of those, and they are easily seen for what they are as I read other ppls code.

a7

:joy:

None. I never use them.

not even as guards against multiple include in .h files ? ( #pragma once being non standard )

You might not, but the Arduino Core and probably every library you use will :smiley:

So you do use them, just passively! If it's good enough for them, and it makes my life easier, then I'm happy to use them. Typing out that 'trace' even once was a pain!

I do have some #ifdef's in some places. Notice that the effect of an ifdef to guard against multiple includes has a very LOCAL effect. It does not really influence the code that follows...

st3v3n92 So you do use them, just passively! If it's good enough for them, and it makes my life easier, then I'm happy to use them. Typing out that 'trace' even once was a pain!

Using existing macros is a different thing than creating them.

Are if/defs and their ilk considered macros?

a7

no they are known as "conditional compilation directives" but they usually work in conjunction with a macro (although the expression they deal with just has to be a constant expression)

Macros can be useful for writing code which can be compiled for different processors, but these would probably be too specific to be candidates for inclusion in a general macro toolbox.

Do you have anything up your sleeve that would be generic enough for inclusion?

This is my modified bare-minimum-sketch

// start of macros dbg and dbgi
#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope

#define dbgi(myFixedText, variableName,timeInterval) \
  do { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  } while (false);
// usage: dbgi("2:my fixed text",myVariable,1000);
// myVariable can be any variable or expression that is defined in scope
// third parameter is the time in milliseconds that must pass by until the next time a 
// Serial.print is executed
// end of macros dbg and dbgi

void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__));
  Serial.print( F("  compiled ") );
  Serial.print(F(__DATE__));
  Serial.print( F(" ") );
  Serial.println(F(__TIME__));  
}


boolean TimePeriodIsOver (unsigned long &periodStartTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();  
  if ( currentMillis - periodStartTime >= TimePeriod )
  {
    periodStartTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}

unsigned long MyTestTimer = 0;                   // variables MUST be of type unsigned long
const byte    OnBoard_LED = 2;


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);
  
  if ( TimePeriodIsOver(MyBlinkTimer,BlinkPeriod) ) {
    digitalWrite(IO_Pin,!digitalRead(IO_Pin) ); 
  }
}



void setup() {
  Serial.begin(115200);
  Serial.println( F("Setup-Start") );
  PrintFileNameDateTime();

}


void loop() {
  BlinkHeartBeatLED(OnBoard_LED,500);

  if ( TimePeriodIsOver(MyTestTimer,1000) ) {

  }  

}

best regards Stefan

2 Likes

That's a whole sketch before you've even started a sketch! :smiley: