0% found this document useful (0 votes)
5 views

C++ Tutorial =_ Macros

Uploaded by

suraj97394878
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views

C++ Tutorial =_ Macros

Uploaded by

suraj97394878
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 3

9/29/21, 9:44 PM C++ Tutorial => Macros

Download C++ (PDF)


C++ Preprocessor

Macros

Example
Macros are categorized into two main groups: object-like macros and function-like macros. Macros are treated as a token sub-
stitution early in the compilation process. This means that large (or repeating) sections of code can be abstracted into a pre-
processor macro.

// This is an object-like macro


#define PI 3.14159265358979

// This is a function-like macro.


// Note that we can use previously defined macros
// in other macro definitions (object-like or function-like)
// But watch out, its quite useful if you know what you're doing, but the
// Compiler doesnt know which type to handle, so using inline functions instead
// is quite recommended (But e.g. for Minimum/Maximum functions it is quite useful)
#define AREA(r) (PI*(r)*(r))

// They can be used like this:


double pi_macro = PI;
double area_macro = AREA(4.6);

The Qt library makes use of this technique to create a meta-object system by having the user declare the Q_OBJECT macro at
the head of the user-defined class extending QObject.

Macro names are usually written in all caps, to make them easier to differentiate from normal code. This isn't a requirement,
but is merely considered good style by many programmers.

When an object-like macro is encountered, it's expanded as a simple copy-paste operation, with the macro's name being re-
placed with its definition. When a function-like macro is encountered, both its name and its parameters are expanded.

double pi_squared = PI * PI;


// Compiler sees:
double pi_squared = 3.14159265358979 * 3.14159265358979;

double area = AREA(5);


// Compiler sees:
double area = (3.14159265358979*(5)*(5))

Due to this, function-like macro parameters are often enclosed within parentheses, as in AREA() above. This is to prevent any
bugs that can occur during macro expansion, specifically bugs caused by a single macro parameter being composed of multi-
ple actual values.

#define BAD_AREA(r) PI * r * r

double bad_area = BAD_AREA(5 + 1.6);


// Compiler sees:
double bad_area = 3.14159265358979 * 5 + 1.6 * 5 + 1.6;

double good_area = AREA(5 + 1.6);


// Compiler sees:
double good_area = (3.14159265358979*(5 + 1.6)*(5 + 1.6));

https://riptutorial.com/cplusplus/example/3527/macros 1/4
9/29/21, 9:44 PM C++ Tutorial => Macros

Also note that due to this simple expansion, care must be taken with the parameters passed to macros, to prevent unexpected
side effects. If the parameter is modified during evaluation, it will be modified each time it is used in the expanded macro, which
usually isn't what we want. This is true even if the macro encloses the parameters in parentheses to prevent expansion from
breaking anything.

int oops = 5;
double incremental_damage = AREA(oops++);
// Compiler sees:
double incremental_damage = (3.14159265358979*(oops++)*(oops++));

Additionally, macros provide no type-safety, leading to hard-to-understand errors about type mismatch.

As programmers normally terminate lines with a semicolon, macros that are intended to be used as standalone lines are often
designed to "swallow" a semicolon; this prevents any unintended bugs from being caused by an extra semicolon.

#define IF_BREAKER(Func) Func();

if (some_condition)
// Oops.
IF_BREAKER(some_func);
else
std::cout << "I am accidentally an orphan." << std::endl;

In this example, the inadvertent double semicolon breaks the if...else block, preventing the compiler from matching the
else to the if . To prevent this, the semicolon is omitted from the macro definition, which will cause it to "swallow" the semi-
colon immediately following any usage of it.

#define IF_FIXER(Func) Func()

if (some_condition)
IF_FIXER(some_func);
else
std::cout << "Hooray! I work again!" << std::endl;

Leaving off the trailing semicolon also allows the macro to be used without ending the current statement, which can be
beneficial.

#define DO_SOMETHING(Func, Param) Func(Param, 2)

// ...

some_function(DO_SOMETHING(some_func, 3), DO_SOMETHING(some_func, 42));

Normally, a macro definition ends at the end of the line. If a macro needs to cover multiple lines, however, a backslash can be
used at the end of a line to indicate this. This backslash must be the last character in the line, which indicates to the preproces-
sor that the following line should be concatenated onto the current line, treating them as a single line. This can be used multiple
times in a row.

#define TEXT "I \


am \
many \
lines."

// ...

std::cout << TEXT << std::endl; // Output: I am many lines.

This is especially useful in complex function-like macros, which may need to cover multiple lines.

https://riptutorial.com/cplusplus/example/3527/macros 2/4
9/29/21, 9:44 PM C++ Tutorial => Macros

#define CREATE_OUTPUT_AND_DELETE(Str) \
std::string* tmp = new std::string(Str); \
std::cout << *tmp << std::endl; \
delete tmp;

// ...

CREATE_OUTPUT_AND_DELETE("There's no real need for this to use 'new'.")

In the case of more complex function-like macros, it can be useful to give them their own scope to prevent possible name colli-
sions or to cause objects to be destroyed at the end of the macro, similar to an actual function. A common idiom for this is do
while 0, where the macro is enclosed in a do-while block. This block is generally not followed with a semicolon, allowing it to
swallow a semicolon.

#define DO_STUFF(Type, Param, ReturnVar) do { \


Type temp(some_setup_values); \
ReturnVar = temp.process(Param); \
} while (0)

int x;
DO_STUFF(MyClass, 41153.7, x);

// Compiler sees:

int x;
do {
MyClass temp(some_setup_values);
x = temp.process(41153.7);
} while (0);

There are also variadic macros; similarly to variadic functions, these take a variable number of arguments, and then expand
them all in place of a special "Varargs" parameter, __VA_ARGS__ .

#define VARIADIC(Param, ...) Param(__VA_ARGS__)

VARIADIC(printf, "%d", 8);


// Compiler sees:
printf("%d", 8);

Note that during expansion, __VA_ARGS__ can be placed anywhere in the definition, and will be expanded correctly.

#define VARIADIC2(POne, PTwo, PThree, ...) POne(PThree, __VA_ARGS__, PTwo)

VARIADIC2(some_func, 3, 8, 6, 9);
// Compiler sees:
some_func(8, 6, 9, 3);

In the case of a zero-argument variadic parameter, different compilers will handle the trailing comma differently. Some compil-
ers, such as Visual Studio, will silently swallow the comma without any special syntax. Other compilers, such as GCC, require
you to place ## immediately before __VA_ARGS__ . Due to this, it is wise to conditionally define variadic macros when portability
is a concern.

// In this example, COMPILER is a user-defined macro specifying the compiler being used.

#if COMPILER == "VS"


#define VARIADIC3(Name, Param, ...) Name(Param, __VA_ARGS__)
#elif COMPILER == "GCC"
#define VARIADIC3(Name, Param, ...) Name(Param, ##__VA_ARGS__)
#endif /* COMPILER */

PDF - Download C++ for free

https://riptutorial.com/cplusplus/example/3527/macros 3/4

You might also like