[.cls] Caricare diversa classe base a seconda delle opzioni

  • Creatore
    Topic
  • #67091
    Up
    0
    Down
    ::


    Sto scrivendo una classe che può caricare tre classi diverse a seconda delle opzioni passatele: due sono KOMA e una è memoir. Ad ognuna di esse devono essere passate delle opzioni predefinite, ma se l’utente ne specifica altre, queste devono avere la precedenza su quelle predefinite. Siccome elaborano le opzioni nell’ordine in cui vengono passate (non nell’ordine in cui sono dichiarate), devo passare le opzioni predefinite prima di quelle specificate dall’utente, quindi prima di \ProcessOptions. D’altro canto, per sapere a quale classe passarle, devo aver letto almeno alcune opzioni, cosa che avviene dopo \ProcessOptions.

    Per risolvere la faccenda ho pensato di usare la versione non asteriscata di \ProcessOptions, per controllare subito se sono state passate alla mia classe le opzioni che fanno da discriminante per stabilire la classe base.

    Esistono comunque diversi modi di procedere, vorrei capire quale è il migliore.

    Primo metodo.
    `
    \newif\if@primodiscriminante
    \newif\if@secondodiscriminante

    \DeclareOption{primodiscriminante}{\@primodiscriminantetrue}
    \DeclareOption{secondodiscriminante}{\@secondodiscriminantetrue}
    \DeclareOption{altraopzionea}{…}
    \DeclareOption{altraopzioneb}{…}
    \DeclareOption*{
    \if@primodiscriminante
    \PassOptionsToClass{\CurrentOption}{classe1}
    \else\if@secondodiscriminante
    \PassOptionsToClass{\CurrentOption}{classe2}
    \else
    \PassOptionsToClass{\CurrentOption}{classe3}
    \fi\fi}

    \PassOptionsToClass{…}{classe1}
    \PassOptionsToClass{…}{classe2}
    \PassOptionsToClass{…}{classe3}

    \ProcessOptions\relax

    \if@primodiscriminante
    \LoadClass{classe1}
    \else\if@secondodiscriminante
    \LoadClass{classe2}
    \else
    \LoadClass{classe3}
    \fi\fi`
    Lo svantaggio è che passo inutilmente delle opzioni a classi che non caricherò. Inoltre gli if/else vengono valutati un numero variabile di volte.

    Secondo metodo.
    `
    \newif\if@primodiscriminante
    \newif\if@secondodiscriminante

    \def\@set@baseclass{
    \if@primodiscriminante\else
    \if@secondodiscriminante\else
    \def\@baseclass{classe3}
    \PassOptionsToClass{…}{classe3}
    \fi
    \fi
    \let\@set@baseclass\relax}

    \DeclareOption{primodiscriminante}{
    \@primodiscriminantetrue
    \def\@baseclass{classe1}
    \PassOptionsToClass{…}{classe1}
    }
    \DeclareOption{secondodiscriminante}{
    \@secondodiscriminantetrue
    \if@primodiscriminante\relax\else
    \def\@baseclass{classe2}
    \PassOptionsToClass{…}{classe2}
    \fi
    }
    \DeclareOption{altraopzionea}{…}
    \DeclareOption{altraopzioneb}{…}
    \DeclareOption*{
    \@set@baseclass
    \PassOptionsToClass{\CurrentOption}{\@baseclass}}

    \ProcessOptions\relax
    \@set@baseclass
    \LoadClass{\@baseclass}`
    È una soluzione ibrida: alcune cose le faccio subito, altre le differisco. Gli if/else vengono valutati più volte (ma il numero è fisso).

    Terzo metodo.
    `
    \newif\if@primodiscriminante
    \newif\if@secondodiscriminante

    \def\@set@baseclass{
    \if@primodiscriminante
    \def\@baseclass{classe1}
    \PassOptionsToClass{…}{classe1}
    \else
    \if@secondodiscriminante
    \def\@baseclass{classe2}
    \PassOptionsToClass{…}{classe2}
    \else
    \def\@baseclass{classe3}
    \PassOptionsToClass{…}{classe3}
    \fi
    \fi
    \let\@set@baseclass\relax}

    \DeclareOption{primodiscriminante}{\@primodiscriminantetrue}
    \DeclareOption{secondodiscriminante}{\@secondodiscriminantetrue}
    \DeclareOption{altraopzionea}{…}
    \DeclareOption{altraopzioneb}{…}
    \DeclareOption*{
    \@set@baseclass
    \PassOptionsToClass{\CurrentOption}{\@baseclass}}

    \ProcessOptions\relax
    \@set@baseclass
    \LoadClass{\@baseclass}`
    Ritardo la scelta della classe base e il passaggio delle opzioni al momento in cui effettivamente servono e mi assicuro di farlo una e una volta sola

    Il terzo metodo è quello che preferisco. Può andar bene? Esistono tecniche migliori?

    Grazie,
    Antonio

    PS: curiosità: a che serve \relax dopo \ProcessOptions?

Visualizzazione 8 filoni di risposte
  • Autore
    Risposte
    • #67092
      OldClaudio
      Partecipante
        Up
        0
        Down
        ::


        Confesso che non le ho provate, ma ho solo letto il codice; da questa lettura solo la terza versine mi pare che possa funziinare, nel senso che le altre due secondo la mia lettura non funzionano.

        Mi spiego: finché dichiari una opzione con \DeclareOption il suo codice viene messo in una lista, ma non viene eseguito finché non viene eseguito il comando \ProcessOptions. Dunque nei primi due casi i vari \if che corrispondono ai discriminanti non sono impostati e di conseguenza le strutture condizionali funzionano solo con i valori di default di quelle variabili booleane, cioè false.

        Ci sarebbe anche la possibilità di usare \ExecuteOption; questo crea una lista di opzioni da usare, con la clausola che se qualche opzione non è definita, viene silenziosamente ignorata.

        Ci sarebbe anche da sfruttare il fatto che le opzioni non definite per una classe non producono errori, anche se funzionano daopzioni globali, ma viene solo emesso un messaggio “Option … not used”, che appare solo nel file .log.

        Però secondo me la terza variante funziona benissimo; io ho incontrato delle difficoltà a passare opzioni a memoir dalla classe che la chiama, ma forse c’è ancora qualcosa che mi sfugge in tutto questo meccanismo.

      • #67093
        Up
        0
        Down
        ::


        Prima di tutto, grazie!

        OldClaudio” post=66073Confesso che non le ho provate, ma ho solo letto il codice; da questa lettura solo la terza versine mi pare che possa funziinare, nel senso che le altre due secondo la mia lettura non funzionano.

        Mi spiego: finché dichiari una opzione con \DeclareOption il suo codice viene messo in una lista, ma non viene eseguito finché non viene eseguito il comando \ProcessOptions. Dunque nei primi due casi i vari \if che corrispondono ai discriminanti non sono impostati e di conseguenza le strutture condizionali funzionano solo con i valori di default di quelle variabili booleane, cioè false.

        Ci sarebbe anche la possibilità di usare \ExecuteOption; questo crea una lista di opzioni da usare, con la clausola che se qualche opzione non è definita, viene silenziosamente ignorata.

        Ci sarebbe anche da sfruttare il fatto che le opzioni non definite per una classe non producono errori, anche se funzionano daopzioni globali, ma viene solo emesso un messaggio “Option … not used”, che appare solo nel file .log.

        Però secondo me la terza variante funziona benissimo;

        Ho provato le varie soluzioni e a me paiono funzionare. Ho letto la clsguide e non vorrei aver preso un abbaglio, ma i condizionali vengono eseguiti indirettamente da \ProcessOptions (perché inseriti in qualche \DeclareOption) oppure direttamente dopo \ProcessOptions.

        Comunque, se ti sembra che la terza vada bene, allora uso quella. 🙂

        io ho incontrato delle difficoltà a passare opzioni a memoir dalla classe che la chiama, ma forse c’è ancora qualcosa che mi sfugge in tutto questo meccanismo.

        L’ho concepito così: se non ci sono opzioni non previste (niente \DeclareOption*, con l’asterisco), allora viene espanso il \my@configbaseclass che segue \ProcessOptions, e che quindi usa i condizionali correttamente impostati. Se invece viene passata un’opzione con \DeclareOption*, allora sono state certamente già eseguite (se ci sono) le opzioni che fanno da discriminante, quindi chiamo \my@configbaseclass prima di passare le nuove opzioni alla classe base. In ogni caso, se non sbaglio qualcosa, \my@configbaseclass si annulla automaticamente dopo la prima chiamata.

        Il vincolo è che devo passare alla classe base (qualunque essa sia) le relative opzioni predefinite prima che vengano passate altre opzioni con \DeclareOption* (con l’asterisco), in modo che queste ultime abbiano la precedenza.

        Questo è l’esempio (quasi) reale:
        `
        \RequirePackage{filecontents}

        \begin{filecontents}{classe.cls}
        \NeedsTeXFormat{LaTeX2e}
        \ProvidesClass{classe}[2011/12/01 v0.1 Classe di prova]

        \newif\ifmy@memoir
        \newif\ifmy@article
        % altri newif

        \def\print{\typeout{\CurrentOption}}

        \def\my@configbaseclass{
        \ifmy@memoir
        \def\my@baseclass{memoir}
        \ifmy@article
        \PassOptionsToClass{10pt,a4paper,article}{memoir}
        \else
        \PassOptionsToClass{10pt,a4paper}{memoir}
        \fi
        \else
        \ifmy@article
        \def\my@baseclass{scrartcl}
        \PassOptionsToClass{10pt,abstract=true}{scrartcl}
        \else
        \def\my@baseclass{scrbook}
        \PassOptionsToClass{10pt}{scrbook}
        \fi
        \fi
        \let\my@configbaseclass\relax}

        \DeclareOption{memoir}{\print\my@memoirtrue}
        \DeclareOption{article}{\print\my@articletrue}
        % altre opzioni
        \DeclareOption*{
        \print
        \my@configbaseclass
        \PassOptionsToClass{\CurrentOption}{\my@baseclass}
        }

        \ProcessOptions\relax
        \my@configbaseclass
        \LoadClass{\my@baseclass}
        \end{filecontents}

        %\documentclass[b5paper,article,11pt,memoir,landscape]{classe}
        \documentclass[b5paper,article,11pt,headinclude]{classe}

        \usepackage[T1]{fontenc}
        \usepackage[utf8]{inputenc}
        \usepackage[italian]{babel}

        \begin{document}
        ciao!
        \end{document}`
        Viene passata correttamente sia l’opzione “landscape” a memoir che “headinclude” a scrbook.

        Sperando che non ci siano altri bug… 🙂

        Grazie di nuovo!

      • #67094
        Up
        0
        Down
        ::


        Se vuoi la mia opinione, eccola. Scegli: o memoir o scrbook. Hanno modi diversi di gestire le cose, non c’è speranza di farle collaborare in modo decente. A meno di non voler tradurre tutto ciò che fa una nel linguaggio dell’altra.

        Ciao
        Enrico

      • #67095
        Up
        0
        Down
        ::

        egreg9″ post=66078Se vuoi la mia opinione, eccola. Scegli: o memoir o scrbook. Hanno modi diversi di gestire le cose, non c’è speranza di farle collaborare in modo decente. A meno di non voler tradurre tutto ciò che fa una nel linguaggio dell’altra.

        Ciao
        Enrico

        La tua opinione è sempre preziosa.

        Nella mia idea originaria, mi sarei basato esclusivamente sulle classi KoMa. Claudio suggeriva di usare memoir: l’idea mi interessa, se è vero che le classi LaTeX3 sono molto più vicine a memoir che alle classi standard LaTeX2e (almeno come concezione, ho letto). Così ho cercato di combinare le due vie, ma andando avanti mi sto rendendo conto che il codice in comune in effetti non è moltissimo… Non vorrei però sin d’ora abbandonarmi esclusivamente a memoir: in linea di principio preferisco l’organizzazione modulare di classi standard e classi KoMa e non vorrei distaccarmene bruscamente. Del resto avere su memoir un layout familiare (classicthesis) mi spingerebbe a conoscere meglio memoir.

        Sto programmando a tempo perso per esplorare un po’ le varie soluzioni, quindi in generale non ho vincoli temporali, né ancora un progetto ben definito: voglio semplicemente vedere che si può fare.

        L’unica cosa che davvero mi serve è un classicthesis in forma di classe con alcune ben precise cose modificate: sto portando avanti le due cose in parallelo.

        PS1: trovi delle falle TeXniche nella soluzione che ho descritto prima?
        PS2: è solo una curiosità, mi sapresti dire perché c’è l’abitudine di mettere \relax dopo \ProcessOptions?

      • #67096
        Up
        0
        Down
        ::

        antonio.macrì” post=66084PS1: trovi delle falle TeXniche nella soluzione che ho descritto prima?
        PS2: è solo una curiosità, mi sapresti dire perché c’è l’abitudine di mettere \relax dopo \ProcessOptions?

        1. Non mi pare: quello di definire una macro che contiene il nome della classe è un trucco che ho consigliato anch’io.

        2. Perché \ProcessOptions può essere seguito da * e quindi, per via di come funziona \@ifstar, mangia gli spazi che seguono e può creare problemi con il primo comando successivo. Mettendo \relax si ferma subito il processo.

        Ciao
        Enrico

      • #67097
        Up
        0
        Down
        ::

        egreg9″ post=66087

        PS2: è solo una curiosità, mi sapresti dire perché c’è l’abitudine di mettere \relax dopo \ProcessOptions?

        2. Perché \ProcessOptions può essere seguito da * e quindi, per via di come funziona \@ifstar, mangia gli spazi che seguono e può creare problemi con il primo comando successivo. Mettendo \relax si ferma subito il processo.

        Ciao
        Enrico

        Scusa se ti disturbo ancora, Enrico. Mi spiegheresti perché? Non ci arrivo… 🙁

      • #67098
        Up
        0
        Down
        ::

        antonio.macrì” post=66094

        Scusa se ti disturbo ancora, Enrico. Mi spiegheresti perché? Non ci arrivo… 🙁

        Mi pare che il problema sia dovuto a ciò che non può andare prima di \ProcessOptions; il processo di \@ifnextchar espande ciò che viene dopo per vedere se è uno spazio (e buttarlo); quindi può succedere che venga espanso qualcosa che è proibito prima di \ProcessOptions (tipicamente \LoadClass o \LoadPackage).

        Ciao
        Enrico

      • #67099
        Up
        0
        Down
        ::

        egreg9″ post=66095Mi pare che il problema sia dovuto a ciò che non può andare prima di \ProcessOptions; il processo di \@ifnextchar espande ciò che viene dopo per vedere se è uno spazio (e buttarlo); quindi può succedere che venga espanso qualcosa che è proibito prima di \ProcessOptions (tipicamente \LoadClass o \LoadPackage).

        Ciao
        Enrico

        Ok, credo di aver intuito qualcosa, domani ci ragiono a mente più fresca.

        Grazie!

      • #67100
        Up
        0
        Down
        ::


        Pagina 24 di clsguide.pdf

        Ciao
        Enrico

    Visualizzazione 8 filoni di risposte
    • Devi essere connesso per rispondere a questo topic.

    Go to top