Macro Magic






4.24/5 (6 votes)
Everything you always wanted to know about Macros but were afraid to ask
Introduction
Think macros are simple, boring and of little use, huh?
Background
C++ templates are not a replacement to the C macros, but rather combined together creates a powerful instrument, which allow experienced programmers to implement interesting idioms like Execute Member Function If It Is Present.
Using the Code
So, what we can do with macros?
-
Multi line
// Literally equal to the #define MIN_(A, B) ((A) < (B) ? (A) : (B)) #define MIN_(A, B) (\ (A) < (B) ? \ (A) : (B)\ )
-
Variadic
// Using PRINT_ERROR_("%s they be stealin my %s!", "oh noes", "bucket") // will print "oh noes they be stealin my bucket" in to the standard error stream #define PRINT_ERROR_(...) fprintf(stderr, __VA_ARGS__)
-
Concatenation
As strings:
#define HELLO_ "say hello to my little" #define SPACE_ " " #define WORLD_ "friend" // Will be ptr. to the compile-time constant C str. "say hello to my little friend" static auto const HELLO_WORLD_ = HELLO_""SPACE_""WORLD_;
As tokens:
#define NUMBER_FROM_DIGITS_(D1, D2, D3) D1 ## D2 ## D3 static const auto MY_NUMBER_ = NUMBER_FROM_DIGITS_(1, 2, 3); // will be 123
-
Stringification
#define MAKE_STR_(STR1, STR2, STR3) #STR1""#STR2""#STR3 // Will be ptr. to the compile-time constant C str. "paint_it_black" static auto const MY_STR_ = MAKE_STR_(paint, _it_, black);
-
Call other
#define M_S_(arg) #arg // stringify #define MAKE_STR__(arg) M_S_(arg) // Will be ptr. to the compile-time constant C str. // In MS VS 2013: "+2,147,483,647" (for 32 bit) // In modern GCC: "INT_MAX" static auto const MY_STR__ = MAKE_STR__(INT_MAX);
-
Function-like AND recursion
int f() throw() {return 1;} // Only expanded if appears with parentheses #define f() (f() + 1) // self-reference is NOT considered a macro call (passed unchanged) static const auto VAL_1_ = f(); // will be 2 static auto const VAL_2_ = f; // will ptr. to the function
-
One definition rule
#define SUM(A, B) ((A) + (B)) #define SUM(A, B) ((A) + (B)) // NO compile error here [the rule doesn't applied to macros] #define SUM(A, B) ((A)+(B)) // compile warning here (allowed redefinition) #define SUM(A, B) ( (A) + (B) ) #define SUM(A, B) ( (A ) + ( B) ) // NO compile warning here (if spacers, their amount is ignored) #define SUM(A, B) ( (A ) /*what if*/ + /*add some comments here*/ ( B) ) // NO redefinition here: comments count as spacers
-
Redefinition "on the fly"
#define SUM(A, B) ((A) + (B)) // Will be 1 + 3 + 2 = 6 static const auto VAL_ = SUM(1 #undef SUM #define SUM + 3 SUM, // new definition is used for argument pre-expansion 2); // original definition is used for argument replacement
Tests
Test code in the Ideone online compiler
std::cout << MIN_(1 + 2, 3 + 4) << std::endl; // prints "3"
PRINT_ERROR_("%s they be stealin my %s!", "oh noes", "bucket"); // "oh noes they be stealin my bucket!"
std::cout << HELLO_WORLD_ << std::endl; // "say hello to my little friend"
std::cout << MY_NUMBER_ + 1 << std::endl; // prints 124
std::cout << MY_STR_ << std::endl; // prints "paint_it_black"
std::cout << MY_STR__ << std::endl; // prints "INT_MAX"
std::cout << VAL_1_ << std::endl; // prints "2"
std::cout << VAL_2_ + 10 << std::endl; // prints "1" (as a non-nil pointer)
std::cout << VAL_ << std::endl; // prints "6"
Points of Interest
Warning! The result may vary due to the compiler used!
Know more
magic? Share in the comments!
History
- 5th March, 2016: Initial version