Препроцесор. Загальні відомості. Директиви препроцесора. Огляд.
Директиви #define, #error, #include, #undef, #import, #using, #pragma, #line
Зміст
- 1. Препроцесор. Призначення. Директиви
- 2. Директива #define. Визначення імені макросу
- 3. Директива #error. Відображення повідомлення про помилку
- 4. Директива #include. Включення заголовку або іншого вихідного файлу
- 5. Директива #undef
- 6. Директива #import. Включення відомостей з бібліотеки типів
- 7. Директива #using. Включення *.dll, *.obj, *.exe, *.net module файлів
- 8. Директива #pragma. Задавання компілятору функцій виконання
- 9. Директива #line. Встановити рядок на назву файлу в повідомленнях про помилку
- Споріднені теми
1. Препроцесор. Призначення. Директиви
У мові програмування C++ препроцесор – це частина компілятора, яка керує формуванням вихідного коду в об’єктний. Препроцесор C++ успадкований з мови програмування C. Препроцесор має набір команд, які називаються директивами препроцесора. З допомогою цих директив препроцесор керує змінами в трансляції вихідного коду в об’єктний.
Будь-яка директива препроцесора починається з символу #. У C++ препроцесор містить наступні директиви:
- #define;
- #if;
- #endif;
- #undef;
- #error;
- #else;
- #ifdef;
- #line;
- #include;
- #elif;
- #ifndef;
- #pragma;
- #using;
- #line;
- # або NULL.
⇑
2. Директива #define. Визначення імені макросу
Директива #define визначає ідентифікатор та послідовніть символів, яка буде замінювати цей ідентифікатор у програмі. Більш детально про директиву #define та приклади її використання можна прочитати тут.
Загальний вигляд використання директиви наступний
#define macro_name character_sequence
тут
- macro_name – ім’я, яке буде використано в тексті програми;
- character_sequence – послідовність символів, яка буде замінювати ім’я macro_name кожного разу як воно зустрінеться у програмі.
Приклад.
#include <iostream> using namespace std; // Піднести x до степеня 4 #define ABC(x) x*x*x*x // Обчислити довжину лінії за двома точками #define LENGTH_LINE(x1, y1, x2, y2) std::sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)) void main() { // 1. Використати макрос ABC int t = 3; int x = ABC(t); // x = 81 cout << "x = " << x << endl; // 2. Використати макрос LENGTH_LINE 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. Директива #error. Відображення повідомлення про помилку
Використання директиви #error змушує компілятор зупинити компіляцію. Ця директива використовується для відлагодження програми.
Загальна форма використання директиви #error має вигляд
#error error_message
де
- error_message – текст повідомлення про помилку.
Приклад.
У прикладі компіляція програми буде виконуватись до директиви #error. Далі компіляція програми зупиниться і видасть повідомлення про помилку
#error: Compilation error
Таким чином *.exe-файл створено не буде.
#include <iostream> using namespace std; void main() { // Виконання обчислень double a = 3, b = 10, c; c = a * b + 5; // Вивести повідомлення про помилку #error Compilation error. }
⇑
4. Директива #include. Включення заголовку або іншого вихідного файлу
З допомогою директиви #include можна підключати до вихідного тексту поточного файлу інші зовнішні файли. Текст цих зовнішніх файлів буде використовуватись при формуванні виконавчого модуля вихідного файлу.
У свою чергу, файли, що включаються, в своєму коді також можуть містити директиви #include. Ці директиви називаються вкладеними. Стандарт мови C++ допускає до 256 рівнів вкладення.
У найбільш загальному випадку підключення файлу з іменем filename.h може здійснюватись одним з двох способів:
#include "filename.h"
або
#include <filename.h>
Наявність лапок “” чи кутових обмежувачів <> визначає спосіб пошуку файлу для його підключення.
При першому способі (“filename.h”) пошук файлу здійснюється в каталозі з поточною програмою (робочому каталозі). Якщо файл не знайдено, то використовується другий спосіб пошуку файлу (з допомогою обмежувачів <>).
При другому способі (обмежувачі <>) пошук файлу здійснюється в каталозі, що вказаний компілятором. Як правило це каталог з усіма заголовочними файлами під назвою INCLUDE.
При підключенні файлів, власно розроблені бібліотеки файлів включаються в подвійні лапки “”, а файли стандартної бібліотеки включаються в обмежувачі <>.
При проектуванні програми, яка розміщується в одному файлі, доброю практикою вважається розбивання програмного коду на два файли:
- файл з розширенням *.h. Тут містяться оголошення (тільки оголошення) функцій, які надаються в загальний доступ (доступні для клієнта);
- файл з розширенням *.cpp. Тут містяться реалізації всіх розроблених функцій. Сюди можуть входити не тільки функції, які були надані для загального доступу, але й будь-які інші додаткові внутрішні функції проекту.
Приклад.
// Підключення стандартних бібліотек time.h, iostream #include <time.h> #include <iostream> // Підключення простору імен using namespace std; // Підключення користувацького файлу my_file.h #include "my_file.h" void main() { }
⇑
5. Директива #undef
Директива #undef використовується для видалення імені макросу, яке було визначене директивою #define. Після використання директиви, ім’я макросу, що видаляється, стає невизначеним.
Загальна форма директиви #undef наступна
#undef macros_name
тут
- macros_name – ім’я макросу, яке потрібно видалити (зробити невизначеним).
Приклад.
#include <iostream> using namespace std; // Директива #undef // Макрос, що множить два числа між собою #define Mult2(x, y) (x * y) // Макрос, що додає два числа #define Add2(x, y) (x + y) void main() { // Перевірка, чи існує ім'я Mult2 #ifdef Mult2 double res = Mult2(2.5, 7.8); cout << "2.5 * 7.8 = " << res << endl; #endif // Відмінити ім'я Add2 - директива #undef #undef Add2 // Перевірка, чи існує ім'я Add2 #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 }
Результат
2.5 * 7.8 = 19.5 The Add2 name is undefined.
⇑
6. Директива #import. Включення відомостей з бібліотеки типів
З допомогою директиви #import можна включати відомості з бібліотеки типів (TLB – type library). У даному контексті бібліотека типів є ієрархічним сховищем інформації про можливості Active-X сервера, яка зберігається як файл з розширенням *.tlb або *.olb.
Загальна форма оголошення директиви #import може бути такою
#import "filename" [attributes] #import <filename> [attributes]
тут
- filename – ім’я файлу, що містить бібліотеку типів. Це може бути файл з розширенням *.tlb, *.olb, *.dll або *.exe. Це може бути також будь-який інший формат файлу, що є зрозумілим для функції API LoadTypeLib();
- attributes – атрибути директиви. Ці атрибути вказують, що компілятор змінює вміст заголовку бібліотеки типів. Більш детальну інформацію про атрибути #import можна знайти в документації на сайті learn.microsoft.com.
Приклад.
#import "MyTypeLib"
⇑
7. Директива #using. Включення *.dll, *.obj, *.exe, *.net module файлів
Директива #using виконує імпорт метаданих у програму, яка була скомпільована з параметром /clr. Це означає, що використання директиви може бути тільки в режимі C++/CLI.
Загальна форма використання директиви #using наступна
#using filename [as_friend]
тут
- filename – ім’я файлу який має розширення *.dll, *.exe, *.net module або *.obj.
Наприклад, що підключити динамічну бібліотеку з іменем “MyLibrary.dll” потрібно виконати такий код
#using <MyLibrary.dll>
⇑
8. Директива #pragma. Задавання компілятору функцій виконання
Директива #pragma дозволяє задавати функції для їх виконання компілятором. Використання тих чи інших функцій залежить від операційної системи та поточного комп’ютера, який ще називають хост-комп’ютером. Характеристики функцій компілятора визначаються при встановленні компілятора C/C++ на комп’ютер. З допомогою директиви #pragma можна пропонувати компілятору різні функції для їх опрацювання з одночасним забезпеченням повної сумісності з мовами C чи C++.
Директива #pragma має багато особливостей використання та декілька реалізацій. Загальна форма однієї з поширених реалізацій наступна
#pragma token_string
тут
- token_string – так званий рядок токена, який може бути різних значень. Цей рядок є набором символів, що визначають набір інструкцій та їх аргументів. До токенів відносять, наприклад, once, alloc_text, component, auto_inline, endregion, loop тощо.
Детальний розгляд особливостей реалізації директиви в поєднанні з тим чи іншим токеном не є предметом даної теми.
Приклад.
У прикладі задається вказівка компілятору включити поточний файл заголовку тільки один раз у випадку, коли буде відбуватись компіляція файлу з вихідним кодом.
Наприклад, файл заголовку – це файл з іменем “MyClass.h”, файл з вихідним кодом – це файл “MyClass.cpp”.
// Файл "MyClass.h" #pragma once
⇑
9. Директива #line. Встановити рядок на назву файлу в повідомленнях про помилку
Використання директиви #line дає вказівку компілятору задати (змінити) номери рядка та назву файлу, що виводяться у повідомленнях про помилку.
При виникненні помилки, компілятор запам’ятовує номер рядка помилки та назву файлу відповідно в макросах __LINE__ та __FILE__. Після виклику директиви #line значення цих макросів змінюється на ті, що були задані у цій директиві.
Директива #line може бути використана в одній з двох можливих загальних форм:
#line digit_sequence
або
#line digit_sequence [filename]
де
- digit_sequence – цілочисельна константа в діапазоні від 0 до 2147483647 включно. Ця константа задає номер рядка і присвоюється макросу __LINE__ після виклику директиви;
- filename – необов’язковий параметр, що є іменем файлу, який виводиться у повідомленні про помилку та записується в макросі __FILE__. Якщо filename опущений, то попереднє ім’я файлу залишається без змін.
Директива #line як правило використовується генераторами програм. У цих програмах-генераторах на основі виконання (не виконання) деякого твердження потрібно задавати текст повідомлення про помилку та посилатись на відповідний вихідний файл.
Приклад.
#include <iostream> using namespace std; int main() { // Вивести значення макросів __LINE__ та __FILE__ cout << "The value of __LINE__: " << __LINE__ << endl; cout << "The value of __FILE__: " << __FILE__ << endl; // Встановити нове значення макросу __LINE__ #line 12 cout << "The value of __LINE__ after changes: " << __LINE__ << endl; cout << "The value of __FILE__ after changes: " << __FILE__ << endl; // Встановити нове значення макросів __LINE__ та __FILE__ #line 122 "source2.cpp" cout << "The value of __LINE__ after changes 2: " << __LINE__ << endl; cout << "The value of __FILE__ after changes 2: " << __FILE__ << endl; }
Результат
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
⇑
Споріднені теми
- Макроси. Директива #define. Приклади
- Умовна компіляція. Директиви #if, #else, #elif, #endif, #ifdef, #ifndef. Оператор defined
⇑