What Are Macros In C++ - C++ Programming Tutorial
C++ Course / Miscellaneous / What Are Macros In C++

What Are Macros In C++

BLUF: Mastering What Are Macros In C++ is a critical step in becoming a proficient C++ developer. This lesson provides a deep dive into the syntax, performance considerations, and real-world applications of this concept.
Key Performance Insight: What Are Macros In C++

C++ is renowned for its efficiency. Learn how What Are Macros In C++ enables low-level control and high-performance computing in the tutorial below.

In C++, a macro refers to a part of the code that holds a specific value which can be substituted. The macro keyword is established using the #define directive. When the program is compiled, the compiler accesses the macros and substitutes the macro name with the defined value. Unlike regular code statements, macros do not require a semicolon (;) at the end for termination.

You have the option to generate macros that accept parameters by employing the #define directive, specifying the macro's name, its parameters enclosed in parentheses, and the replacement content. As an illustration:

Example

#define SQUARE(x) ((x)*(x))

This defines a macro called "SQUARE" which accepts a single parameter, "x," and outputs the square of that input value. This macro can be incorporated into your code as shown below:

Example

int x = 5;
int y = SQUARE(x); // equivalent to y = x*x;

Keep in mind that the preprocessor enlarges macros prior to compiling the code, which can occasionally result in unforeseen behavior or errors if not employed cautiously. It is crucial to be aware of the possible drawbacks of macros, including inadvertent side effects or problems related to operator precedence.

Example 1:

Example

#include <iostream>
using namespace std;

#define AREA(l, b) (l * b)

int main() {
    
    int l1 = 14, l2 = 6, area;

  
    area = AREA(l1, l2);
    cout << "Area of the rectangle" << " is: "<< area;

    return 0;
}

Output:

Explanation:

In this program, we will calculate the area of a rectangle using macros that take the length and width of the rectangle as input from the user. The main class of the program declares two integer values representing the length and width. The user provides integer inputs which are then used to compute the rectangle's area through the macro's logic.

Types of Macros in C++

In C++, there exists a wide variety of macro types. Let's delve into each category individually.

1. Chain macros:

In C++, concatenation macros refer to macros that enable the merging of various strings or arguments into a unified expression. These concatenation macros prove to be highly beneficial for intricate macro operations that produce code depending on multiple inputs.

We can form a macro chain using the ##operator. This operator is commonly known as a token-pasting operator, which combines two operands to create a unified token.

Example

#define CONCAT(a, b) a##b

The micro accepts two parameters, a and b, and combines them using the ##operator. For instance, when utilizing the macro in this manner:

Example

int CONCAT(x, y) = 42;

it will be expanded by the preprocessor to:

int xy = 42;

You also have the option to utilize chain macros for constructing more intricate expressions or statements.

For example:

Example

#define ASSERT(expr, message) \
   
if (!(expr)) { \
       
printf("Assertion failed: %s\n", #expr); \
       
printf("%s\n", message); \
    }

This macro accepts an expression "expr" along with a message string "message" to produce a basic assert statement. The ## operator combines the message string with the printf statement. For instance, when employing the macro in this manner:

Example

ASSERT(x == y, "x and y should be equal");
It will be expanded by the preprocessor to:
if (!(x == y)) {
   
printf("Assertion failed: %s\n", "x == y");
   
printf("%s\n", "x and y should be equal");
}

Chain macros have the potential to be a robust asset in developing reusable code and minimizing redundancy. However, an excessive or incorrect application of these macros can complicate code comprehension and debugging processes. It is crucial to exercise caution in their utilization and be mindful of the drawbacks they may entail.

Example 2:

Example

#include <iostream>
using namespace std;
 
 
#define INSTAGRAM FOLLOWERS
#define FOLLOWERS 120
 
int main() {
    
   
cout << " This user have "<< INSTAGRAM << "K followers on Facebook!";
 
   
return 0;
}

Output:

2. An object like macros:

In C++, macros serve to establish a series of commands that get executed upon calling the macro. Object-like macros, a category of macros in C++, function by straightforwardly substituting text.

In a macro resembling an object, a particular name is given a value, which could be a direct value, a mathematical expression, or another name. When this name appears in the code, it is substituted with the assigned value.

Here is an example of an object-like macro:

Example

#define PI 3.14159

In this instance, PI serves as the identifier and is initialized with the value 3.14159. Whenever PI is referenced in the code, it will be substituted with 3.14159.

Object-type macros are commonly employed to declare constants or to streamline code by substituting a lengthy expression with a concise label. Nevertheless, they have the potential to introduce errors if not employed cautiously, as they have the capability to alter the interpretation of code in unforeseen manners.

It is crucial to understand that macros resembling objects are a functionality of the preprocessor, indicating that they are processed prior to code compilation. This could result in unforeseen outcomes if the macro is applied in a situation where it is inappropriate, like inside a string or a comment. To mitigate these potential problems, it is advisable to employ const or enum in lieu of macros for constant definitions, as they are assessed by the compiler and provide enhanced type security.

Example:

Example

#include <iostream>
using namespace std;
 
 
#define DATE 25
 
 
int main() {
    
    cout << "The lockdown is extended upto "<< DATE << "-JUNE-2021";
 
    return 0;
}

Output:

3. Function like macros:

Function-like macros in C++ are a category of macros that mimic functions but are specifically a preprocessor functionality offering a mechanism for substituting text. These macros have the ability to accept arguments, and upon invocation, these arguments are substituted with their actual values, which are then inserted into the body of the macro.

Here is an example of a function-like macro:

Example

#define SQUARE(x) ((x) * (x))

SQUARE is the designated macro identifier in this instance, with a parameter x. The body of the macro is ((x) * (x)), which calculates the square of the input x. Upon employing this macro within the code, for instance:

Example

int y = SQUARE(3);

the preprocessor substitutes SQUARE(3) with ((3) * (3)), resulting in nine once the code is compiled.

Function-style macros can also support multiple parameters, such as:

Example

#define MAX(a, b) ((a) > (b) ? (a) : (b))

This macro accepts two parameters, a and b, and outputs the larger value among the two.

Nevertheless, function-style macros have the potential to introduce errors if not employed cautiously, as they have the ability to alter the interpretation of code unexpectedly. An prevalent concern arises from the fact that macro parameters are not subjected to type validation, resulting in unforeseen outcomes when the parameters exhibit side effects or undergo multiple evaluations.

Hence, it is advisable to employ function-like macros sparingly and opt for function templates whenever feasible. Function templates offer a more secure and dependable option, ensuring type safety.

Example:

Example

#include <iostream>
using namespace std;
 
#define min(x, y) (((x) < (y)) ? (x) : (y))
 
 
int main() {
 
    int x = 81;
    int y = 58;
 
    cout << "The minimum value between " << x << " and " << y
        
<< " is: " << min(x, y);
 
    return 0;
}

Output:

4. Multiline macros:

Multiline macros in C++ represent a category of macros that enable the inclusion of multiple lines of code. This feature proves beneficial when the macro definition involves numerous statements or when the definition exceeds the space of a single line.

Here is an example of a multiline macro:

Example

#define DEBUG_LOG(msg) \
   
std::cout << "DEBUG: " << __FILE__ << ":" << __LINE__ << ": " << msg << std::endl

In this instance, DEBUG_LOG represents the macro identifier and accepts a solitary parameter named msg. The macro definition encompasses numerous lines of code, delineated by the \ symbol, signaling the continuation of the macro definition onto the subsequent line.

When the macro is used in the code, for example:

Example

DEBUG_LOG("Hello, World!");
the preprocessor replaces DEBUG_LOG("Hello, World!"); with:
std::cout << "DEBUG: " << __FILE__ << ":" << __LINE__ << ": " << "Hello, World!" << std::endl;

which prints a debug message to the console.

It's crucial to understand that multiline macros have the potential to introduce bugs if not handled with care, as they have the ability to alter the interpretation of code in unforeseen manners. Ensuring uniform indentation and enclosing all macro parameters and lines of code within parentheses is essential to prevent any unanticipated problems related to operator precedence.

It is commonly advised to opt for inline functions over macros when dealing with intricate code due to their improved type safety and easier debugging capabilities. Nonetheless, multiline macros can remain beneficial for uncomplicated code scenarios like debug messages or basic utility functions.

Example:

Example

#include <iostream>
using namespace std;
 
 
#define ABA 1, \
           
2, \
           
3
 
 
int main() {
  
    int arr[] = { ABA };
 
   
cout<<"Elements of Array are:\n";
 
    for (int i = 0; i < 3; i++) {
       
cout << arr[i] << ' ';
    }
 
    return 0;
}

Output:

Common Preprocessor Functions

There are numerous preprocessors accessible within C++ macros. The following section will delve into these in detail.

1. #include:

In C++, the #include directive serves as a preprocessor command utilized to insert the content of a header file into the source code. Upon encountering an #include directive, the preprocessor substitutes the directive with the content of the designated file prior to the compiler initiating the compilation process.

Macros, conversely, serve as a method to declare symbolic constants or snippets of code that the preprocessor can enlarge into more extensive code segments. They are established through the #define command and are commonly employed to streamline code or enhance its clarity.

In certain scenarios, leveraging macros alongside #include can offer advantages. One instance is defining a macro to include a specific header file, then applying this macro at various points within your code. This approach simplifies future modifications to the included files by requiring adjustments solely to the macro definition rather than each separate #include directive.

Here's an example of using a macro with #include:

Example

#define MY_HEADER "my_header.h"
#include MY_HEADER

In this instance, the macro MYHEADER is assigned the value "myheader.h", and subsequently, the #include directive employs the MYHEADER macro to indicate the specific header file for inclusion. This is essentially the same as explicitly stating #include "myheader.h", however, incorporating a macro can enhance the modularity of the code and simplify maintenance tasks.

2. #define:

In C++, #define serves as a preprocessor command utilized for establishing macros, which offer a method for defining symbolic constants or small code snippets that the preprocessor can enlarge into larger code segments. Macros are beneficial for streamlining code, enhancing readability, and preventing code duplication.

A macro definition is composed of the #define directive, which is succeeded by the macro's name and the corresponding value or code it signifies. The structure of the #define directive is outlined below:

Example

#define macro_name replacement_text

Here, the macroname specifies the unique identifier of the macro, while the replacementtext denotes the content or code that the macro represents.

It is crucial to understand that macros undergo expansion by the preprocessor prior to the compilation of the code. This indicates that macros do not form part of the compiled code and are not subject to debugging in the same manner as standard code. Moreover, the utilization of macros may potentially introduce complexity to the code's readability and comprehension. Therefore, it is essential to employ macros judiciously and assign them descriptive names.

3. #undef:

In C++, the #undef command is a preprocessor instruction utilized for deleting a macro definition. The format of the #undef directive is as depicted below:

Example

#undef macro_name

Here, the macro_name represents the specific macro you intend to delete.

The #undef directive becomes handy when you need to eliminate a macro definition that is no longer necessary or when you aim to redefine a macro with an alternative value or code.

It is crucial to understand that macros undergo expansion by the preprocessor prior to code compilation. Consequently, the impact of the #undef directive is solely observable in the preprocessor stage and not during the code's actual compilation process. Moreover, eliminating a macro definition that is currently utilized in the code may lead to compilation issues. Therefore, exercising caution is essential when utilizing the #undef directive.

Input Required

This code uses input(). Please provide values below:

Logic Practice
Install Logic Practice
Add to home screen for a faster app-like experience