What are some tricks I can use with macros?

15,600

Solution 1

In C, it's common to define macros that do some stuff getting the verbatim argument, and at the same time define functions to be able to get the address of it transparently.

// could evaluate at compile time if __builtin_sin gets
// special treatment by the compiler
#define sin(x) __builtin_sin(x)

// parentheses avoid substitution by the macro
double (sin)(double arg) {
    return sin(arg); // uses the macro
}

int main() {
    // uses the macro
    printf("%f\n", sin(3.14));

    // uses the function
    double (*x)(double) = &sin;

    // uses the function
    printf("%f\n", (sin)(3.14));
}

Solution 2

Coolest macro is: assert, include guards, __FILE__, __LINE__.
Avoid using other macro in your code.

EDIT:
Use macros only when you don't have legal solution w/o them.

Solution 3

SHOW() for debugging:

#define SHOW(X) cout << # X " = " << (X) << endl

The double-evaluation to expand the arguments trick: (E.g. Use the actual line number and not "__LINE__".)

    /* Use CONCATENATE_AGAIN to expand the arguments to CONCATENATE */
#define CONCATENATE(      x,y)  CONCATENATE_AGAIN(x,y)
#define CONCATENATE_AGAIN(x,y)  x ## y

Static compile-time assertions.
E.g.:

#define CONCATENATE_4(      a,b,c,d)  CONCATENATE_4_AGAIN(a,b,c,d)
#define CONCATENATE_4_AGAIN(a,b,c,d)  a ## b ## c ## d

    /* Creates a typedef that's legal/illegal depending on EXPRESSION.       *
     * Note that IDENTIFIER_TEXT is limited to "[a-zA-Z0-9_]*".              *
     * (This may be replaced by static_assert() in future revisions of C++.) */
#define STATIC_ASSERT( EXPRESSION, IDENTIFIER_TEXT)                     \
  typedef char CONCATENATE_4( static_assert____,      IDENTIFIER_TEXT,  \
                              ____failed_at_line____, __LINE__ )        \
            [ (EXPRESSION) ? 1 : -1 ]

Used via:

typedef  int32_t  int4;

STATIC_ASSERT( sizeof(int4) == 4, sizeof_int4_equal_4 );

Initializing an instance of class CodeLocation: (Storing File/Line/Function from the point of invocation -- this can *ONLY* be done with a macro or by directly accessing the __FILE__/__LINE__/etc macros at the source point.)

        /* Note:  Windows may have __FUNCTION__.  C99 defines __func__. */
#define CURRENT_CODE_LOCATION()  \
           CodeLocation( __PRETTY_FUNCTION__, __FILE__, __LINE__ )

Subsequently used by MESSAGE/WARN/FAIL macros as a convenient source-location printing mechanism. For example:

#define WARN_IF_NAN(X)                                      \
  do                                                        \
  {                                                         \
    if ( isnan(X) != 0 )                                    \
      WARN( # X " is NaN (Floating Point NOT-A-NUMBER)" );  \
    if ( isinf(X) != 0 )                                    \
      WARN( # X " is INF (Floating Point INFINITY)" );      \
  } while ( false )

Assert/Unless macros. You can pass any token, including operators like '==', through a macro. So constructs like:

ASSERT( foo, ==, bar )

Or

UNLESS( foo, >=, 0, value=0; return false; );

Are legal. Assert/Unless macros can automatically add all sorts the nice useful info like CodeLocation, stack traces, or throwing exceptions / coredumping / exiting gracefully.


Making errno simplier:

#define ERRNO_FORMAT  "errno= %d (\"%s\")"
#define ERRNO_ARGS    errno, strerror(errno)
#define ERRNO_STREAM  "errno= " << errno << " (\"" << strerror(errno) << "\") "

E.g. printf( "Open failed. " ERRNO_FORMAT, ERRNO_ARGS );

Solution 4

You can have a look at Boost.Preprocessor to find lot's of interesting uses of the preprocessor...

Solution 5

One of my favorite tricks is a way to pass variable number of arguments to macros, to be later used in calling printf-like functions for example. To do this, I specify that the macro has only one parameter and use it in the body of the macro without (), but pass all the parameters to the macro in (( and )), so the list looks like a single argument. For example,

#define TRACE( allargs) do { printf allargs; } while ( 0)
...
TRACE(( "%s %s\n", "Help", "me"));
Share:
15,600
Admin
Author by

Admin

Updated on June 12, 2022

Comments

  • Admin
    Admin almost 2 years

    In our legacy code, as well as our modern code, we use macros to perform nifty solutions like code generations, etc. And we make use of both the # and ## operators.

    I am curious how other developers use macros to do cool things, if they use them at all.