C++ Constant Expressions Vs Macros

C++ Constant Expressions Vs Macros 

In this post, we will discuss C++ Constant Expressions( const expr) and how they replace traditional  Macros(#define).

  1. Macros in C/C++

  • Macro is a Preprocessor directive in C/C++ used to define expressions with names. 
  • Before compilation, the Preprocessor will search for the name in the entire code and replace it with the expression.
  • Below is an example of how to define a macro identifier with a value

#include<stdio.h>

//here are defining a macro
#define PI 3.14
int main() {
    int radius = 5;
    /*
    * In the below code Preprocess replace PI with 3.14 before compilation
    */
    int area_of_circle = PI*radius*radius;
    printf("Area is : %d\n",area_of_circle);
    int perimeter = 2*PI*radius;
    printf("Perimeter  is : %d\n\n",perimeter);
    return 0;
}

  • Below is an example of how to define a macro expression 
  • #include<stdio.h>
    
    //her are defining a macro expression
    #define GREATER(x,y) (x > y ? x :y)
    int main() {
        int radius = 5;
        /*
        * In below code Preprocess will replace GREATER(a,b) with (a > b ? a :b) before compilation.
        */
        int a = 100;
        int b = 509;
        printf("greater among %d, %d  is %d \n", a,b, GREATER(a,b));
        return 0;
    }
    

  • There will be no memory allocated for Macro identifiers or expressions.
  • Preprocessor parses the code before compilation and will replace the macro name with expression/identifier without any validation. Which may lead to compile or runtime errors.
  • The Below diagram explains the Preprocessing step during code building




  • 1.1 Problems with Macros

    • As we know Preprocessor will replace all occurrences of Macro with expression/value blindly, which will lead to compile errors or runtime errors.
    • Macros are also insecure, because text replacement may lead to malicious code that can corrupt the system on which it is running. 
    • Check the below code which is an example of issues with Macro. Read the comments inside the code to understand better.                    

    #include <stdio.h>
    
    // Define a Structure
    struct MyStruct
    {
        int data1;
        short data_short;
        char letter;
    };
    
    //Define a Macro used to declare pointers to MyStruct.
    //When the Preprocessor finds the string "MY_STRUCT_PTR" it will be replaced by the string "struct MyStruct*"
    #define MY_STRUCT_PTR struct MyStruct*
    
    int main() {
        struct MyStruct data = {.data1 = 100, .data_short = 10, .letter ='x'};
        /*
        * In the below line we want to create 2 pointers of struct MyStruct
        * But after Preprocessor replace macro name "MY_STRUCT_PTR" with expression "struct MyStruct*", 
        * the final declaration will look like this below
        *     MY_STRUCT_PTR data1, data2;   --------> struct MyStruct *data1, data2;
        * We can observe the "data1" is pointer but "data2" is normal object
        */
        MY_STRUCT_PTR data1, data2; 
        data1 = &data;
        // In the below line we will get a compile error because we are assigning a normal object to a pointer
        data2 = &data;
        return 0;
    }
    

    2. Constant Expressions in C++

    • Constant expressions are introduced in C++11
    • We can define variables and expressions with Constant expressions that are evaluated at the time of compilation.
    • Like Macros there will be no memory allocated for Constant expressions.
    • Constant expressions are also like variables or functions in C++ 
    • Unlike Macros there will be no text replacement mechanism used to evaluate Constant expressions. Hence there will be no hazards of unexpected compile or runtime errors.
    • There are 2 types of Constant expressions
      1. Constant expression variables
      2. Constant expression functions

    2.1 Constant expression variables

    • We can declare constant expression variables so that the compiler will replace all occurrences of variable names in code with their value.
    • Below is an example for constant expression variables, read comments inside the code for better understanding.  
    #include <iostream>
    
    //define a constant expression for a packet lengths
    constexpr int header_length = 10;
    constexpr int footer_length = 2;
    constexpr int payload_length = 2;
    constexpr int total_pkt_length = header_length + footer_length + payload_length;
    
    int main() {
        
        /*
        * In the below code "total_pkt_length" value will be computed at compile time and the resultant 
        * expression will be like below
        *   char packet[14] = {0};
        */
        char packet[total_pkt_length] = {0};
        return 0;
    }
    

    2.2 Constant expression functions

    • Constant expression functions will be evaluated at compile time to return a result.
    • Compiler will replace all occurrences of function names in the code with its corresponding return value. 
    • We can't pass normal variables as arguments while calling a constant expression function. Only variables declared as constexpr are allowed.
    • After C++14 it is allowed to pass normal variables as arguments but then the function will converted to a normal inline function.
    • Below is an example of a constant expression function, read comments inside the code for better understanding. 
    #include <iostream>
    
    using namespace std;
    
    /*
    * Below is the Constant expression function that converts temperature in Fahrenheit to Celsius
    * Call to this function will be evaluated at runtime and replaced with the returned value
    */
    constexpr int convert_fh_to_celsius(const int &temp_fh) {
        return (temp_fh - 32)*5/9;    
    }
    
    int main() {
        constexpr int min_temp_in_fh = 50;
        /*
        * In the below code function "convert_fh_to_celsius" will called at compile time and return value will be replaced.
        * Below statement will be converted as 
        *      cout<<"Minimum temperature in celsius: "<<10<<endl; 
        * 50 degree Fahrenheit is 10 degree in Celsius
         */
        cout<<"Minimum temperature in celsius: "<< convert_fh_to_celsius(min_temp_in_fh)<<endl;
        constexpr int max_temp_in_fh = 120;
        /*
        * Same as above below statement will be coverted as 
        *       cout<<"Maximum temperature in celsius: "<< 48<<endl;
        * 100 degree Fahrenheit is 48 degree in Celsius
         */
         cout<<"Maximum temperature in celsius: "<< convert_fh_to_celsius(max_temp_in_fh)<<endl;
        return 0;
    }
    

    Hope you will start using Constant expressions instead of Macros in your projects✌

    Comments

    Post a Comment

    Popular posts from this blog

    C++ Virtual Functions uses and Limitations

    Importance of Unit Testing