13

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.

3 Answers 3

13

The 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
}
4
  • This seems like it'd lead to a lot of duplicated work. Why not use a single \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.
    – TH.
    Jun 20, 2011 at 10:17
  • @TH.: Indeed, this is a good idea, especially for a large number of mutually options. Jun 20, 2011 at 10:21
  • 1
    @MartinScharrer: Can your solution be improved so it will support a default option from the set alpha, beta and gamma?
    – Dror
    Aug 27, 2013 at 10:38
  • @TH: Can you post a full answer? Your comment is rather cryptic...
    – Dror
    Aug 27, 2013 at 10:38
6

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.

2
  • 3
    You also need \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.
    – Yan Zhou
    Jun 20, 2011 at 6:19
  • 1
    Thanks for that qualification, Yan. I modified my response, particularly since I happened to choose an example that uses the unstarred version of ProcessResponse. I use the starred version because I have options that are mutually interacting rather than mutually exclusive, and I forgot that essential detail. Jun 20, 2011 at 9:49
4

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}}

:
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .