Using Macro in C/C++ (Advanced Use of #define)

Argument substitution ( # and ## ) in #define


  • Convert argument to string
    • The argument followed by a # will be converted to a string. Ex: 
//Ex:1

#define make_str (param) #param

char *p=make_str(Hello World);
//will be converted after pre-processing
char *p="Hello World";

//Ex:2

#define concat_str(str1,str2) #str1 " " #str2
char *p2= concat(Hello, World);
//will be converted after pre-processing
char *p2= "Hello" " " "World";  // same as char* p2="Hello World";

//Ex:3

#define print_int(x) printf("value of "#x" is %d",x)

int a=5;
print_int(a); //output: value of a is 5

  • Macro symbol expansion
    • A ## preprocessing parameter shall not occur either end of macro expansion.
    • A parameter is immediately preceded or followed by a ## token, will be replaced by  the corresponding argument’s preprocessing token sequence
//Ex:1

#define MY_FUNCTION ( name ) fn_##name

int MY_FUNCTION( add )(int a, int b);

// will be replaced by 
int fn_add(int a, int b);




Structures and its operation by using macro


structures are very useful in c/c++, it provides a way to define users own complex data type. For example

struct student
{
    char first_name[50];
    char last_name[50];
    int age;
} ;

int main()
{ 
    struct student s[]={{"Amit","Kumar",16},{"Santosh","Kumar",18},{"Rahul","Singh",15}}; 
    for(int i=0;i<sizeof(s)/sizeof(struct student);i++) 
    { 
         printf("first_name:%s,last_name:%s,age:%d\n",s[i].first_name,s[i].last_name,s[i].age); 
    } 
}

In the above example student is a structure type which have three members first_name (char type), last_name (char type) and age (int type).

Now since structure's fields are not constant, its varies between different structures, like in the above example structure student have three fields, but some other structure say employee may have different fields. Because of this nature it is difficult to create common function that can handle multiple structures. For example, suppose one function student* duplicate_student(student* src_p); that takes a student structure's pointer and returns a duplicate structure which will have the same value as source pointer. But it is not possible to get a duplicate employee pointer by passing a pointer of structure employee to the same function duplicate_student.
After implementing the structure and its corresponding function if structure's member needs to be altered then everywhere changes will be required, that is in structure definition, its corresponding functions and other related operations.

By using macro one can avoid this problem, let see how we can do this by using macro...

Lets create a struct.def file and define all the related structure's name and it's members as shown below.

struct.def


/* C_STRING,INTEGER and create_struct is implementation dependent which is defined in cpp files, if not defined then throw error*/
#ifndef C_STRING
#error "C_STRING not defined"
#endif
#ifndef INTEGER
#error "INTEGER not defined"
#endif
#ifndef create_struct
#error "create_struct not defined"
#endif

/* create_struct(structure_name,
       structure_members
) */
create_struct(student,
        C_STRING(first_name,50)
        C_STRING(last_name,50)
        INTEGER(age)
)

create_struct(employee,
        C_STRING(first_name,50)
        C_STRING(last_name,50)
        INTEGER(age)
        INTEGER(salary)
        C_STRING(address,100)
        C_STRING(contact_no,20)
)

/* define other related structure here */

/*finally undefine all the defined symbol*/
#undef C_STRING
#undef INTEGER
#undef create_struct

Now, create a cpp file called macro_struct.cpp that includes struct.def and generates structures and its related functions  (after preprocessing) by using struct.def

macro_struct.cpp

#include "stdio.h"
#include "string.h"

/* create structure definition */
#define C_STRING(var_name,size) char var_name[size];
#define INTEGER(var_name) int var_name;

#define create_struct(struct_id,data_type)\
typedef struct struct_id##_tag{\
    data_type\
}struct_id;
#include "struct.def"

/* create a function named duplicate_XYZ which will create a duplicate structure */
#define C_STRING(var_name,size) strcpy(dst_p->var_name,src_p->var_name);
#define INTEGER(var_name) dst_p->var_name=src_p->var_name;
#define create_struct(struct_id,data_type)\

struct_id* duplicate_##struct_id(struct_id* src_p)\
{\
    struct_id* dst_p=new struct_id();\
    data_type\
    return dst_p;\
}
#include "struct.def"

/* implementation of structure for testing */

int main()
{
    student s[]={{"Amit","Kumar",16},{"Santosh","Kumar",18},{"Rahul","Singh",15}};
    for(int i=0;i<sizeof(s)/sizeof(student);i++)
    {
        printf("first_name:%s,last_name:%s,age:%d\n",s[i].first_name,s[i].last_name,s[i].age);
    }
}

After execution of pre-processor the code will be:

typedef struct student_tag{ 
        char first_name[50]; 
        char last_name[50]; 
        int age; 
}student;

typedef struct employee_tag{ 
        char first_name[50]; 
        char last_name[50]; 
        int age; 
        int salary;
        char address[100];
        char contact_no[20];
}employee;

student* duplicate_student(student* src_p) {
        student* dst_p=new student();
        strcpy(dst_p->first_name,src_p->first_name);
        strcpy(dst_p->last_name,src_p->last_name);
        dst_p->age=src_p->age;
        return dst_p;
}
employee* duplicate_employee(employee* src_p)
{
        employee* dst_p=new employee();
        strcpy(dst_p->first_name,src_p->first_name);
        strcpy(dst_p->last_name,src_p->last_name);
        dst_p->age=src_p->age;
        dst_p->salary=src_p->salary;
        strcpy(dst_p->address,src_p->address);
        strcpy(dst_p->contact_no,src_p->contact_no);
        return dst_p;
}

/* implementation of structure for testing */

int main()
{
    student s[]={{"Amit","Kumar",16},{"Santosh","Kumar",18},{"Rahul","Singh",15}};
    for(int i=0;i<sizeof(s)/sizeof(student);i++)
    {
        printf("first_name:%s,last_name:%s,age:%d\n",s[i].first_name,s[i].last_name,s[i].age);
    }
}

You can see in the above code that if create_struct(student,member_list) is defined in struct.def then its corresponding structure definition and a function duplicate_student is is generated. In the same way code is also generated for employee because it is defined in struct.def.
Now suppose after implementation, later if you  want to add one more filed say student_id in student structure then you need to add only in student member_list in struct.def file instead of adding the member in all the places.

No comments :

Post a Comment