Is there a standard way to define mutually exclusive options in a package?
Could someone provide a simple example?
Currently I am mostly intrested in LaTeX2e but it would be nice to see a LaTeX3 implementation also.
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It only takes a minute to sign up.
Sign up to join this communityThe kvoptions
package, which provides key-value type options, also has the possibility to define complementary options:
\DeclareBoolOption{draft}
\DeclareComplementaryOption{final}{draft}
This generates an if-switch which is set to true by draft
but to false by final
.
If you however mean otherwise mutually exclusive options, e.g. three options where only one can be used, then you best define three if-switches which are set to true by its corresponding option. The options then either set the if-switches of the other options to false or raise an error or warning if one of these if-switches are already set to true.
% package 'foo'
% Example: three mutually options alpha, beta, gamma; last one
\newif\iffoo@alpha
\newif\iffoo@beta
\newif\iffoo@gamma
\DeclareOption{alpha}{\foo@alphatrue\foo@betafalse\foo@gammafalse}
\DeclareOption{beta}{\foo@alphafalse\foo@betatrue\foo@gammafalse}
\DeclareOption{gamma}{\foo@alphafalse\foo@betafalse\foo@gammatrue}
\ProcessOptions*% process options it the order the user provides them
% package body:
\iffoo@alpha
% alpha code
\fi
\iffoo@beta
% beta code
\fi
\iffoo@gamma
% gamma code
\fi
or with error messages:
\def\foo@mutopterr{%
\PackageError{foo}{Options 'alpha', 'beta' and 'gamma' are mutually exclusive.}{}%
}%
\DeclareOption{alpha}{%
\iffoo@beta\foo@mutopterr\fi
\iffoo@gamma\foo@mutopterr\fi
\foo@alphatrue\foo@betafalse\foo@gammafalse
}
\DeclareOption{beta}{%
\iffoo@alpha\foo@mutopterr\fi
\iffoo@gamma\foo@mutopterr\fi
\foo@alphafalse\foo@betatrue\foo@gammafalse
}
\DeclareOption{gamma}{%
\iffoo@alpha\foo@mutopterr\fi
\iffoo@beta\foo@mutopterr\fi
\foo@alphafalse\foo@betafalse\foo@gammatrue
}
\count
register initialized to -1
instead? The three (or more) options would simply be 1, 2, 3, (etc.) and when using them, you simply use \ifcase
. To ensure that they're mutually exclusive, you just use a single \ifnum
check.
default
option from the set alpha
, beta
and gamma
?
Could someone provide a simple example?
The standard LaTeX document classes take onesided
and twosided
, draft
and final
, titlepage
and notitlepage
as options, each pair of which is mutually exclusive. There are also mutually exclusive options to specify the paper size and point size, but here there are multiple choices rather than two. The implementations of these options are a simple `\DeclareOption'. An example for the fictitious foo/nofoo option:
\newif\@foo
\@footrue % Makes foo the default; use \@foofalse if the default is nofoo
\DeclareOption{foo}{\@footrue}
\DeclareOption{nofoo}{\@foofalse}
A key question: How to address a user who specifies contradictory options? For example, specify twosided,final,onesided,draft
as the options to one of the base LaTeX document classes and you will get (without complaints) a two-sided document formatted in final mode.
This is because the base LaTeX document classes use \ProcessOption
to process the options. \ProcessOption
walks through the options in the order the options are declared. If those classes had used \ProcessOption*
instead of \ProcessOption
, the consequences of twosided,final,onesided,draft
would be a one-sided document in draft form. With \ProcessOption
the winner in the case of conflict is the package itself while with \ProcessOption*
the winner in case of conflict is the user of the package.
If you want the implementation to scold the user for specifying mutually exclusive options you will have to do something a bit more involved.
\ProcessOptions*
instead of \ProcessOptions
to have the last one win. The former process the options in the calling order while the later in the order of options declarations.
Below is an example that I use for a list of option where only one can be active. You can also specify a default option.
\newcommand\opt@select[1]{@#1@select}
\newcommand\opt@ifpre[1]{@#1@}
\newcommand\DeclareOptionList[3]{%
\@namedef{\opt@select{#1}}{#3}%-- default
\edef\opt@list{\zap@space#2 \@empty}%
\@for\opt@item:=\opt@list\do{%
\expandafter\newif\csname if\opt@ifpre{#1}\opt@item\endcsname
\edef\opt@temp{%
\noexpand\DeclareOption{\opt@item}%
{\def\expandafter\noexpand\csname\opt@select{#1}\endcsname{\opt@item}}}%
\opt@temp}}
\newcommand\SetListOption[1]{%
\def\opt@set{\@nameuse{\opt@select{#1}}}%
\@nameuse{\opt@ifpre{#1}\opt@set true}}
\newcommand\IfListOption[3]{%
\csname if\opt@ifpre{#1}#2\endcsname #3\fi}
%____ Process options ___________________________________________
\DeclareOptionList{layout}{goldenblock,a5block,wideblock,stdblock}{goldenblock}
\DeclareOptionList{fonts}{cm,charter,fourier,times}{cm}
\ProcessOptions*\relax
\AtEndOfPackage{\let\@unprocessedoptions\relax}
\SetListOption{layout}%--- Activate layout option
\SetListOption{fonts}%---- Activate font option
%____ Page Layout _______________________________________________
\RequirePackage[a4paper]{geometry}
\IfListOption{layout}{goldenblock}{%
\geometry{text={0.6667\paperwidth, 1.0787\paperwidth}}%
\geometry{hmarginratio=1:1}%
\geometry{vmarginratio=2:3}%
\geometry{marginparwidth=50pt}}
\IfListOption{layout}{a5block}{%
\geometry{text={0.5\paperheight, \paperwidth}}%
\geometry{hmarginratio=1:1}%
\geometry{vmarginratio=2:3}%
\geometry{marginparwidth=50pt}}
\IfListOption{layout}{wideblock}{%
\geometry{margin=25mm}%
\geometry{marginparwidth=50pt}}
\IfListOption{layout}{stdblock}{}%----- Latex default type block ----
%____ Fonts _________________________________________________________
\IfListOption{fonts}{cm}{%
\RequirePackage[T1]{fontenc}
\RequirePackage{textcomp}
\RequirePackage{lmodern}
\RequirePackage{bm}}
: