Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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.

share|improve this question

3 Answers 3

up vote 8 down vote accepted

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
}
share|improve this answer
    
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 '11 at 10:17
    
@TH.: Indeed, this is a good idea, especially for a large number of mutually options. –  Martin Scharrer Jun 20 '11 at 10:21
    
@MartinScharrer: Can your solution be improved so it will support a default option from the set alpha, beta and gamma? –  Dror Aug 27 '13 at 10:38
    
@TH: Can you post a full answer? Your comment is rather cryptic... –  Dror Aug 27 '13 at 10:38

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.

share|improve this answer
2  
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 '11 at 6:19
    
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. –  David Hammen Jun 20 '11 at 9:49
    
thank you! –  pmav99 Jun 26 '11 at 12:10

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

:
share|improve this answer
    
thank you! –  pmav99 Jun 26 '11 at 12:09

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.