CMake Meta Build System
Common Algorithms from the STL Library in C++
Control Techniques for Robot Manipulators
Robot Operating System (ROS) - Command Line Tools
Sampling-based Motion Planners
Motion Planning in Robotics
Visual SLAM - An Introduction
Coverage Problem in Mobile Robotics
C++ is one of the most widely used general-purpose programming languages. Advanced memory management, RAII, pointers, low-level memory manipulation and many more features make C++ one of the most sought after languages. It provides a developer, all the tools needed to code features closest to the hardware. Thread manipulation, atomicity of operations and data synchronization are some of the few low-level tasks that C++ is best at.
C++ is a high-level language written in plain English and the source code needs to be converted into machine-readable code because the CPU does not understand anything but binary 0s and 1s which are essentially digital voltage signals. The process of conversion of high-level code to machine readable language is called compilation.Unlike interpreted languages that convert code sequentially to machine level language at runtime (and just in time), compiled languages like C++ convert the complete code, at once, into machine level code, ready to execute and thus fairly faster than the interpreted languages.
C++ can be called a fairly strictly-typed language but can also have templated code which is converted to actual code and then to machine code during compilation. Code compilation does all the needed effort upfront to generate immediately executable code after checking the same for syntactical errors. However, the process of conversion of source code into a valid executable binary is much more than only compilation. It involves a preprocessing phase before compilation and a phase of linking multiple libraries and third-party resources/binaries afterwards.
The main steps of generating an executable from a source code are pre-processing, compilation and linking. The following C++ code is used as an example to explain each of these 3 phases.
Before the user written high-level code is converted to machine code, it is important to aggregate code and run some precompilation instructions for different header files(.h) or source code files(.cpp). Any such instructions starting with
# are called directives and are executed during this preprocessing phase.
|| Used to import other headers from local user defined source code or from native C++ code like
||If macro is defined, returns true, else false|
||If macro is not defined, returns true, else false|
||End the if condition preprocessor|
||Prints out the error message on a stderr|
||Removes the definition of a macro|
||Special commands to the compiler|
#ifdef and #ifndef are usually used as header guards that avoid duplicate code declarations from multiple header files.
#define keyword defines some concise functions or variables or other macros for reuse. The preprocessing phase replaces all instances of the symbolic code to actual values. An example can be seen from the compiler_example.h file shown above.
#if, #else and
#elif are used for conditional statements that decide a few compilation during the precompilation.
Compilation is most important step of creating an executable binary from the source code. This conversion from the code generated after preprocessing to machine level code is done by a compiler.
A compiler is just another computer program like the user written code to be compiled. It reads the complete source code and converts it to the machine level code as per the language syntax. This conversion is done in several steps which involves understanding the user written characters, syntactical checks, assembly-level conversion and final conversion to machine-level code equivalent.
There are several C++ compilers like g++, minGW and clang among others. GNU Compiler Collection provides g++, one of the most used compiler drivers for C++ code. It also works on C code but considers the files as C++ source code. It thereafter automatically decides the backend as cc1 or cc1plus depening on the file type.
The compilation steps are shown in the figure below and each of the steps has its own significance in ensuring that the executable being created is free of all preemptible errors, developer mistakes and logical inconsistencies.
compiler_example.cppline 11, the lexical analyzer understands that the variable
summis an integer which equal to integer
[KEYWORD, "int"] [ID, "summ"] [EQUALS] [ID, "a"] [SUM] [ID, "b"] [SEMICOLON]
These mentioned steps above are very brief explanation and have more sophisticated steps within. The scope of this article is largely to understand how the user written code is converted into a code that the integrated circuits execute. It helps understand the reason for errors that come up during code compilation and steps to avoid the same with appropriate code reuse, imports, library usage and executable configuration.
While compilation phase generates the executable, the complete project being worked on may have static libraries. There may be several executables that are inter-dependent, multiple object files that are linked together to ensure that the executable works at runtime.
As mentioned earlier, C++ is an amazing language but delving into the intricacies exposes multiple layers of the language that are intimidating to understand but worth applauding at the same time. The developers and the community does a great job at making this convoluted language be a high-performance platform while also not being impossible t decipher.
IF YOU LIKED THE ARTICLE, DON'T FORGET TO LEAVE A REACTION OR A COMMENT!