Beim Erstellen einer eigenen Dokumentenklasse ist mir aufgefallen, dass entgegen der Beschreibung im clsguide Optionen, mit denen die Klasse aufgerufen wird, in bestimmen Fällen an die Basisdokumentenklasse weitergegeben werden, selbst wenn sie als klasseneigene Optionen definiert wurden. Das folgende Minimalbeispiel ohne praktische Anwendung verdeutlicht das Problem.

Dokumentenklasse:

Open in Online-Editor
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{testklasse}

\newcommand{\Optionserkennung}{Es wurde keine klasseneigene Option erkannt.}

\DeclareOption{landscape}{\renewcommand{\Optionserkennung}{Die klasseneigene Option landscape wurde erkannt.}}

\DeclareOption*{\PassOptionsToClass{\CurrentOption}{scrartcl}}
\ProcessOptions\relax

\LoadClass{scrartcl}

Dokument, das die Klasse verwendet:

Open in Online-Editor
\documentclass[landscape]{testklasse}
\usepackage{blindtext}

\begin{document}
\Optionserkennung

\blindtext
\end{document}

In der Ausgabe erkennt man, dass die Option landscape zwar als klasseneigene Option erkannt wurde, aber trotzdem an die Klasse scrartcl weitergegeben wurde. Das gleiche passiert entsprechend auch bei der Option pagesize, nicht aber beispielsweise bei der Option leqno und auch nicht mit der Basisdokumentenklasse article. Wie lässt sich dieses Verhalten erklären? (Und welche Wirkung hat eigentlich der relax-Befehl nach ProcessOptions?)

gefragt 07 Apr '15, 19:05

Cletus's gravatar image

Cletus
1.6k75866
Akzeptiert-Rate: 75%

3

Auch wenn die Option nicht an die Basisdokumentenklasse weitergegeben wird, ist sie eine globale Option für Pakete. scrartcl lädt das Paket typearea, welches die Option landscape kennt und deshalb für die Ausgabe des Dokuments im Querformat sorgt. Und auch pagesize ist eine Option des Paketes typearea.

Von \ProcessOptions gibt es auch eine Sternform. Das \relax wird verwendet, um die Suche nach diesem Stern zu verhindern.

(08 Apr '15, 01:25) esdd

Das klingt plausibel. Allerdings finde ich dieses Verhalten etwas problematisch, da man als „Weiterverwerter“ einer Dokumentenklasse nicht unbedingt weiß, welche Pakete diese aufruft. (In der objektorientierten Programmierung wäre so etwas inakzeptabel, Stichwort Kapselung.)

(08 Apr '15, 19:09) Cletus

Klassen bei LaTeX haben nichts mit den Klassen in objektorientierten Sprachen zu tun. Man sollte das nicht in einen Topf werfen. Die einzige Kapselung und die einzigen Scopes, die TeX kennt, sind Gruppen. Will man LaTeX wirklich verstehen, muss man TeX verstehen. Will man TeX verstehen sollte man sich weniger mit objektorientieren als mit Makro-Sprachen beschäftigen.

Und in der KOMA-Script-Anleitung ist sehr wohl dokumentiert, dass die KOMA-Script-Klassen typearea verwenden.

(08 Apr '15, 19:56) saputello

Wie @esdd in ihrem Kommentar schon richtig bemerkt hat, ist Option landscape bei KOMA-Script gar nicht eine Option der Klasse, sondern eine Option des Pakets typearea. Dieses Paket wird von allen KOMA-Script-Klassen geladen, um Satzspiegel- und Randeinstellungen vorzunehmen. Die Optionen, die man bei \documentclass angibt, sind wiederum nicht nur Optionen für die Klasse, sondern globale Optionen. Diese globalen Optionen werden bei \ProcessOptions und \ProcessOptions* in einem Paket nach der Abarbeitung der Optionen, die nur für das Paket bestimmt waren, abgearbeitet, soweit das Paket explizit eine gleichnamige Option deklariert hat. In einem Paket werden globale Optionen dagegen nicht an \DeclareOption* weitergereicht.

Will man nun also, dass eine Option aus \documentclass weder für die Unterklasse noch für eines der geladenen Pakete sichtbar ist, so muss man sie aus der Liste der globalen Optionen entfernen. Das kann man beispielsweise so machen:

Open in Online-Editor
\RequirePackage{filecontents}
\begin{filecontents}{testklasse.cls}
\ProvidesClass{testklasse}[2014/04/25 demonstation objekt only]
\providecommand*{\removefromglobaloptions}[1]{% #1 ist die unerwünschte Option
  \begingroup
    \let\reserved@a\@classoptionslist% \reserved@a bekommt den Inhalt der globalen Optionen
    \let\@classoptionslist\@empty% die globalen Optionen werden gelöscht
    \edef\reserved@b{#1}%
    \expandafter\@for\expandafter\reserved@a\expandafter:\expandafter=%
    \reserved@a\do{% jede ehemals globale Option
      \ifx\reserved@a\reserved@b\else% die nicht der unerwünschten Option entspricht
        % wieder der Liste der globalen Optionen hinzufügen:
        \ifx\@classoptionslist\@empty\global\let\@classoptionslist\reserved@a
        \else\expandafter\g@addto@macro\expandafter\@classoptionslist
             \expandafter{\expandafter,\reserved@a}\fi
      \fi
    }%
  \endgroup
}
\DeclareOption{twoside}{\typeout{Option `twoside' used.}\removefromglobaloptions{twoside}}
\DeclareOption{titlepage}{\typeout{Option `titlepage' used.}\removefromglobaloptions{titlepage}}
\DeclareOption{landscape}{\typeout{Option `landscape' used.}\removefromglobaloptions{landscape}}
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{scrartcl}}
\ProcessOptions\relax
\LoadClass{scrartcl}
\end{filecontents}
\documentclass[twoside,titlepage,landscape,ngerman]{testklasse}
\usepackage{babel}
\usepackage{mwe}
\begin{document}
\title{Test}
\author{Me}
\maketitle
\tableofcontents
\Blinddocument
\end{document}

Würde man nun später im Dokument beispielsweise geometry laden, so wären twoside und landscape auch für dieses Paket nicht existent. Soll das jedoch nur für Pakete gelten, die scrartcl automatisch lädt, so müsste man die Liste der globalen Optionen zusätzlich sichern und später wiederherstellen:

Open in Online-Editor
\RequirePackage{filecontents}
\begin{filecontents}{testklasse.cls}
\ProvidesClass{testklasse}[2014/04/25 demonstation objekt only]
\providecommand*{\removefromglobaloptions}[1]{%
  \begingroup
    \let\reserved@a\@classoptionslist
    \let\@classoptionslist\@empty
    \edef\reserved@b{#1}%
    \expandafter\@for\expandafter\reserved@a\expandafter:\expandafter=%
    \reserved@a\do{%
      \ifx\reserved@a\reserved@b\else
        \ifx\@classoptionslist\@empty\global\let\@classoptionslist\reserved@a
        \else\expandafter\g@addto@macro\expandafter\@classoptionslist
             \expandafter{\expandafter,\reserved@a}\fi
      \fi
    }%
  \endgroup
}
\let\all@classoptionslist\@classoptionslist
\DeclareOption{twoside}{\typeout{Option `twoside' used.}\removefromglobaloptions{twoside}}
\DeclareOption{titlepage}{\typeout{Option `titlepage' used.}\removefromglobaloptions{titlepage}}
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{scrartcl}}
\ProcessOptions\relax
\LoadClass{scrartcl}
\let\@classoptionslist\all@classoptionslist
\end{filecontents}
\documentclass[twoside,titlepage,ngerman]{testklasse}
\usepackage{babel}
\usepackage{geometry}
\usepackage{mwe}
\begin{document}
\title{Test}
\author{Me}
\maketitle
\tableofcontents
\Blinddocument
\end{document}

Aber: Man kann auf diese Weise nicht verhindern, dass jemand per \PassOptionsToClass{titlepage}{scrartcl} doch eine Titelseite einschaltet oder per \PassOptionsToPackage{twoside,landscape}{typearea} doch wieder doppelseitiges Querformat aktiviert. Man müsste dazu zusätzlich die Optionen aus den Listen der Klassenoptionen von scrartcl und der Paketoptionen von typearea vor dem Laden der Klasse bzw. des Pakets entfernen.

BTW: Ich bin mir bewusst, dass es für das Entfernen von Optionen aus der aktuellen Liste schnelleren Code gibt. Obiger Code erscheint mir aber nicht nur recht zuverlässig, sondern auch gerade noch verständlich. Irgendwelche Pattern-Match-Argument-Tricks sind das hingegen nicht. Deshalb habe ich diesen Weg gewählt.

Permanenter link

beantwortet 25 Apr '15, 15:19

saputello's gravatar image

saputello
11.1k174365
Akzeptiert-Rate: 51%

bearbeitet 25 Apr '15, 18:57

esdd's gravatar image

esdd
17.8k284257

@saputello Danke :-)

(25 Apr '15, 19:00) esdd
Deine Antwort
Vorschau umschalten

Folgen dieser Frage

Per E-Mail:

Wenn sie sich anmelden, kommen Sie für alle Updates hier in Frage

Per RSS:

Antworten

Antworten und Kommentare

Markdown-Grundlagen

  • *kursiv* oder _kursiv_
  • **Fett** oder __Fett__
  • Link:[Text](http://url.com/ "Titel")
  • Bild?![alt Text](/path/img.jpg "Titel")
  • nummerierte Liste: 1. Foo 2. Bar
  • zum Hinzufügen ein Zeilenumbruchs fügen Sie einfach zwei Leerzeichen an die Stelle an der die neue Linie sein soll.
  • grundlegende HTML-Tags werden ebenfalls unterstützt

Frage-Themen:

×14
×13

gestellte Frage: 07 Apr '15, 19:05

Frage wurde gesehen: 8,485 Mal

zuletzt geändert: 25 Apr '15, 19:00