Preprocesor

Preprocesor je počítačový program, který zpracovává vstupní data tak, aby výstup mohl dále zpracovávat jiný program. Preprocesor je často používán pro předzpracování zdrojového kódu před vlastní kompilací. Druh a míra předzpracování závisí zejména na schopnostech preprocesoru. Většina preprocesorů je relativně jednoduchá, zvládá nahrazování textu a jednoduchá makra. Existují též sofistikované preprocesory, případně plně rozvinuté programovací jazyky.

Některé programovací jazyky (například jazyk C) vždy používají preprocesor ve fázi, která se nazývá preprocesing.

Lexikální preprocesory

Lexikální preprocesory jsou nejnižším stupněm preprocesorů. Pro svoji činnost využívají pouze lexikální analýzu, tzn. že, pracují se zdrojovým kódem jako s obyčejným textem. Ve vstupním textu provádějí úpravy typu: najít a nahradit, použít makro, připojit externí soubor, atd.

Preprocesing v jazyce C/C++

Nejznámějším a hojně využívaným preprocesorem je preprocesor jazyka C/C++. Tento preprocesor funguje na výše zmíněném principu.

Připojení externích souborů

Preprocesoru v jazyce C/C++ se nejčastěji používá pro připojení hlavičkových souborů.

#include "…"

nebo

#include <…>

Rozdíl mezi těmito příkazy je v umístění, kde bude preprocesor zadaný soubor hledat. „…“ značí lokální hlavičkový soubor v projektu. <…> znamená soubor umístěný v některém společném úložišti hlavičkových souborů pro všechny projekty. Na ukázku, připojení souborů <math.h> a <stdio.h> ze standardní C/C++ knihovny umožňuje využívat matematické a I/O operace.

Zmíněný lexikální preprocesor není schopný sám ohlídat vícenásobné připojení jednoho hlavičkového souboru nebo dokonce cyklické připojování hlavičkových souborů. Mnohonásobné připojení musí v tomto případě ohlídat programátor za pomocí podmíněného překladu. Mimo jiné je tento přístup pomalý, neboť je nutné před každou kompilací připojovat hlavičkové soubory i když se v nich nic nezměnilo.

Od roku 1970, bylo vymyšleno mnoho alternativ hlavičkových souborů z jazyka C/C++. Tyto alternativy jsou mnohdy efektivnější, rychlejší a použití je přehlednější. Vybrané implementace sdílených souborů: Java a Common Lisp používají balíčky, Pascal má unity, Modula, OCaml, Haskell a Python mají moduly a C# využívá importy jmenných prostorů z připojených knihoven.

Makra

Makra jsou primárně používána v jazyce C. Umožňují vkládat drobné části kódu na mnoho míst, čímž se šetří velikost zdrojového kódu a přehlednost. V makrech je možné používat i zástupné znaky (něco jako parametry funkcí). Makra jsou zpracovávána preprocesorem, proto je tento kód kompilován vícekrát, i když je zapsán pouze jednou. Makra nemají vliv na rychlost překladu.

Na ukázku:

#define max(a,b) a > b ? a : b

definuje makro max se dvěma parametry a a b. Takovéto makro může být „voláno“ jako funkce (stejnou syntaxí). Využití maker je v C/C++ velmi významné hlavně při ladění, kdy je možné rychle předefinovat makro a měnit tak kód na mnoha místech zároveň.

To, že se makra nechovají stejně jako funkce si ukážeme na následujícím příkladu, kde f a g jsou dvě funkce vracející integer. Zavolání

z = max(f(), g());

nevyčíslí f()jednou a g() taky jednou jak by se dalo čekat u funkce, nýbrž jedna z funkcí f nebo g bude vyčíslena dvakrát (v závislosti na tom, která vrací větší číslo). Toto chování může mít za následek katastrofální chování v případě, že záleží na počtu volání f nebo g.

Většina moderních programovacích jazyků již nevyužívá tyto možnosti maker, právě z těchto nejednoznačných důvodů a snadného zaměnění za funkce. Z toho důvodu existují v dnešních jazycích pouze funkce nebo metody.

Podmíněný překlad

Preprocesor jazyka C/C++ podporuje podmíněnou kompilaci. To umožňuje mít více verzí stejného kódu, který se například liší ve výpisu logovacích informací nebo je platformě závislý. Takto je možné do jednoho zdrojového kódu napsat program kompilovatelný pro více platforem, či různé jazykové mutace, …

Nejčastěji se používá následující konstrukce:

#ifndef SOUBOR_H
#define SOUBOR_H
(zdrojový kód hlavičkového souboru)
#endif

Programátor tím zabraňuje mnohonásobnému připojení hlavičkového souboru. Tímto problémem jsme se zabývali výše.

Podmíněný překlad je možné využít v komplexnějších případech jako je tento:

 #ifdef DEBUG
 
 #else
 
 #endif

nebo

 #if DEBUG
 
 #else
 
 #endif

Většina moderních programovacích jazyků odstraňuje tuto schopnost a uchyluje se k tradičnímu použití if…then…else…. To má za následek pomalejší běh programu, neboť podmínka musí být vyhodnocena za běhu programu a nikoliv pouze při kompilaci.

Syntaktické preprocesory

Syntaktické preprocesory byly poprvé představeny v jazyce Lisp. Jejich úkolem je převést syntaktický strom na sérii uživatelem definovaných přepisovacích pravidel. V několika jazycích jsou pravidla napsána ve stejném jazyce jako program (compile-time reflection), případ právě jazyka Lisp a OCaml. Ostatní jazyky mají tato pravidla definována v jiných jazycích, jako například XSLT preprocesor pro XML.

Syntaktické preprocesory jsou typicky použity pro individuální přizpůsobení jazyka, doplnění nových primitiv, atd. Takto je možné dosáhnout všeobecně použitelného programovacího jazyka.

Přizpůsobení syntaxe

Pěkný příklad naleznete na internetové adrese http://caml.inria.fr/pub/docs/manual-camlp4/manual007.html. Program může být zapsán dvěma různými syntaxemi při zachování stejné funkčnosti a záleží hlavně na subjektivním posouzení programátorem.

Velký počet programů napsaných v OCaml přizpůsobuje syntaxi doplněním nových operátorů. Definici vlastních operátorů podporuje i jazyk C++.

Specializace jazyka

Jedna z neobvyklých vlastností jazyka Lisp je možnost použít makra k vytvoření vlastního minijazyka uvnitř projektu. Typicky ve velkých projektech bývají některé moduly napsány v minijazycích. Modul pro přístup k SQL databázi využívá dialekt se základem právě v SQL příkazech. Jiné moduly mohou využívat jiné minijazyky, například modul pro práci s GUI, tiskem, atd. Standardní knihovna Common Lisp obsahuje příklad, kdy se k využívá ke složitému iterování v cyklu minijazyk založený na jazyce ALGOL.

Univerzální preprocesory

Drtivá většina preprocesorů je zaměřena na jeden konkrétní jazyk, resp. spolupracuje s konkrétním kompilátorem. Ale existují také tzv. univerzální preprocesory, které nejsou svázány s konkrétním jazykem. V takových případech je možné preprocesor „programovat“ vlastními pravidly pro konkrétní programovací jazyk.

Související články

Externí odkazy

This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.