Sunday, January 08, 2012

C++ - Safe Guard Header file inclusion using Pragma Once

Pragma Once - Introduction

Header files usually have the declarations required by the definitions. Say for example a class template and its layout is specified in the header file. This layout will just have variable and function names with their scopes. The implementation file actually makes use of the variables and links them with the function implementation.

Including the header file

Header files are referred by the implementation files by using the #include pre-processor directive. The file name extension for the header file is .h and some old C style programmer uses .hpp also.  The implementation file with an extension .cpp refers these header files to know the declarations.  So if we have some common declarations required for more than one implementation groups, we do refer the same header file for both the implementation files.

You can include the header file in two different ways.
1) Using the <>
2) Using the “”

Below are the examples for it:

#include "stdafx.h"
#include <conio.h>
In the below section we will see the difference.


Two types of header file inclusion

The header file specified in between the angle braces <> tells that the header file is part of the c++ libraries. The preprocessor will search for the file first in the IDE specific paths and then in the path specified path the compiler switch /I. This is shown below:

Specifying search path


The project property shows where you can set this /I option.

OK. What about the other option, which is using the header between double quotes? In that case, the compiler will search for the current directory of the file that has the #include statement. When the referred header file is not found, then it searches the file like it did for the <> braces.


Multiple header file inclusions Problem

The #include is the pre-processor and the statement is replaced by the file content before the actual compilation takes place. This leads to a situation of getting declaration or definition twice in a file say ab.cpp when is includes a header file that includes the one more header file which used by the file ab.cpp already.

To explain this, let us go with a simple example.

SimpleMath.h

Below is the content of the file and no explanation is required her as it is a two simple function declaration and its implementation.

int Add_Numbers(int, int);
int Mult_Numbers(int, int);

int Add_Numbers(int a, int b)
{
 return (a + b);
}

int Mult_Numbers(int a, int b)
{
 return (a * b);
}

ExtendedMath.h

In this header file, the basic mathematic function to add two numbers is extended to support adding three numbers. In this file, the function to add three numbers is implemented by making use of the function that adds two numbers, which is already defined in the SimpleMath.h. So ExtendedMath.h header file includes the simplemath.h header file. This file content is shown below:

#include "SimpleMath.h"

int Add_three_numbers(int, int, int);

int Add_three_numbers(int a, int b, int c)
{
 return ( Add_Numbers(a, b) + c );
}

Explanation

When the pre-processing (before the compilation) takes place, the #include is replaced with file content by the compiler. Also, note that the compiler will not generate any object code for the header file say SimpleMath.obj, extendedmath.obj

CppTest.cpp

Do you got confused in the previous section when I said compiler will not generate any object code for the header file say SimpleMath.obj, extendedmath.obj? And you may have a question that each header file provided some processing that computes multiplication as well as addition. Where is the object code? And How do the linker will create the exe for those functions?

Right! The compiler will generate object code for the CPP files. When the CPP file includes the header files, then the file has the replaced content of the header and that replaced content will go into an object file and linker will generate the exe. Look at the code for the CPP file:

// CPPTST.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include "SimpleMath.h"
#include "ExtendedMath.h"
#include <conio.h>
int _tmain(int argc, _TCHAR* argv[])
{
 return 0;
}

When you compile the above file or the project, which has this cpp file, you will get the error shown below:
C2084 Compiler Error


Why?

As I already told, #include is the pre-processor directive, and the compiler will replace the content of header file before processing the cpptest.cpp. When it does the file looks like as shown below (I am skipping the stdafx.h, conio.h):

int Add_Numbers(int, int);
int Mult_Numbers(int, int);

int Add_Numbers(int a, int b)
{
            return (a + b);
}

int Mult_Numbers(int a, int b)
{
            return (a * b);
}

int Add_Numbers(int, int);
int Mult_Numbers(int, int);

int Add_Numbers(int a, int b)
{
            return (a + b);
}

int Mult_Numbers(int a, int b)
{
            return (a * b);
}

int Add_three_numbers(int, int, int);

int Add_three_numbers(int a, int b, int c)
{
            return ( Add_Numbers(a, b) * c );
}
int _tmain(int argc, _TCHAR* argv[])
{

            return 0;
}

The first set of code in red color is the content of simplemath.h
The second set of code in Green Normal font style is also the content of simplemath.h (Note that extendedmath.h includes the simplemath.h)
The stuff is green bold is actual content of the extendedmath.h
The final Red bold text is the original content of the CPP file.
Now once all the #include s are replaced, the compiler check return an error and now I no need to explain that error as you can understand it now.


Avoiding multiple inclusions – A

We can avoid these multiple inclusion problems in two different ways. First, we will look at the conditional inclusion of pre-processor statements. To do this we should use the #define in combination with #ifndef.  This is shown below. The #define statement informs the compiler that marks a macro called TAG and knows that it is defined or set for use. So the header file content is placed in between the #ifndef and #endif with a very first statement that defines the macro tested by the #ifndef.

Safe Guarding the Header file


First, think that compiler comes to header file when it is referred by the source file(.cpp). Keeping that in mind, now follow the description given below for the above illustration:
1) The compiler first checks that the TAG is not already defined.
2) When it is already defined none of the header file content is included in the referring source file
3) When it is not defined, it first defines the preprocessor tag TAG. The scope of this TAG is until the generation of object file for a referring (#include ‘ing source file)

Now look at the change for SimpleMath.h header file as well as the ExtendedMath.h header file.

#ifndef _SIMPLEMATH_H__
#define _SIMPLEMATH_H__

int Add_Numbers(int, int);
int Mult_Numbers(int, int);

int Add_Numbers(int a, int b)
{
 return (a + b);
}

int Mult_Numbers(int a, int b)
{
 return (a * b);
}
#endif

#ifndef _EXTMATH_H_
#define _EXTMATH_H_
#include "SimpleMath.h"

int Add_three_numbers(int, int, int);
int Add_three_numbers(int a, int b, int c)
{
 return ( Add_Numbers(a, b) * c );
}

#endif

Now the compilation goes successful as the #include SimpleMath.h inside the ExtendedMath.h is skipped by the compiler when it is referred from the CppTest.Cpp

Avoiding multiple inclusions – B

Oh! That’s simple. You no need to use the #define, #ifndef & #endif set of directives. Instead of that, we should just use #pragma once pre-processor directive. And it will take care of everything. Below is sample for extendedmath.h


#pragma once
#include "SimpleMath.h"
int Add_three_numbers(int, int, int);

int Add_three_numbers(int a, int b, int c)
{
 return ( Add_Numbers(a, b) * c );
}

Closing notes

When going through the example you may be thinking, why we included SimpleMath.h and ExtendedMath.h in our Cpp file? Without using any pre-processor macro I can solve the problem just by including the extendedmath.h.

If you thought like that, I can say you are right. That definitely that solves the problem with a cost of the person who makes use of our simple and extended math should have the knowledge of the header file. Also, designing a header file not considering multiple inclusions is not a good habit for c++ coders.

No comments:

Post a Comment

Leave your comment(s) here.

Like this site? Tell it to your Firend :)