simples and Conditional Macros and Its exapansions

1. The Concept of C Macros

Macros are generally used to define constant values that are being used repeatedly in program. Macros can even accept arguments and such macros are known as function-like macros. It can be useful if tokens are concatenated into code to simplify some complex declarations. Macros provide text replacement functionality at pre-processing time.
Here is an example of a simple macro :
 #define MAX_SIZE 10
The above macro (MAX_SIZE) has a value of 10.
Now let’s see an example through which we will confirm that macros are replaced by their values at pre-processing time. Here is a C program :
#include<stdio.h>

#define MAX_SIZE 10

int main(void)
{
   int size = 0;
   size = size + MAX_SIZE;

   printf("\n The value of size is [%d]\n",size);

   return 0;
}
Now lets compile it with the flag -save-temps so that pre-processing output (a file with extension .i ) is produced along with final executable :
$ gcc -Wall -save-temps macro.c -o macro
The command above will produce all the intermediate files in the gcc compilation process. One of these files will be macro.i. This is the file of our interest. If you open this file and get to the bottom of this file :
...
...
...
int main(void)
{
   int size = 0;
   size = size + 10;

   printf("\n The value of size is [%d]\n",size);

   return 0;
}
So you see that the macro MAX_SIZE was replaced with it’s value (10) in preprocessing stage of the compilation process.
Macros are handled by the pre-compiler, and are thus guaranteed to be inlined. Macros are used for short operations and it avoids function call overhead. It can be used if any short operation is being done in program repeatedly. Function-like macros are very beneficial when the same block of code needs to be executed multiple times.
Here are some examples that define macros for swapping numbers, square of numbers, logging function, etc.
#define SWAP(a,b)({a ^= b; b ^= a; a ^= b;})
#define SQUARE(x) (x*x)
#define TRACE_LOG(msg) write_log(TRACE_LEVEL, msg)
Now, we will understand the below program which uses macro to define logging function. It allows variable arguments list and displays arguments on standard output as per format specified.
#include <stdio.h>
#define TRACE_LOG(fmt, args...) fprintf(stdout, fmt, ##args);

int main() {
int i=1;
TRACE_LOG("%s", "Sample macro\n");
TRACE_LOG("%d %s", i, "Sample macro\n");
return 0;
}
Here is the output:
$ ./macro2
Sample macro
1 Sample macro
Here, TRACE_LOG is the macro defined. First, character string is logged by TRACE_LOG macro, then multiple arguments of different types are also logged as shown in second call of TRACE_LOG macro. Variable arguments are supported with the use of “…” in input argument of macro and ##args in input argument of macro value.

2. C Conditional Macros

Conditional macros are very useful to apply conditions. Code snippets are guarded with a condition checking if a certain macro is defined or not. They are very helpful in large project having code segregated as per releases of project. If some part of code needs to be executed for release 1 of project and some other part of code needs to be executed for release 2, then it can be easily achieved through conditional macros.
Here is the syntax :
#ifdef PRJ_REL_01
..
.. code of REL 01 ..
..
#else
..
.. code of REL 02 ..
..
#endif
To comment multiples lines of code, macro is used commonly in way given below :
#if 0
..
.. code to be commented ..
..
#endif
Here, we will understand above features of macro through working program that is given below.
#include <stdio.h>

int main() {

#if 0
printf("commented code 1");
printf("commented code 2");
#endif

#define TEST1 1

#ifdef TEST1
printf("MACRO TEST1 is defined\n");
#endif

#ifdef TEST3
printf("MACRO TEST3 is defined\n");
#else
printf("MACRO TEST3 is NOT defined\n");
#endif

return 0;
}
Output:
$ ./macro
MACRO TEST1 is defined
MACRO TEST3 is NOT defined
Here, we can see that “commented code 1”, “commented code 2” are not printed because these lines of code are commented under #if 0 macro. And, TEST1 macro is defined so, string “MACRO TEST1 is defined” is printed and since macro TEST3 is not defined, so “MACRO TEST3 is defined” is not printed.

2. The Concept of C Inline Functions

Inline functions are those functions whose definition is small and can be substituted at the place where its function call is made. Basically they are inlined with its function call.
Even there is no guarantee that the function will actually be inlined. Compiler interprets the inline keyword as a mere hint or request to substitute the code of function into its function call. Usually people say that having an inline function increases performance by saving time of function call overhead (i.e. passing arguments variables, return address, return value, stack mantle and its dismantle, etc.) but whether an inline function serves your purpose in a positive or in a negative way depends purely on your code design and is largely debatable.
Compiler does inlining for performing optimizations. If compiler optimization has been disabled, then inline functions would not serve their purpose and their function call would not be replaced by their function definition.
To have GCC inline your function regardless of optimization level, declare the function with the “always_inline” attribute:
void func_test() __attribute__((always_inline));
Inline functions provides following advantages over macros.
  • Since they are functions so type of arguments is checked by the compiler whether they are correct or not.
  • There is no risk if called multiple times. But there is risk in macros which can be dangerous when the argument is an expression.
  • They can include multiple lines of code without trailing backlashes.
  • Inline functions have their own scope for variables and they can return a value.
  • Debugging code is easy in case of Inline functions as compared to macros.
It is a common misconception that inlining always equals faster code. If there are many lines in inline function or there are more function calls, then inlining can cause wastage of space.
Now, we will understand how inline functions are defined. It is very simple. Only, we need to specify “inline” keyword in its definition. Once you specify “inline” keyword in its definition, it request compiler to do optimizations for this function to save time by avoiding function call overhead. Whenever calling to inline function is made, function call would be replaced by definition of inline function.
#include <stdio.h>

void inline test_inline_func1(int a, int b) {
    printf ("a=%d and b=%d\n", a, b);
}

int inline test_inline_func2(int x) {
    return x*x;
}

int main() {

    int tmp;

    test_inline_func1(2,4);
    tmp = test_inline_func2(5);

    printf("square val=%d\n", tmp);

    return 0;
}
Output:
$ ./inline
a=2 and b=4
square val=25

No comments:

Post a Comment