A plethora of macros

Today I want to share a few very useful macros with you, which I’ve accumulated over the last years of my programming career. Enjoy!

The following three use Visual Studio’s non-standard C++ extensions:

/// allows member functions to be made abstract. uses nonstandard C++ extensions provided by MSVC
#define ME_ABSTRACT    abstract

/// marks member functions as being an override of a base class virtual function. uses nonstandard C++ extensions provided by MSVC
#define ME_OVERRIDE    override

/// allows classes and member functions to be made sealed. uses nonstandard C++ extensions provided by MSVC
#define ME_FINAL       sealed

Even though they use non-standard C++ keywords, they are useful because these extensions must be enabled in any Windows application anyway (Microsoft still hasn’t fixed this issue in Visual Studio 2010). So while they are available, why not use them? Marking member functions with abstract or override gives you the same nice compiler errors you would get from a language such as C#, and by wrapping them in a macro you can easily define them to “=0” and an empty macro on other platforms, respectively (or use the C++11 keywords if your compiler supports them).

Personally, I don’t use the sealed keyword, but I’ve added it for completeness sake because final is a keyword in the new C++11 standard (hence the macro name ME_FINAL).

The next few keywords/compiler hints are rather simple, but maybe you never needed them in the past, or didn’t know they actually existed:

/// allows to emit pragmas from within macros
#define ME_PRAGMA(pragma)    __pragma(pragma)

/// support for C99 restrict keyword
#define ME_RESTRICT    __restrict

/// tells the compiler that the return value (RV) of a function is an object that will not be aliased with any other pointers
#define ME_RESTRICT_RV    __declspec(restrict)

/// tells the compiler that a function call does not modify or reference visible global state and only modifies the memory pointed to directly by pointer parameters
#define ME_NO_ALIAS    __declspec(noalias)

/// forces a function to be inlined
#define ME_INLINE    __forceinline

/// tells the compiler to never inline a particular function
#define ME_NO_INLINE    __declspec(noinline)

/// passes optimization hints to the compiler
#define ME_HINT(hint)    __assume(hint)

/// used in switch-statements whose default-case can never be reached, resulting in more optimal code
#define ME_NO_SWITCH_DEFAULT    ME_HINT(0)

Those macros should be self-explanatory after reading the comments.

The next macro is is really handy if you want to stringize a token in the pre-processor, and the token you want to stringize is a macro itself:

/// stringizes a string, even macros
#define ME_STRINGIZE_HELPER(token)    #token
#define ME_STRINGIZE(str)             ME_STRINGIZE_HELPER(str)

Same goes for joining two tokens – the following will join any tokens, even macros themselves:

/// concatenates two strings, even when the strings are macros themselves
#define ME_JOIN(x, y)                    ME_JOIN_HELPER(x, y)
#define ME_JOIN_HELPER(x, y)             ME_JOIN_HELPER_HELPER(x, y)
#define ME_JOIN_HELPER_HELPER(x, y)      x##y

And last, but definitely not least, a macro for evaluating how many parameters have been passed to a variable argument macro:

#define ME_VA_NUM_ARGS(...)                        ME_VA_NUM_ARGS_HELPER(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define ME_VA_NUM_ARGS_HELPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...)    N

Have you ever wanted to write different “implementations” of a macro, taking a different number of arguments, while still having the same name? You can do that now with the above! A simple example (no, don’t use a macro for that):

#define ME_MAX_2(a, b)            std::max(a, b)
#define ME_MAX_3(a, b, c)         ME_MAX_2(ME_MAX_2(a, b), c)
#define ME_MAX_4(a, b, c, d)      ME_MAX_2(ME_MAX_3(a, b, c), d)
#define ME_MAX(...)               ME_JOIN(ME_MAX_, ME_VA_NUM_ARGS(__VA_ARGS__)) ME_PASS_VA(__VA_ARGS__)

Unfortunately, even Visual Studio 2010 has this bug which treats a __VA_ARGS__ argument as being one single parameter. Hence, we have to work around this bug using the following piece of macro magic:

// ME_VA_NUM_ARGS() is a very nifty macro to retrieve the number of arguments handed to a variable-argument macro
// unfortunately, VS 2010 still has this compiler bug which treats a __VA_ARGS__ argument as being one single parameter:
// https://connect.microsoft.com/VisualStudio/feedback/details/521844/variadic-macro-treating-va-args-as-a-single-parameter-for-other-macros#details
#if _MSC_VER >= 1400
#    define ME_VA_NUM_ARGS_HELPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...)    N
#    define ME_VA_NUM_ARGS_REVERSE_SEQUENCE            10, 9, 8, 7, 6, 5, 4, 3, 2, 1
#    define ME_LEFT_PARENTHESIS (
#    define ME_RIGHT_PARENTHESIS )
#    define ME_VA_NUM_ARGS(...)                        ME_VA_NUM_ARGS_HELPER ME_LEFT_PARENTHESIS __VA_ARGS__, ME_VA_NUM_ARGS_REVERSE_SEQUENCE ME_RIGHT_PARENTHESIS
#else
#    define ME_VA_NUM_ARGS(...)                        ME_VA_NUM_ARGS_HELPER(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#    define ME_VA_NUM_ARGS_HELPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...)    N
#endif

// ME_PASS_VA passes __VA_ARGS__ as multiple parameters to another macro, working around the above-mentioned bug
#if _MSC_VER >= 1400
#    define ME_PASS_VA(...)                            ME_LEFT_PARENTHESIS __VA_ARGS__ ME_RIGHT_PARENTHESIS
#else
#    define ME_PASS_VA(...)                            (__VA_ARGS__)
#endif

In some situations, this macro really comes in handy. Feel free to use those macros in your projects as well, and let me know if anything causes problems or is unclear.

Advertisements

2 thoughts on “A plethora of macros

  1. Pingback: An improved assert() | Molecular Musings

  2. Pingback: C++ compile time checked printf format | wumpfblog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s