C++. Preprocessor. General information. Preprocessor directives. Review

Preprocessor. General information. Preprocessor directives. Review.
Directives #define, #error, #include, #undef, #import, #using, #pragma, #line


Contents


1. Preprocessor. Purpose. Directives

In the C++ programming language, the preprocessor is the part of the compiler that controls the formation of source code into object code. The C++ preprocessor is inherited from the C programming language. The preprocessor has a set of commands called preprocessor directives. With these directives, the preprocessor controls changes in the translation of source code into object code.

Any preprocessor directive begins with the # character. In C++, the preprocessor contains the following directives:

  • #define
  • #if
  • #endif
  • #undef
  • #error
  • #else
  • #ifdef
  • #line
  • #include
  • #elif
  • #ifndef
  • #pragma
  • #using
  • #line
  • # or NULL.

 

2. Directive #define. Macro name definition

The #define directive defines an identifier and a sequence of characters that will replace this identifier in the program. More details about the #define directive and examples of its use can be found here.

The general usage of the directive is as follows

#define macro_name character_sequence

here

  • macro_name – name that will be used in the program text;
  • character_sequence – a sequence of characters that will replace the macro_name name every time it occurs in the program.

Example.

#include <iostream>
using namespace std;

// Raise x to the power of 4
#define ABC(x) x*x*x*x

// Calculate the length of a line given two points
#define LENGTH_LINE(x1, y1, x2, y2) std::sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))

void main()
{
  // 1. Use ABC macro
  int t = 3;
  int x = ABC(t); // x = 81
  cout << "x = " << x << endl;

  // 2. Use LENGTH_LINE macro
  double x1, y1, x2, y2;
  x1 = 3.8;
  y1 = 2.7;
  x2 = -1.4;
  y2 = 5.5;
  double len = LENGTH_LINE(x1, y1, x2, y2);
  cout << "len = " << len << endl;
}

 

3. Directive #error. Display the error message

Using the #error directive causes the compiler to pause compilation. This directive is used to debug a program.

The general form of using the #error directive is

#error error_message

where

  • error_message – error message text.

Example.

In the example, the program will be compiled until the #error directive is encountered. Further, the compilation of the program will stop and an error message will be displayed.

#error: Compilation error

Thus, the *.exe file will not be created.

#include <iostream>
using namespace std;

void main()
{
  // Calculations
  double a = 3, b = 10, c;

  c = a * b + 5;

  // Display the error message
#error Compilation error.
}

 

4. Directive #include. Including a header or other source file

Using the #include directive, you can include other external files in the source code of the current file. The text of these external files will be used when generating the executable module of the source file.

In turn, included files can also contain #include directives in their code. These directives are called nested. The C++ language standard allows up to 256 levels of nesting.

In the most general case, including a file named filename.h can be done in one of two ways:

#include "filename.h"

or

#include <filename.h>

The presence of quotation marks “” or angle delimiters <> determines how the file is searched to include. The first method (“filename.h”) searches for a file in the directory with the current program (working directory). If the file is not found, the second way to find the file is used (using <> delimiters).

The second method (<> delimiter) searches for the file in the directory specified by the compiler. Typically, this is a directory with all the header files called INCLUDE.

When including files, proprietary library files are included in double quotes “”, and standard library files are included in <> delimiters.

When designing a single-file program, it is good practice to split the program code into two files:

  • file with *.h extension. This contains declarations (declarations only) of functions that are shared (available to the client);
  • file with *.cpp extension. It contains implementations of all developed functions. This may include not only the functions that are shared, but also any other additional internal functions of the project.

Example.

// Inclusion of standard libraries time.h, iostream
#include <time.h>
#include <iostream>

// Connecting a namespace
using namespace std;

// Including a user file my_file.h
#include "my_file.h"

void main()
{

}

 

5. The #undef directive

The #undef directive is used to remove the macro name defined by the #define directive. After using the directive, the name of the macro to be removed becomes undefined.

The general form of the #undef directive is as follows

#undef macros_name

where

  • macros_name – macro name to be deleted.

Example.

#include <iostream>
using namespace std;

// Directive #undef

// A macro that multiplies two numbers with each other
#define Mult2(x, y) (x * y)

// A macro that adds two numbers
#define Add2(x, y) (x + y)

void main()
{
  // Checking if the name Mult2 exists
#ifdef Mult2
  double res = Mult2(2.5, 7.8);
  cout << "2.5 * 7.8 = " << res << endl;
#endif

  // Undo name Add2 - #undef directive
#undef Add2

// Checking if the name Add2 exists
#ifdef Add2
  int a = 8, b = 9;
  int resAdd = Add2(a, b);
  cout << "a + b = " << resAdd << endl;
#else
  cout << "The Add2 name is undefined." << endl;
#endif
}

Result

2.5 * 7.8 = 19.5
The Add2 name is undefined.

 

6. Directive #import. Including information from the type library

Using the #import directive, you can include information from the type library (TLB – type library). In this context, a type library is a hierarchical repository of information about the Active-X server’s capabilities, which is stored as a *.tlb or *.olb file.

The general form of the declaration of the #import directive can be as follows

#import "filename" [attributes]
#import <filename> [attributes]

where

  • filename – the name of the file containing the type library. It can be a *.tlb, *.olb, *.dll or *.exe file. It can also be any other file format understood by the LoadTypeLib() API function;
  • attributes – attributes of the directive. These attributes indicate that the compiler is modifying the content of the type library header. For more information about #import attributes, see the documentation at learn.microsoft.com.

Example.

#import "MyTypeLib"

 

7. Directive #using. Including *.dll, *.obj, *.exe, *.net module files

The #using directive imports metadata into a program that was compiled with the /clr option. This means that the directive can only be used in C++/CLI mode.

The general form of using the #using directive is as follows

#using filename [as_friend]

where

  • filename – file name with extension *.dll, *.exe, *.net module or *.obj.

For example, to include a dynamic library named “MyLibrary.dll” you need to execute the following code

#using <MyLibrary.dll>

 

8. Directive #pragma. Specifying execution functions for the compiler

The #pragma directive allows you to specify functions to be executed by the compiler. The use of certain functions depends on the operating system and the current computer, which is called the host computer. The characteristics of the compiler functions are determined when you install the C/C++ compiler on your computer. Using the #pragma directive, you can offer the compiler various functions for processing them, ensuring full compatibility with C or C++ languages.

The #pragma directive has many uses and several implementations. The general form of one of the common implementations is as follows

#pragma token_string

where

  • token_string – the so-called token string, which can have different values. This string is a set of characters that define a set of instructions and their arguments. Tokens include, for example, once, alloc_text, component, auto_inline, endregion, loop, etc.

A detailed consideration of the implementation features of the directive in combination with one or another token is not the subject of this topic.

Example.

The example instructs the compiler to include the current header file only once when the source file will be compiled.
For example, the header file is the file named “MyClass.h”, the source file is the file “MyClass.cpp”.

// File "MyClass.h"
#pragma once

 

9. Directive #line. Set string to file name in error messages

Using the #line directive instructs the compiler to set (change) the line numbers and file name that are displayed in error messages.

When an error occurs, the compiler remembers the error line number and file name in the __LINE__ and __FILE__ macros, respectively. After the #line directive is called, the value of these macros is changed to the values specified in this directive.
The #line directive can be used in one of two possible general forms:

#line digit_sequence

or

#line digit_sequence [filename]

where

  • digit_sequence is an integer constant in the range from 0 to 2147483647 inclusive. This constant specifies the line number and is assigned to the __LINE__ macro after the directive is called;
  • filename is an optional parameter that is the name of the file that is displayed in the error message and written in the __FILE__ macro. If filename is omitted, then the previous filename remains unchanged.

The #line directive is commonly used by program generators. In these generator programs, based on the execution (not execution) of some statement, you need to specify the text of the error message and refer to the corresponding source file.

Example.

#include <iostream>
using namespace std;

int main()
{
  // Print values of macros __LINE__ and __FILE__
  cout << "The value of __LINE__: " << __LINE__ << endl;
  cout << "The value of __FILE__: " << __FILE__ << endl;

  // Set the new value of the __LINE__ macro
#line 12
  cout << "The value of __LINE__ after changes: " << __LINE__ << endl;
  cout << "The value of __FILE__ after changes: " << __FILE__ << endl;

  // Set the new value of the __LINE__ and __FILE__ macros
#line 122 "source2.cpp"
  cout << "The value of __LINE__ after changes 2: " << __LINE__ << endl;
  cout << "The value of __FILE__ after changes 2: " << __FILE__ << endl;
}

Result

The value of __LINE__: 7
The value of __FILE__: D:\Programs\C++\Project10\Source.cpp
The value of __LINE__ after changes: 12
The value of __FILE__ after changes: D:\Programs\C++\Project10\Source.cpp
The value of __LINE__ after changes 2: 122
The value of __FILE__ after changes 2: D:\Programs\C++\Project10\source2.cpp

 


Related topics