Developing a C++ Header
Writing a C++ Application – Part 1
Forward: In this part of the series I explain the development of a C++ header.
By: Chrysanthus Date Published: 7 Feb 2013
Introduction
My volume, C++ Course (Taking the Bull by the Horns) is divided into two portions. The first portion is the professional course; the second portion is the advanced course to make you an expert. The second portion is divided further into two portions. The first portion here deals with C++ core coding. The second portion deals with the C++ standard library and how to produce a library of your own. I hope you complete the volume, continuing from here.
Back to the story at hand: Originating a C++ Header.
Prerequisite
This series is part of my C++ Course volume. So you should have completed all the series before this one, in the volume. This series is a continuation. To know what you should have completed just click the link, “C++ Course” below.
Application
An application in C++, is an executable file compiled from a number of C++ files. Among these files, one of them has the C++ main function. Assuming that you have the files, fileA.cpp, fileB.cpp, and fileC.cpp in your working directory to compile, the command prompt line to type for the MinGW compiler is:
g++ fileA.cpp fileB.cpp fileC.cpp -o appFile.exe
where appFile.exe is the compiled resulting executable file (application). The portion, appFile of appFile.exe is a name of your choice. With this command, the appFile.exe file will be created and saved automatically in the working directory.
A different compiler command line will be similar. All the files (1 file, 2 files, 3 files, 4 files, etc.) are separated by space and typed in front of the switch, -o , - is the minus sign and not some decorated hyphen. The letter just after - is the lowercase O and not zero. Some of these files may consist of C++ entities, others may have programs. However, it is the last one near -o that has the C++ main function.
The above command is the official way to join files. Files are joined at compile time. Another way to join files is to use the #include preprocessing directive. This second way has limitation as I explain later in the series. I advise you to use the #include preprocessing directive mainly for header files (see later).
Entities
The following are C++ entities: object, class, array, fundamental object, function, enumerator, template, macro, pointer, and namespace.
I start with two files to illustrate the principles of this tutorial. One of the files is called, sampleEntities.cpp. It has samples of the following entities: fundamental double object, a macro, an array, a function, an enumeration, a namespace, a class and an instantiated object. I like to call an instantiated class an instantiated object, to distinguish it from a fundamental object.
The other file is called mainfile.cpp. This file has the main function. The main function has the function call to the function in the sampleEntities.cpp. This main file should have the prototype of the function of the sampleEntities.cpp file. This main file is actually very simple. It should have just two code segments. The first one is just one statement, which is the prototype of the function of the sampleEntities.cpp file. The second code segment is the main function, which has only a call to the function of the sampleEntities.cpp file.
The content of the sampleEntities.cpp file is:
#include <iostream>
using namespace std;
const double pi = 3.141592;
#define myConstStr "I love you."
enum DayNumber {today, tomorrow, afterTomorrow, theDayAfterTomorrow};
DayNumber theDay = afterTomorrow;
class Calculator
{
public:
int num1;
int num2;
Calculator(int ident1, int ident2)
{
num1 = ident1;
num2 = ident2;
}
int add()
{
int sum = num1 + num2;
return sum;
}
};
Calculator::Calculator myObject(2,3);
int arr[10];
namespace premier
{
#define e 2.718281
int ident = 33;
void fnA();
}
void fn()
{
cout << pi << "\n";
cout << myConstStr << "\n";
arr[0] = 25;
cout << arr[0] << "\n";
cout << theDay << "\n";
cout << myObject.add() << "\n";
using namespace premier;
cout << e << "\n";
cout << ident << "\n";
}
The content of the mainfile.cpp file is:
int main()
{
fn();
return 0;
}
Read through the content of the sampleEntities.cpp file. It begins with the lines:
#include <iostream>
using namespace std;
You cannot really have the first line “#include <iostream>”, without the other line. This is because the declarations of the class, iostream are inside the std namespace – more on this later. The cout object, which sends data to the output console, is declared in the iostream class – more on that later. This object is used in the fn() function defined in the sampleEntities.cpp file and that is why the two lines are there. The function entity, fn() of the file displays values of the rest of the entities. The namespace entity, has the declaration of a macro (constant), an int fundamental object, and a function. Read through the content of the file above to identify the entities and how the fn() function operates.
Right now, the mainFile.cpp file has just the main function with a call to the function, fn() in the other file, sampleEntities.cpp.
Type the content of the sampleEntities.cpp file in a text editor. Create a directory called, dir1 in your root directory. Save the content typed with the name, sampleEntities.cpp, in the dir1 directory. I am using the command prompt (DOS) of the Windows operating system, so my directory path for this is, c:\dir1. Type the content of the mainfile.cpp file and save it in your C++ working directory. Mine is C:\MinGW; I use the MinGW compiler (suite).
g++ c:\dir1\sampleEntities.cpp mainFile.cpp -o mainFile.exe
This is the command line for the MinGW compiler that I use. If you try this command, you would receive an error message saying that the fn() function was not declared in the file scope of the mainFile.cpp file; as a result, the application does not compile. The syntax of this command is OK; there is nothing wrong with the command: You have c:\dir1\ because the sampleEntities.cpp file is in the dir1 directory. mainFile.cpp is not preceded by any directory path because it is in the working directory; if it were not in the working directory, then it would have to be preceded by the relevant directory path. Also, if you want the application (executable) file to be saved in a different directory, then precede the name with the relevant directory path.
At this point, if you try the above command, you would receive an error message. Note that the function, fn() is defined in the sampleEntities.cpp file and not in the mainFile.cpp file that has the main function. The solution for successful compilation is simple: just type the “void fn();” declaration (without the quotes), at the top of the mainFile.cpp file.
Remember, the error message is that the fn() function has not been declare in the file scope of the mainFile.cpp file. So the simple solution above works. With that the compiler will use the declaration to look for the full definition in the sampleEntities.cpp file.
The file scope is the global scope. Know that the above command line does not unite the file scopes (does not form one overall global scope for the files). It is the #include directive that forms an overall global scope with files. However, you are advised to use the #include directive mainly for header files (see later), which still does not really form an overall global scope. Joining of files has to use a different mechanism through the header files.
So, modify the mainFile.cpp file to obtain the following and compile the application with the above command line.
void fn();
int main()
{
fn();
return 0;
}
You should now have the file, mainFile.exe, in your working directory. The application has compiled. To test it just type the following command at the prompt of the working directory and press Enter:
mainFile.exe
The top portion of any file is the header of the file. In C++, this portion normally has special instructions. These special instructions together, are also called, the header. If these instructions are placed in a different file and then integrated (brought) into the file in question, with the #include preprocessing directive, then the different file becomes the header file.
For the compiled application above, the header for the mainFile.cpp file is:
void fn();
and the header for the sampleEntities.cpp file is:
#include <iostream>
using namespace std;
Useful Header
A useful header, simply called, header, consists of declarations of some C++ entities. In the compiled application above, the compiler uses the declaration of “void fn();” at the header of the mainFile.cpp file to look for the full definition of the fn() function in any other file in the compile command line, to use in the mainFile.cpp file. It sees the full definition in the sampleEntities.cpp file, and uses it in the application. The calls within the fn() function body are to entities in the sampleEntities.cpp file; there is no problem with that.
So, if the definition of an entity and its call are in the same file, there is no problem there, and the file is compiled. However, if the call is in one file, be it the file with the main function or not, and the entity is in a different file, the declaration of the entity has to be in the file that has the call (like “void fn();”) above. Well, this rule does not apply to all entities; that is C++.
Developing the Header
In the above compiled application, only the entity, fn() of the sampleEntities.cpp file was used directly in the mainFile.cpp file. If you want to use more entities directly in the mainFile.cpp file, you have to type the declarations of the entities in the header of the mainFile.cpp file. If you do not do that, the compiler will complain that the entity (or entities) you want to use directly has (have) not been declared in the mainFile.cpp file scope. For convenience, this same header of the mainFile.cpp file is placed in the sampleEntities.cpp file, without double typing of the declarations.
With time more files may be added to an application, and the added files would need to use the entities of the sampleEntities.cpp file directly. In this case, the header acquired will also be placed in the added files. Since the header is the same for all the files, it is more convenient to type the header in a separate file of its own, with the file extension, .hh. For the .cpp files of the application, the header file is included with the #include preprocessing directive. When included, it would be the same as if it were typed in the .cpp files.
Let us now develop a header from the sampleEntities.cpp file. Remember, for convenience the header will also stay in the sampleEntities.cpp file. This header will be the one used for all the files in the application. It will ultimately reside in its own file called, demo.hh.
To obtain the header, you look for the declarations of the entities and placed them up (top) in the file (sampleEntities.cpp). At the end there should be no double tying of the declarations. Again, the declarations of all entities would not work in the common header. So, we obtain the declarations, entity by entity. The pi statement is:
const double pi = 3.141592;
The declaration stays the same. This will just be raised to the header in its entirety.
For the macro, myConstStr, the declaration stays the same, that is:
#define myConstStr "I love you."
raised to the header in its entirety. For the enum entity, the declaration is:
enum DayNumber {today, tomorrow, afterTomorrow, theDayAfterTomorrow};
This declaration goes to the header leaving the instantiation of theDay below in the body of the file. For the class (description), the declaration is:
class Calculator;
However, since the class has properties and methods (not empty braces), you need but the synopsis for the header. The synopsis is:
class Calculator
{
public:
int num1;
int num2;
Calculator(int ident1, int ident2);
int add();
};
With this synopsis, the normal class description (definition) in the sampleEntities.cpp file above, will not be typed. In its place, you will have the definition of the constructor and the add() member functions (see below).
You can have an instantiated object in a header, but you would have to precede it with the specifier, static. I prefer to call instantiated class, instantiated object, to differentiate it from fundamental object. So the declaration of myObject(2,3) in the file would be modified to the following and raised (in its entirety) to the top of the file, just below the class synopsis:
static Calculator::Calculator myObject(2,3);
The namespace does not have a declaration suitable for the header. However, header items can go into a namespace.
The function declaration is the greatest candidate for the header. In this application, just type the prototype at the top of the sampleEntities.cpp file and define the function in a relevant position below in the file. The prototype for the above function is:
void fn();
With the function declaration at the header, the function definition is typed in the normal way without any change, as opposed to class member functions. This rule is broken when the header is in a namespace.
Rewrite the content of the sampleEntities.cpp file as follows:
#include <iostream>
using namespace std;
const double pi = 3.141592;
#define myConstStr "I love you."
enum DayNumber {today, tomorrow, afterTomorrow, theDayAfterTomorrow};
class Calculator
{
public:
int num1;
int num2;
Calculator(int ident1, int ident2);
int add();
};
static Calculator::Calculator myObject(2,3);
void fn();
DayNumber theDay = afterTomorrow;
Calculator::Calculator(int ident1, int ident2)
{
num1 = ident1;
num2 = ident2;
}
int Calculator::add()
{
int sum = num1 + num2;
return sum;
}
int arr[10];
namespace premier
{
#define e 2.718281
int ident = 33;
void fnA();
}
void fn()
{
cout << pi << "\n";
cout << myConstStr << "\n";
arr[0] = 25;
cout << arr[0] << "\n";
cout << theDay << "\n";
cout << myObject.add() << "\n";
using namespace premier;
cout << e << "\n";
cout << ident << "\n";
}
Read through the code. Now save the modified file and compile the application with the following command line (or similar) again:
g++ c:\dir1\sampleEntities.cpp mainFile.cpp -o mainFile.exe
The application (assembled and executable file) should have compiled, with no error message. To test it, use the command line:
mainFile.exe
The header in the sampleEntities.cpp file begins from the line, “#include <iostream>” and ends at the line, “void fn();”. Note again that in the file, there is only one myObject(2,3) object declaration, which is now at the header. Also note that the class code now consists of synopsis at the header and member functions implementation in the file body. Our header (our created header) of interest should not have the first two lines; the header is:
const double pi = 3.141592;
#define myConstStr "I love you."
enum DayNumber {today, tomorrow, afterTomorrow, theDayAfterTomorrow};
class Calculator
{
public:
int num1;
int num2;
Calculator(int ident1, int ident2);
int add();
};
static Calculator::Calculator myObject(2,3);
void fn();
Now, copy this header of interest into the mainFile.cpp file at the top, replacing the single line header, “void fn();”. Note that “void fn();” is still in the new header. Do not touch the main function. The mainFile.cpp file does not need the first two lines of the sampleEntities.cpp file because it does not use the cout output object.
So the two .cpp files now have the same header (of interest). The code below the header now in the sampleEntities.cpp file depends on its header for its declarations: the enum and class code entities are examples; even the function, fn() is a good example, but not very obvious (since in sampleEntities.cpp the definition of fn() can work without its prototype declaration at the top).
The reason the header of the sampleEntities.cpp file has also been placed in the mainFile.cpp file, is because the mainFile.cpp file may want to use more of the entities (not only fn()) from the sampleEntities.cpp file to carry out a task entirely new in future, not found already in either of the files. In this case, the header in the mainFile.cpp file will be the connection to the entities in the sampleEntities.cpp file. You can add more files to the application that would use the entities of the sampleEntities.cpp file. In that case, you have to type the same header of interest in all the added files (at the top). In each file, the header will be the connection to the sampleEntities.cpp file. If it happens that an added file does not need the entities of the sampleEntities.cpp file, then it should not have the header of interest.
Now save the two files if you have not already done so, then compile the application consisting of the above two files with the following command line (or similar):
g++ c:\dir1\sampleEntities.cpp mainFile.cpp -o mainFile.exe
The application should compile. Test the application with the following command:
mainFile.exe
You should see the output of the fn() function defined in the sampleEntities.cpp file. Apart from the “void fn();” declaration in the mainFile.cpp file, the rest of the declarations are not used in the mainFile.cpp file. However, they may be used any time, in future, in the file.
It has been a long ride, for this tutorial. You should have understood the meaning of a C++ header. We take a break here and continue in the next part of the series.
Chrys
Related Courses
C++ CourseRelational Database and Sybase
Windows User Interface
Computer Programmer – A Jack of all Trade – Poem
NEXT