Preprocesor
Preprocesor – program komputerowy, którego zadaniem jest przetworzenie kodu źródłowego, w sposób określony przez programistę za pomocą dyrektyw preprocesora, na kod wyjściowy – tak przetworzony kod źródłowy poddawany jest następnie analizie składniowej, kompilacji, a w końcu konsolidacji.
Preprocesor jest najczęściej zintegrowany z kompilatorem języka programowania.
Preprocesor języków C i C++
edytujNajbardziej znane języki, które wyposażone są w preprocesor „wbudowany w język”, to C i C++. Dyrektywy preprocesora mogą występować w ogólności w dowolnym miejscu programu, a rozróżnienie ich od tekstu kodu źródłowego w językach C i C++ dokonywane jest poprzez poprzedzenie dyrektywy znakiem hash – „#”[1][2].
Do najważniejszych dyrektyw należą:
#include
… – dyrektywa włączająca tekst innego pliku źródłowego w miejscu jej wystąpienia w pliku podlegającym aktualnie przetwarzaniu, przy czym możliwe jest zagłębione występowanie dyrektywy include,#define
… – definiuje stałe i makroinstrukcje (pseudofunkcje)#undef
… – usuwa definicje stałej lub makra#if
… – dyrektywy kompilacji warunkowej#elif
… – działa podobnie jak else if w języku C#endif
… – oznacza koniec bloku kompilacji warunkowej#ifdef
… – znaczy to samo co#if defined
(…)#ifndef
… – znaczy to samo co#if !defined
(…)- i inne.
Dyrektywy preprocesora C/C++ wykorzystuje się często do zabezpieczenia plików nagłówkowych przed wielokrotnym dołączaniem do tego samego projektu. Jeżeli treść pliku nagłówkowego nazwa.h
obejmie się instrukcjami:
#ifndef _NAZWA_H_ /* (1) */ #define _NAZWA_H_ /* (2) */ /* ... treść właściwa ...(to co chcesz definiować np. struktury) */ #endif /* (3) */
to przy pierwszej próbie dołączenia pliku, kompilator najpierw sprawdzi, czy zdefiniowano stałą _NAZWA_H_ (może ona mieć dowolną nazwę, ten sposób jest jednak dobrym zwyczajem promowanym przez programistów) (1) – jeżeli nie, zostanie ona zdefiniowana (2) i do programu zostanie dołączona treść między (2) i (3), oznaczający koniec części dodawanej tylko przy spełnieniu warunku (1).
Niektóre kompilatory obsługują także następującą konstrukcję:
#pragma once
Zapobiega ona ponownemu załączeniu treści całego pliku, w którym została użyta. Metoda ta jednak nie ma oparcia w oficjalnym standardzie. Podobnie, jak wszystkie użycia dyrektywy #pragma
, jej ewentualna obsługa jest rozszerzeniem wprowadzonym przez dany kompilator i nie jest przenośna pomiędzy różnymi narzędziami.
Preprocesor języka Clipper
edytujW wersji 5.x języka Clipper został zaimplementowany preprocesor wzorowany na preprocesorze języka C[3]. Znalazły się więc w nim takie dyrektywy jak: #define, #ifdef, ifndef, #include, #undef. Ale preprocesor ten zawiera również dwie dodatkowe dyrektywy: #command i #translate. Ich składnia jest następująca: #command <szablon rozpoznawczy> ⇒ <szablon wynikowy> i identycznie dla dyrektywy translate.
Dyrektywy te umożliwiają definiowanie rozkazów i pseudorozkazów. Analizowany kod źródłowy jest sprawdzany pod kątem wystąpienia zdefiniowanych szablonów w następującej kolejności:
- #define
- #translate
- #command
Po znalezieniu wzorca następuje podstawienie w jego miejsce tekstu utworzonego według szablonu wynikowego i ponowne sprawdzenie, czy nie występuje kolejny szablon rozpoznawczy w utworzonym kodzie.
Preprocesor języka Pike
edytujRównież preprocesor języka Pike wzorowany jest na preprocesorze języka C. W zasadzie składnia dyrektyw preprocesora tego języka jest identyczna jak w C. Zasadnicze różnice to: brak plików nagłówkowych dołączanych dyrektywą include (choć sama dyrektywa istnieje i służy do dołączania plików z kodem źródłowym), oraz dodatkowe dyrektywy jak np. string dołączająca plik jako wartość tekstową.
Preprocesor języka PL/1
edytujNajbardziej rozbudowanym zestawem instrukcji dysponuje preprocesor języka PL/1 (implementacje IBM w systemie OS)[4]. W tym przypadku znakiem wyróżniającym dyrektywę preprocesora jest znak procentu ‘%’, a semantyka poleceń preprocesora jest niemal zgodna z semantyką analogicznych instrukcji języka PL/1. Lista instrukcji tego preprocesora obejmuje większość analogicznych instrukcji samego języka, w tym takich jak:
- %DECLARE – deklaracje zmiennych preprocesora,
- %vv=ee – przypisania,
- %GOTO – skoku,
- %IF – warunkowa,
- %DO - grupująca
- %DO vv=ee TO … BY … - pętli,
- %PROCEDURE - procedury
- i inne,
oraz specyficzne dla preprocesora, jak
- %INCLUDE – włączająca,
- i inne.
Choć preprocesor tego języka, podobnie jak w C, był wbudowany w kompilator, dzięki pewnym sztuczkom mógł być stosowany jako preprocesor dla innych języków programowania, np. FORTRAN.
Preprocesor asemblera
edytujW asemblerach rolę analogiczną do preprocesora pełnią makroinstrukcje, dyrektywy i pseudoinstrukcje. Powodują one wykonanie operacji na tekście kodu źródłowego w trakcie translacji wykonywanej przez program asemblera[5].
Niektóre asemblery, np. makroasembler IBM, w ramach pakietu oprogramowania dostarczają programy ułatwiające pisanie kodów źródłowych w asemblerze. Przykładem jest program SALUT wchodzący w skład ww. pakietu, który przekształcał instrukcje strukturalne na instrukcje asemblera. Działał on więc na zasadzie preprocesora lecz nie wbudowanego w program główny, tylko jako osobny program. Przed uruchomieniem procesu asemblacji należało uruchomić program SALUT, i dopiero plik wynikowy tego programu poddać procesowi asemblowania.
W programie tym instrukcje strukturalne zapisane są w pliku źródłowym i poprzedzone znakiem dolara '$'. Program dostarcza takich konstrukcji programowania strukturalnego jak instrukcja warunkowa $IF ... $ELSE ... $ENDIF, instrukcja pętli $DO w różnych wariantach, czy instrukcja przeszukiwania $SEARCH.
Preprocesor a dyrektywy kompilatora
edytujNiektóre implementacje języków programowania (np. Pascal, Object Pascal), choć nie posiadają wbudowanego preprocesora, to udostępniają dyrektywy kompilatora, które są rozpatrywane w trakcie kompilacji, a nie jak w przypadku preprocesora przed jej wykonaniem. Zwykle lista takich dyrektyw jest dużo mniejsza w porównaniu z listą dyrektyw preprocesora.[6][7][8]. Wynika to z budowy języków Pascal i Object Pascal, które nie używają plików nagłówkowych, natomiast instrukcje dołączania plików bibliotecznych są częścią składni języka (słowo kluczowe: uses
). Języki te obsługują również stałe w przeciwieństwie do języka C, który ich nie obsługuje (pojawiły się one dopiero w C++, w języku C zamiast nich stosuje się ciągi znaków zamieniane przez preprocesor, tzw. pseudostałe). Ponadto języki Pascal i Object Pascal nie obsługują makr preprocesora, jako że ich użyteczność jest niewielka. Zamiast nich stosowane są funkcje/procedury opatrzone klauzulą inline
.
W języku C# istnieje preprocesor i jest on częścią kompilatora[9]. Jednak w odróżnieniu od preprocesora języków C i C++ jest on podobny w działaniu do dyrektyw kompilatora używanych w języku Object Pascal. Oczywiście lista dyrektyw preprocesora języka C# różni się od listy dyrektyw kompilatora języka Object Pascal.
Najważniejszą (ale nie jedyną) rolą dyrektyw kompilatorów języków Pascal i Object Pascal oraz dyrektyw preprocesora języka C# jest kompilacja warunkowa.
Wady preprocesora
edytujJedną z wad preprocesora jest to, że makra i pseudostałe mogą powodować problemy w trakcie kompilacji plików źródłowych w języku C/C++, ponieważ nie są sprawdzane przez kompilator, który otrzymuje kod źródłowy już po zamianie dokonanej przez preprocesor[10][11]. Także podczas debugowania programu makra mogą sprawiać problemy - kompilator nie mając o nich wiedzy, nie generuje dla nich symboli debugowania. Z tego m.in. powodu makra nie są stosowane w nowszych językach programowania takich jak: C#, Java czy Object Pascal. Natomiast w języku C++ zalecane jest używanie szablonów oraz konstrukcji językowych wprowadzonych wraz z nowszymi standardami tego języka[12].
Zobacz też
edytujPrzypisy
edytuj- ↑ Jan Bielecki, Od C do C++, programowanie obiektowe w języku C, Wydawnictwa Naukowo-Techniczne, Warszawa 1990, ISBN 83-204-1332-X
- ↑ Jan Bielecki, Turbo C z grafiką na IBM PC, Wydawnictwa Naukowo-Techniczne, Warszawa 1990, Seria: Mikrokomputery, ISBN 83-204-1101-7
- ↑ Wojciech Rogowski , Arkadiusz Serodziński , Clipper 5.0, Warszawa: Wydawnictwo PLJ, 1991, ISBN 83-85190-20-1, OCLC 749775734 .
- ↑ Jan Bielecki, Rozszerzony PL/I i JCL w systemie OS/RIAD, Państwowe Wydawnictwo Naukowe, Warszawa 1986, Seria: Biblioteka Informatyki, ISBN 83-01-06146-4
- ↑ Eugeniusz Wróbel: Asembler 8086/88. Wyd. 2. Warszawa: Wydawnictwa Naukowo-Techniczne, 1992, seria: Mikrokomputery. ISBN 83-204-1504-7.
- ↑ Global compiler directives | Free Pascal and Lazarus Wiki. wiki.lazarus.freepascal.org. [dostęp 2021-04-23]. (ang.).
- ↑ Local compiler directives | Free Pascal and Lazarus Wiki. wiki.lazarus.freepascal.org. [dostęp 2021-04-23]. (ang.).
- ↑ Delphi Compiler Directives (List) Index | Embarcadero Docwiki. docwiki.embarcadero.com/RADStudio/Sydney/en/Main_Page. [dostęp 2021-04-23]. (ang.).
- ↑ Dyrektywy preprocesora języka C# | Microsoft Docs. docs.microsoft.com. [dostęp 2021-04-23]. (ang.).
- ↑ C/Preprocesor (w: C). Wikibooks. [dostęp 2021-04-23]. (pol.).
- ↑ Teoria kompilacji: Preprocessing. cpp-polska.pl. [dostęp 2021-04-23]. (pol.).
- ↑ constexpr expressions instead of macros (w: C# preprocessor directives) | Microsoft Docs. docs.microsoft.com. [dostęp 2021-04-23]. (ang.).