Quando si scrive un pacchetto è bene dare una mano agli utenti che potrebbe-ro interpretare male i comandi a disposizione e, nei casi che possiamo prevedere, interrompere con un messaggio di errore e l’aiuto relativo.
Supponiamo, per esempio, che uno dei nostri comandi preveda che l’utente specifichi come argomento un numero non negativo. Scriveremo allora qualcosa come
\def\mycommand#1{% \ifnum#1<0
\PackageError{xyzzyx}{Invalid number} {The argument to \protect\mycommand\space
must be positive.}% \else
\dosomething \fi}
Qui\dosomethingsta al posto di quanto vogliamo che la macro esegua, mentre xyzzyxè il nome del nostro pacchetto . Nel caso in cui l’argomento sia scorretto, LATEX interromperà la compilazione e mostrerà il seguente messaggio
! Package xyzzyx Error: Invalid number.
See the xyzzyx package documentation for explanation. Type H <return> for immediate help.
...
l.312 \mycommand{-2} ?
Così l’utente saprà a quale pacchetto fare riferimento per rimediare all’errore. Il terzo argomento a\PackageErrorè il testo che viene stampato se l’utente premehin risposta al messaggio di errore; si noti che un nome di comando va preceduto da \protecte seguito da\spacese si desidera uno spazio. La punteggiatura finale va solo in quest’ultimo messaggio informativo, nei messaggi di errore il punto esclama-tivo iniziale e il punto finale sono aggiunti automaticamente. Se il messaggio non sta su una riga si può usare\MessageBreaknei punti dove si desidera andare a capo. Esiste anche il corrispondente comando\ClassErrorda usare per i messaggi di errore inviati da una classe.
Non necessariamente un comportamento scorretto dell’utente deve essere mar-cato da un messaggio di errore; a volte è conveniente inserire solo un avvertimento e ciò si ottiene con i comandi
\ClassWarning{〈classe 〉}{〈messaggio 〉} \ClassWarningNoLine{〈classe 〉}{〈messaggio 〉} \PackageWarning{〈classe 〉}{〈messaggio 〉} \PackageWarningNoLine{〈classe 〉}{〈messaggio 〉}
che mostrano il secondo argomento nel caso siano chiamati in azione. La differenza fra le due varietà è che i comandi conNoLinenon stampano a schermo il numero di riga in cui è stato commesso il ‘misfatto’; quale dei due preferire è questione di gusti. Per esempio, potrebbe essere usata la varietàNoLineper messaggi riguardanti op-zioni obsolete oppure per messaggi cumulativi da inviare alla fine della compilazione dove potrebbero ricevere maggiore attenzione:
\DeclareOption{lira}{\AtEndDocument{%
\PackageWarningNoLine{xyzzyx}{Option ‘lira’ is obsolete and has been\MessageBreak automatically replaced by ‘euro’}} Esistono anche i comandi che hannoInfoinvece diWarning, la loro funzione è di scrivere i messaggi solo sul file.log; li si adoperi per messaggi informativi di carattere secondario.
Un esempio non banale di trattamento degli errori è il seguente. Poniamo che una versione del nostro pacchetto richieda alcuni registri\dimenper certi scopi, ma che poi ci accorgiamo che possiamo farne a meno. Siamo anche abbastanza sicuri che pochi utenti abbiano sentito la necessità di adoperare questi registri, perché i valori impostati dal pacchetto erano sufficienti nella maggioranza dei casi. Per la nuova versione abbiamo il problema di trattare il caso in cui l’utente abbia impostato queste dimensioni a valori diversi. Ecco un possibile modo di procedere:
\def\xyzzyx@obsoletelength#1{% \def#1{\dimen@\z@
\PackageError{xyzzyx}{Obsolete command \protect#1} {The length \protect#1 is not needed any more.
I’ll ignore it.}% \dimen@}}
\xyzzyx@obsoletelength\Awidth \xyzzyx@obsoletelength\Bwidth \xyzzyx@obsoletelength\Cwidth
Siccome i registri obsoleti sono più di uno, definiamo un comando generico che a ogni chiamata genererà il comando corretto. Mostriamo, in righe successive l’espansione della chiamata nella prima riga (con\ERRORabbreviamo la parte riguardante l’errore:
\setlength{\Awidth}{3cm} \Awidth 3cm\relax
\dimen@\z@ \ERROR \dimen@3cm\relax
e così si avrà solo il messaggio di errore, perché il valore usato viene assegnato a un registro temporaneo e dimenticato. Vediamo l’altro caso:
\addtolength{\Awidth}{1cm} \advance \Awidth 1cm\relax
\advance \dimen@\z@ \ERROR \dimen@ 1cm\relax
e di nuovo il comando è ignorato, a parte l’emissione del messaggio di errore. Si può naturalmente preferire un messaggio di avviso usando\PackageWarning; in ogni caso, nella documentazione della nuova versione dovrà essere chiaramente indicata la modifica.
Rimane da vedere che la nostra strategia funziona anche con una chiamata come \settowidth{\Awidth}{pippo}o comandi analoghi:
\settowidth{\Awidth}{pippo} \@settodim\wd{\Awidth}{pippo}
\setbox\@tempboxa\hbox{{pippo}}\Awidth\wd\@tempboxa ...
(dove i puntini indicano codice irrilevante). Dopo la costruzione della\hboxi token spariscono dal flusso di input e le espansioni successive sono
\Awidth\wd\@tempboxa ...
\dimen@\z@ \ERROR \dimen@\wd\@tempboxa ...
che è ancora codice corretto: vengono eseguite assegnazioni al registro temporaneo \dimen@che non fanno alcun danno.
ε-TEX e
TEX
Le distribuzioni di TEX, da qualche tempo, usano il programmapdftexper la com-pilazione dei documenti LATEX. In questo programma sono disponibili le cosiddette estensioniε-TEX. La storia è un po’ complicata: un gruppo di programmatori decise di
estendere TEX aggiungendo varie funzionalità. Il loro scopo era di produrre un nuovo programma in Java, compatibile a ritroso con TEX; il progetto cominciò aggiungendo a TEX alcune funzioni e primitive per sperimentarle per il nuovo programma, che però non è mai stato scritto. Altri, in particolare Hàn Thê´ Thành, cominciarono a sviluppare un’altra versione che avesse come uscita diretta un documentoPDF. In seguito anche aPDFTEX furono aggiunte le estensioni ε-TEX e per un po’ di tempo ci furono i due programmiPDFTEX ePDF-ε-TEX, poi finalmente riuniti in uno solo.
È importante sapere che quando si compila un documento LATEX tutte queste estensioni sono disponibili. Tuttavia occorre ricordare che quandopdftexè predi-sposto per l’uscita tradizionale inDVI, alcuni pacchetti non funzionano o, viceversa, alcuni pacchetti non possono essere usati se l’uscita è inPDF. Il problema non si pone per le estensioniε-TEX, ma solo per quelle diPDFTEX. A tale scopo è disponibile il pacchettoifpdfche definisce il condizionale\ifpdfche è vero se l’uscita è inPDF, falso altrimenti. Normalmente è sufficiente caricare il pacchetto dopo aver dichiarato la classe, ma se si volesse distinguere fra opzioni globali nelle due uscite, è necessario ricorrere a \RequirePackage{ifpdf} \ifpdf \documentclass[...]{article} \else \documentclass[...]{article} \fi
perché\usepackagenon è definito prima che sia dato il comando\documentclass. È l’unico caso in cui\RequirePackageva usato in un documento.tex.
Abbiamo già detto cheε-TEX mette a disposizione un numero maggiore di registri,
ma che occorre caricare il pacchettoetexper accedere ai nuovi. Questo perché LATEX ha una definizione dell’allocazione legata ai 256 classici registri di ciascun tipo.
Esistono già alcuni pacchetti che usano le estensioniε-TEX, per esempiobigfoot, siunitxefrontespizio. Questo non deve spaventare l’utente finale, che al massimo si troverà di fronte al problema di aggiornare la sua distribuzione TEX, del resto una buona cosa da fare.
Vedremo dapprima le estensioniε-TEX, con qualche esempio d’uso, poi le nuove
primitive messe a disposizione daPDFTEX.
7.1 Nuovi condizionali inε-TEX
Ci sono due nuovi condizionali:\ifdefinede\ifcsname. Il test per il primo è un comando, il risultato è vero se il comando è definito, falso altrimenti. Per evitare in certi casi di definirsi un nuovo condizionale è sufficiente usare un comando ‘sen-tinella’; per esempio, nella scrittura di un pacchetto abbiamo bisogno di eseguire una certa parte di codice se viene data l’opzionexyz, un’altra se viene data l’opzione noxyz: \DeclareOption{xyz}{\let\pack@xyz=T} \DeclareOption{noxyz}{\let\pack@xyz\@undefined} ... \ProcessOptions\relax ... \ifdefined\pack@xyz 〈codice per l’opzionexyz〉 \else
〈codice per l’opzionenoxyz〉 \fi
...
Il modo classico è di dire\newif\ifpack@xyz(come già detto,packè un prefisso qualsiasi scelto per le macro private del pacchetto):
\newif\ifpack@xyz
\DeclareOption{xyz}{\pack@xyztrue} \DeclareOption{noxyz}{\pack@xyzfalse} ...
\ifpack@xyz
〈codice per l’opzionexyz〉 \else
〈codice per l’opzionenoxyz〉 \fi
Con il metodo descritto prima si risparmiano ben tre comandi. Il secondo condizionale,\ifcsnameha un test della forma
〈codice 〉\endcsname
con un 〈codice 〉 che possa essere usato fra\csnamee\endcsname. Per esempio, \ifcsname pack@xyz\endcsname
è del tutto equivalente al codice precedente con\ifdefined. C’è però una diffe-renza notevole rispetto a\csname: se il token complesso che viene prodotto dal condizionale non è definito, rimane tale e non diventa equivalente a\relax.
*La definizione usuale di\@ifundefinedusa\csnamee quindi il token che si prova al-la fine risulta definito ed equivalente a\relax. Si potrebbe perciò pensare di ridefinire \@ifundefinedcon
\def\@ifundefined#1{% \ifcsname #1\endcsname \expandafter\@secondoftwo \else \expandafter\@firstoftwo \fi}
ma non è così semplice. Infatti la definizione di LATEX è \def\@ifundefined#1{% \expandafter\ifx\csname#1\endcsname\relax \expandafter\@firstoftwo \else \expandafter\@secondoftwo \fi}
e si vede che la macro di fatto controlla solo se il token prodotto con\csnameè o no equivalente a\relax. Per esempio
\@ifundefined{relax}{NONDEF}{DEF}
stamperebbe ‘NONDEF’, perché\relaxè equivalente a\relax. Purtroppo non è possibile verificare in quali pacchetti (e ce ne sono) si usi questa particolarità. Naturalmente nulla vieta che nel nostro pacchetto ci definiamo una macro\@newifundefinedche usi il metodo con \ifcsnameche risparmia memoria.
Un’altra estensione che riguarda i condizionali è\unlessche, messo davanti a un condizionale (primitivo o definito, ma escluso\ifcase) rovescia il valore di vero e falso. Questo può rendere più semplici certe macro complicate dalle negazioni di condizioni.
Un terzo condizionale è\iffontcharil cui test è 〈font 〉〈numero 〉, dove 〈font 〉 è un comando che sceglie un font e il 〈numero 〉 è nell’intervallo 0–255. L’uso principale è del tipo
\iffontchar\font‘A <V>\else<F>\fi
perché in questo contesto\fontindica il font in uso. Il condizionale è vero se il font contiene un carattere al posto 〈numero 〉. Per esempio, certi font non hanno tutti i caratteri per la codifica di outputTS1(simboli vari) e può essere necessario questo sistema per prendere provvedimenti al riguardo.