V m4 je možno získat uvozený řetězec definice makra. Slouží k tomu vestavěné makro
defn(`nazev_makra')
Uvedenou vlastnost můžete použít pro ladicí účely, ke kopírování nebo
přejmenování makra.
Je-li makro definované uživatelem, je výsledkem expanze jeho definice uzavřená
do uvozovek. Jestliže se však defn použije na vestavěné makro procesoru
m4, makro expanduje na speciální token odkazující na vnitřní definici makra.
Tento token je smysluplný jenom jako druhý parametr k define nebo
pushdef; v jakémkoliv jiném kontextu (třeba při pokusu o jeho vypsání
na výstup) je ignorován.
Následující ukázka zobrazí definici makra makro:
define(`makro', `Makro `$0' získalo $# argumentů') => defn(`makro') => Makro `$0' získalo $# argumentů
Jen tak cvičně, přejmenujme si makro undefine na vymaz:
define(`vymaz', defn(`undefine')) => vymaz(`undefine') => undefine(`vymaz') => undefine(`vymaz')
Makro defn je rozpoznáváno pouze s parametry.
Nepřímé volání maker
V odstavci o definici maker jsem vám slíbil, že prozradím, jak volat makro s nepřípustným názvem. Už vám tedy nebudu déle tajit, že existuje vestavěné makro
indir(`nazev_makra' [, parametry, ...])
Volání indir donutí m4 expandovat makro s názvem v prvním parametru a
předat mu zbytek argumentů. Klidně můžete dělat tohle:
indir(`define', `makro', `ahoj') => makro => ahoj
ale mnohem užitečnější je něco takového:
define(`$vnitrni_makro', `Jen pro nepřímé volání!') => $vnitrni_makro => $vnitrni_makro indir(`$vnitrni_makro') => Jen pro nepřímé volání!
Jak vidíte, mechanismus můžete použít k definování „soukromých“ maker, která slouží jen pro vnitřní účely a u kterých takto nehrozí, že budou uživatelem volány omylem.
Z podobného soudku je i makro
builtin(`nazev_makra' [, parametry, ...])
sloužící k nepřímému volání vestavěných maker. Všechna standardní makra procesoru m4 si můžete předefinovat podle svých představ, přitom ale neztratíte možnost volat kterékoliv z nich právě pomocí makra builtin:
define(`makro', `nazdar') => define(`undefine', `ahoj') => undefine(`makro') => ahoj makro => nazdar builtin(`undefine', `makro') => makro => makro
Ovšem, předefinujete-li si i makro builtin, nepomůže vám už ani svěcená voda :-).
Makro builtin je expandováno jen s parametry. Není tomu tak u indir, které bez argumentů zahlásí chybu:
indir E> stdin:1: m4: Undefined macro `'
Testování definice makra
Expanze maker na holý text, ačkoliv ji můžeme ovlivnit parametry, je užitečná jen v omezených případech. Obvykle potřebujeme něco víc: aby makro expandovalo na různé výstupy v závislosti na rozhodnutích uskutečněných za chodu, tj. potřebujeme nějaký druh podmínkových příkazů. GNU m4 poskytuje dva základní příkazy větvení. Prvním z nich je ifdef:
ifdef(`nazev_makra', výsledek1 [, výsledek2])
Příkaz umožňuje testovat, zda je nějaké makro definováno, nebo ne. Jestliže má identifikátor nazev_makra přiřazenou definiční hodnotu, ifdef expanduje na „výsledek1“. V opačném případě bude výstupem řetězec „výsledek2“. Třetí argument makra ifdef je volitelný, proto když jej neuvedete, použije se ve shodě s pravidly procesoru prázdný řetězec.
define(`vypis_autora', `ifdef(`$autor', `Autor: indir(`$autor')', `neznámý autor')') => vypis_autora => neznámý autor define(`$autor', `Karel Zelený') => vypis_autora => Autor: Karel Zelený
Makro ifdef je rozpoznáváno jen s argumenty.
Porovnávání řetězců
Poněkud užitečnějším podmínkovým příkazem je vestavěné makro ifelse:
ifelse(poznámka) ifelse(řetězec1, řetězec2, rovnají se [, nerovnají se]) ifelse(řetězec1, řetězec2, jsou rovny, ...)
Použijete-li jej jenom s jedním argumentem, nestane se nic a výsledkem bude prázdný řetězec. Z tohoto důvodu je makro ifelse občas využíváno pro různé poznámky.
ifelse(`Tak tohle je nějaká poznámka.') =>
Stejného účinku, ale čitelnějšího zdrojového kódu dosáhnete třeba
takto:
define(`poznamka', `') => poznamka(` Tohle je dlouhatánská poznámka, která se na výstupu neobjeví. ') =>
Má-li ifelse tři nebo čtyři parametry, provádí porovnávání prvních dvou argumentů. Jsou-li si řetězec1 a řetězec2 rovny, expanduje ifelse na řetězec „rovnají se“. V opačném případě bude výsledkem volitelný čtvrtý argument „nerovnají se“ (nebo prázdný řetězec, pokud čtvrtý parametr neuvedete).
ifelse(`pepa', `teta', `stejné') => ifelse(`pepa', `pepa', `stejné') => stejné ifelse(`pepa', `teta', `stejné', `různé') => různé ifelse(`teta', `teta', `stejné', `různé') => stejné
Makro ifelse ale umí zpracovat i více argumentů než jenom čtyři. Jsou-li první dva argumenty shodné, expanduje ifelse na text třetího parametru. Jinak se vše opakuje bez prvních tří argumentů – tedy porovnává se čtvrtý a pátý parametr a v případě shody se vrátí šestý argument atd. Chybí-li makru argumenty pro další porovnávání a všechna předchozí porovnání dopadla neúspěšně, použije se pro expanzi případný poslední argument. Viz příklad:
ifelse(`pepa', `teta', `1 = 2', `teta', `běta', `4 = 5', `nerovnají se') => nerovnají se ifelse(`pepa', `teta', `1 = 2', `teta', `běta', `4 = 5') => ifelse(`pepa', `teta', `1 = 2', `teta', `teta', `4 = 5', `nerovnají se') => 4 = 5 ifelse(`teta', `teta', `1 = 2', `teta', `teta', `4 = 5', `nerovnají se') => 1 = 2
Pochopitelně, v praxi bude ifelse používáno situacích mnohem složitějších, než byly zde uvedené. Jeho zásadní použití tkví v rekurzivně volaných makrech, ale o tom někdy příště.
Makro ifelse je rozpoznáváno jen s argumenty.