C++ Modular Programming & Compile Multi File Project

Discussion in 'C++' started by BiplabKamal, May 14, 2016.

  1. In the previous chapter we saw how to compile a C++ source code in a single file. The extension of the source code file name should be .cpp (*.cpp). We also included other files inside the cpp file. While we can include any text file with any name but the standard is to include header files which only contains declaration and not the definition. These header files names have the extension .h (*.h). You can also compile multiple .cpp files and create a single executable. Compilers will compile each file to an object file(*.o) and the linker will link all the object files in the executable. This enables you to divide your program in logical modules across different files. This approach is called modular programming. Modular programming helps to divide complexity and manage it separately. The main tool of modular programming is dividing the whole program into multiple functions instead of implementing everything in a single function. Different functions can be placed in different files so that they can be worked on separately and finally merged when complete.

    Let us create a program which will have more than one functions and files. Our program will create different type of variables and displays the content of the memory of each variable in hexadecimal format. As displaying the memory content for each variable is similar and repetitive task we will define a function for the task and called the function from the main function multiple times. We will also keep the additional function in a separate file. Let us name the function as ‘ShowMemory’ and placed in file ShowMemory.cpp. The ShowMemory function takes two arguments, one is the memory address of the variable and the size in byte of the variable.

    Here is the content of the ShowMemory.cpp file-

    Code:
    #include<stdio.h>
    void ShowMemory(void* p, int size)
    {
    	unsigned char* pUc = (unsigned char*)p; //Convert the void pointer to 1 byte variable pointer
    	pUc += size - 1; // Increment the address by 3 bytes to reach the last byte 
    	printf("Memory content: 0x");
    	//Print the bits of every byte from the end byte to the first byte
    	//Reversing the order is to overcome the little endianness of Intel architecture
    	// where msb is the last byte and lsb is the first byte in the memory
    	for (int i = 0;i < size;++i)
    	{
    		unsigned int ui = *pUc;
    		printf("%02x", ui); // print in hex format consisting 2 digits
    		--pUc;
    	}
    	printf("\n");
    }
    
    Now the main function is written in another file named MemoryContentMain.cpp and the code is -
    Code:
    #include<stdio.h>
    void ShowMemory(void* p, int size); // Deceleration only
    int main()
    {
    	short i = 100;
    	printf("\nShort Integer:\n");
    	ShowMemory(&i,sizeof(i));
    
    	wchar_t c = 100;
    	printf("\nWide Character:\n");
    	ShowMemory(&c, sizeof(c));
    
    	long l= 100;
    	printf("\nLong Integer:\n");
    	ShowMemory(&l, sizeof(l));
    	
    	float f = 5.2;
    	printf("\nSingle floating point:\n");
    	ShowMemory(&f, sizeof(f));
    
    }
    
    Note that the 2nd line in the above file is the declaration of the ShowMemory function defined in the other file. This is required because every source file is compiled independently and you know, during compilation only the declaration is enough. During linking the linker search for the definition and if it is not there in any of the object file it will generate error. Now let us only compile(not link) the two files separately using following commands :
    Code:
    clang -c ..\MemoryContentMain.cpp
    clang -c ..\ShowMemory.cpp
    
    Those two command will compile each cpp files and create object files MemoryContentMain.o and ShowMemory.o respectively. Now to create the executable by linking those two object files using following command

    clang MemoryContentMain.o Showmemory.o -o Memorycontent.exe

    It will create the executable file named Memorycontent.exe. Run it using the command Memorycontent and you will get the following console output:
    Code:
    Short Integer:
    Memory content: 0x0064
    
    Wide Character:
    Memory content: 0x0064
    
    Long Integer:
    Memory content: 0x00000064
    
    Single floating point:
    Memory content: 0x40a66666
    
    It is not neither mandatory nor standard to compile the source files separately and liking. Compilers can take care of this. Instead running three command you can run only one command as shown below:

    clang ..\MemoryContentMain.cpp ..\Showmemory.cpp -o Memorycontent.exe

    Now here is another thing to consider. Suppose you have a program where lot of functions in many files exists. You will be using the functions from one file into other files. So you have to place declaration for functions in many files before using them. Their comes the header file concept. You can put all the decelerations in a single or multiple header files and include them in place of declaration. In the above program you can create a header file Showmemory.h with the content:
    Code:
    void ShowMemory(void* p, int size);
    
    And then include the header file in the MemoryContentMain.cpp as shown below. Everything else remain same. You compile in the same way.

    Code:
    #include<stdio.h>
    #include "Showmemory.h"
    int main()
    {
    	short i = 100;
    	printf("\nShort Integer:\n");
    	ShowMemory(&i,sizeof(i));
    
    	wchar_t c = 100;
    	printf("\nWide Character:\n");
    	ShowMemory(&c, sizeof(c));
    
    	long l= 100;
    	printf("\nLong Integer:\n");
    	ShowMemory(&l, sizeof(l));
    
    	float f = 5.2;
    	printf("\nSingle floating point:\n");
    	ShowMemory(&f, sizeof(f));
    }
    
    When you use header files it is first processed by the pre-processor which replaces the code from the header in the cpp file where the include statement is inserted. This is called header expansion. If the header again include a header then the header will be expanded first and so on. Also multiple header expansion in a single cpp file will be in the order it is included. So including header has a compile time overhead if header file is very big. This happens when you use standard library headers just to use a single function. It makes your source file very big by inserting the whole header file.

    When you include a file you may give a full path of the file or relative path. If you are not giving the full path, the preprocessor search for the file in a list of path. There are two syntax of including header file. One way is to specify the name of the file within a double quote like #include "Showmemory.h" and another way is to specify the name of the file within angular bracket (<>) like #include<stdio.h>. In the first case it will search in current directory and then in the include directory path given and then in the predefined list of path. In the second case it will search in predefined list of path and the given include path. Include path can be specified in the compiler command option -i. If you run the following command the preprocessor will include the path-C:\MinGW\lib\gcc\mingw32\4.9.3\include\c++ in the search list

    clang -I C:\MinGW\lib\gcc\mingw32\4.9.3\include\c++ Showmemory.cpp

    You should consult the compiler documentation about the search strategy

    We very often use functionality which has been already implemented and tested by some body else. One way to use external code is to add all required source files from the third party program in your program and compile. The other way is to add the compiled version i. e. object files of the third party code during link time. In either case you have to include declarations or headers with declarations in every file you want to use the external functions. The first way is not a standard because that is not easy way to use and the source code other than header files may not be available. When you use c/c++ standard library functions you only include the headers. Library code is not available but object files are available. You might be surprised that in the examples we used library functions like printf() but we did not add the object files. The compiler internally add those object files while invoking the linker. This object files are also called library files. If you have an object file other than standard library files you can add that explicitly. Following command is adding an object file along with cpp file:

    clang MemoryContentMain.cpp Showmemory.o -o Memorycontent.exe

    Similar to include file the linker search for object files or library files in a predefined list of path. You can add additional path in the search list with linker option. You should consult the compiler documentation about the search strategy.

    When we talk about compilation as a single step it means everything- preprocessing, compiling, assembling and linking. We can also call the complete process as ‘building’. In Visual studio it is the build process which pre-process, comipile ,assemble and link. You should follow the documentation of build environment you will be using. There are lot of compiler options which might be useful in different projects, so better to master them from the compiler documentation.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice