3

I was pretty sure that options (including unknown ones) passed at the class level were also passed to the loaded packages. Hence I don't understand why the following MCE works like a charm:

\documentclass{article}
\usepackage[acronym,nomain]{glossaries}
\makeglossaries
\newacronym{at}{at}{Acronym test}
\begin{document}
\glsaddall
\printglossary[type=\acronymtype]
\end{document}

but not the following one where the acronym option is passed to the class rather to the glossaries package:

\documentclass[acronym]{article}
\usepackage[nomain]{glossaries}
\makeglossaries
\newacronym{at}{at}{Acronym test}
\begin{document}
\glsaddall
\printglossary[type=\acronymtype]
\end{document}

This leads indeed to the following error message:

! Missing \endcsname inserted.
<to be read again>
                   \glsdefaulttype
l.6 \newacronym{at}{at}{Acronym test}

? s
OK, entering \scrollmode...
! Missing \endcsname inserted.
<to be read again>
                   \glsdefaulttype
l.6 \newacronym{at}{at}{Acronym test}

! Missing \endcsname inserted.
<to be read again>
                   \glsdefaulttype
l.6 \newacronym{at}{at}{Acronym test}


! Package glossaries Error: Glossary type '\glsdefaulttype ' has not
been defined.

[...]

Note that the following MCE (acronym option passed to the class, but no nomain glossaries option):

\documentclass[acronym]{myclass}
\usepackage{glossaries}
\makeglossaries
\newacronym{at}{at}{Acronym test}
\begin{document}
\glsaddall
%
\printglossary[type=\acronymtype]
\end{document}

does work (well, mostly: the name of the glossary is "Glossary" whereas it is "Acronym" if acronym option is passed not to the class but to glossaries directly).

BTW, passing explicitly (thanks to \PassOptionsToPackage) the acronym class option to glossaries in a personal class gives the same error:

\begin{filecontents*}{myclass.cls}
\NeedsTeXFormat{LaTeX2e}
\newif\if@acronym\@acronymfalse
\DeclareOption{acronym}{\@acronymtrue}
\ProcessOptions\relax
\LoadClass{article}
%
\RequirePackage{filehook}
\RequirePackage{hopatch}
%
\hopatch@AfterPackage{glossaries}{%
  \if@acronym
  \PassOptionsToPackage{acronym}{glossaries}%
  \fi
}
\endinput
\end{filecontents*}

\documentclass[acronym]{myclass}
\usepackage[nomain]{glossaries}
\makeglossaries
\newacronym{at}{at}{Acronym test}
\begin{document}
\glsaddall
%
\printglossary[type=\acronymtype]
\end{document}
  • I suspect that the problem is that acronym is not declared as an option in glossaries. – egreg Nov 7 '13 at 22:02
  • 1
    I know that it's possible to pass the option to glossaries, but there's no \DeclareOptionX{acronym} and I believe this is the problem. – egreg Nov 8 '13 at 9:24
  • 1
    The acronym option is defined using \define@boolkey{glossaries.sty}[gls]{acronym}[true]{...} (via xkeyval) – Nicola Talbot Nov 8 '13 at 12:19
  • 1
    @NicolaTalbot Which precisely is the problem: it's not in the list of package options, so when evaluating the list of global options, acronym is disregarded. – egreg Nov 8 '13 at 14:31
  • 2
    @egreg Yes, I agree this is the problem, but since the xkeyval manual indicates you can use this method for defining key=value package options, I think the solution needs to be provided by xkeyval rather than all the individual packages that use xkeyval to define key=value options. – Nicola Talbot Nov 8 '13 at 14:38
2

Here's a general example that illustrates the issue. Suppose I write a package called, say, test like this:

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{test}

\RequirePackage{xkeyval}
\RequirePackage{etoolbox}

\define@boolkey{test.sty}[test]{bold}[true]{}

\newcommand*{\test@animal}{parrot}
\newcommand*{\test@parrot@sound}{squawk}
\newcommand*{\test@dog@sound}{woof}
\newcommand*{\test@duck@sound}{quack}
\newcommand*{\test@cow@sound}{moo}

\define@choicekey{test.sty}{animal}{dog,duck,cow}{%
  \renewcommand*{\test@animal}{#1}%
}

\newcommand*{\test@adj}{silly}

\define@key{test.sty}{adj}{\renewcommand*{\test@adj}{#1}}

\ProcessOptionsX

\newcommand*{\test}{{\iftestbold \bfseries \fi The \test@adj\ 
\test@animal\ said ``\csuse{test@\test@animal @sound}!''}}

\endinput

Here's an example document that uses this class:

\documentclass{article}

\usepackage[bold,adj={giant killer},animal=duck]{test}

\begin{document}
\test
\end{document}

This produces:

The giant killer duck said "quack!"

However, as mentioned in the comments, these options can't be passed via the document class options. If I try:

\documentclass[animal=duck]{article}

I get the warning:

LaTeX Warning: Unused global option(s):
    [animal=duck].

Even worse, if I try:

\documentclass[animal={duck}]{article}

I get the error:

! LaTeX Error: Missing \begin{document}.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.123 \input
            {size1\@ptsize.clo}

In general, I'm not very keen on the idea of specifying package options in \documentclass as it's possible there may be multiple packages that have the same options. If the user is not aware of this, unexpected results can occur. (This is evident by the number of times there is a question either here or on another site or newsgroup from a user who can't understand why their included images are all in draft mode.) In fact, babel language options are the only options I'd recommend being passed with the document class options (but only because babel doesn't provide a convenient list of loaded languages).

So what are the alternatives?

Packages usually provide commands that can be used instead of package options. Here is an improvement to the package design:

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{test}

\RequirePackage{xkeyval}
\RequirePackage{etoolbox}

\define@boolkey{test.sty}[test]{bold}[true]{}

\newcommand*{\test@animal}{parrot}
\newcommand*{\test@parrot@sound}{squawk}
\newcommand*{\test@dog@sound}{woof}
\newcommand*{\test@duck@sound}{quack}
\newcommand*{\test@cow@sound}{moo}

\newcommand*{\setanimal}[1]{%
  \ifcsdef{test@#1@sound}%
  {%
     \renewcommand*{\test@animal}{#1}%
  }%
  {%
     \PackageError{test}{Unknown animal `#1'}{}%
  }%
}

\define@choicekey{test.sty}{animal}{dog,duck,cow}{%
  \setanimal{#1}%
}

\newcommand*{\test@adj}{silly}

\newcommand*{\setadj}[1]{\renewcommand*{\test@adj}{#1}}

\define@key{test.sty}{adj}{\setadj{#1}}

\ProcessOptionsX

\newcommand*{\setuptest}[1]{\setkeys{test.sty}{#1}}

\newcommand*{\test}{{\iftestbold \bfseries \fi The \test@adj\ 
\test@animal\ said ``\csuse{test@\test@animal @sound}!''}}

\endinput

Now if I'm unable to set the options when the package is loaded (for example, the package is automatically loaded by another package) I can specify my options via these new commands. For example:

\documentclass{article}

\usepackage{test}

\setuptest{bold,adj={giant killer},animal=duck}

\begin{document}
\test
\end{document}

Unfortunately there are occasions where the option must be set when the package is loaded, which brings us back to glossaries. Most of the package options have an equivalent command or a set of commands that achieve the same effect. For example, the shortcuts option can be achieved via the command \DefineAcronymSynonyms. The acronym option is a little more complicated:

\documentclass{article}

\usepackage{glossaries}

\newglossary[alg]{acronym}{acr}{acn}{\acronymname}%
\renewcommand*{\acronymtype}{acronym}%
\DeclareAcronymList{acronym}

\newglossaryentry{sample}{name=sample,description={an example}}
\newacronym{abc}{ABC}{Sample}

\makeglossaries

\begin{document}
\gls{sample}
\gls{abc}

\printglossaries
\end{document}

There are only really two options that absolutely have to be set when the package is loaded: makeindex and xindy. With version 4.01, you can also use makeindex or xindygloss (equivalent to the xindy option without a value) as class options.

Edit:

Note that package options defined using \DeclareOptionX also can't be passed via the document class. For example, suppose my test package is now:

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{test}

\RequirePackage{xkeyval}
\RequirePackage{etoolbox}

\newcommand*{\test@shape}{}

\DeclareOptionX{em}{\renewcommand*{\test@shape}{\em}}

\define@boolkey{test.sty}[test]{bold}[true]{}

\newcommand*{\test@animal}{parrot}
\newcommand*{\test@parrot@sound}{squawk}
\newcommand*{\test@dog@sound}{woof}
\newcommand*{\test@duck@sound}{quack}
\newcommand*{\test@cow@sound}{moo}

\newcommand*{\setanimal}[1]{%
  \ifcsdef{test@#1@sound}%
  {%
     \renewcommand*{\test@animal}{#1}%
  }%
  {%
     \PackageError{test}{Unknown animal `#1'}{}%
  }%
}

\define@choicekey{test.sty}{animal}{dog,duck,cow}{%
  \setanimal{#1}%
}

\newcommand*{\test@adj}{silly}

\newcommand*{\setadj}[1]{\renewcommand*{\test@adj}{#1}}

\define@key{test.sty}{adj}{\setadj{#1}}

\ProcessOptionsX

\newcommand*{\setuptest}[1]{\setkeys{test.sty}{#1}}

\newcommand*{\test}{{\test@shape \iftestbold \bfseries \fi The \test@adj\ 
\test@animal\ said ``\csuse{test@\test@animal @sound}!''}}

\endinput

And the test document is:

\documentclass[em]{article}

\usepackage{test}

\begin{document}

\test

\end{document}

The em option is ignored and a warning is issued:

LaTeX Warning: Unused global option(s):
    [em].

The only way that I can think of that will allow this option to be passed via either \documentclass or \usepackage is to define it with both \DeclareOptionX and \DeclareOption and then process the class options before \ProcessOptionsX like this:

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{test}

\RequirePackage{xkeyval}
\RequirePackage{etoolbox}

\newcommand*{\test@shape}{}

\DeclareOptionX{em}{\renewcommand*{\test@shape}{\em}}
\DeclareOption{em}{\renewcommand*{\test@shape}{\em}}

\define@boolkey{test.sty}[test]{bold}[true]{}

\newcommand*{\test@animal}{parrot}
\newcommand*{\test@parrot@sound}{squawk}
\newcommand*{\test@dog@sound}{woof}
\newcommand*{\test@duck@sound}{quack}
\newcommand*{\test@cow@sound}{moo}

\newcommand*{\setanimal}[1]{%
  \ifcsdef{test@#1@sound}%
  {%
     \renewcommand*{\test@animal}{#1}%
  }%
  {%
     \PackageError{test}{Unknown animal `#1'}{}%
  }%
}

\define@choicekey{test.sty}{animal}{dog,duck,cow}{%
  \setanimal{#1}%
}

\newcommand*{\test@adj}{silly}

\newcommand*{\setadj}[1]{\renewcommand*{\test@adj}{#1}}

\define@key{test.sty}{adj}{\setadj{#1}}

\@for\CurrentOption :=\@declaredoptions\do{%
  \ifx\CurrentOption\@empty
  \else
    \@expandtwoargs
      \in@ {,\CurrentOption ,}{,\@classoptionslist,\@curroptions,}%
    \ifin@ \@use@ption
      \expandafter \let\csname ds@\CurrentOption\endcsname\@empty
    \fi
  \fi
}

\ProcessOptionsX

\newcommand*{\setuptest}[1]{\setkeys{test.sty}{#1}}

\newcommand*{\test}{{\test@shape \iftestbold \bfseries \fi The \test@adj\ 
\test@animal\ said ``\csuse{test@\test@animal @sound}!''}}

\endinput

Now the following two documents produce the same output:

Example 1:

\documentclass[em]{article}

\usepackage[bold,adj={giant killer},animal=duck]{test}

\begin{document}

\test

\end{document}

Example 2:

\documentclass{article}

\usepackage[em,bold,adj={giant killer},animal=duck]{test}

\begin{document}

\test

\end{document}

In both cases the result is:

Image of resulting document

  • IMHO, an advantage to pass babel language options in \documentclass is that they are taken in account by other packages e.g. varioref. Would there exist a convenient way for babel to list the loaded languages that could achieve this result? – Denis Bitouzé Nov 13 '13 at 11:47
  • @DenisBitouzé I wish babel did provide a list of loaded languages. Last time I checked it didn't, but it would certainly help my packages a lot if it did. – Nicola Talbot Nov 13 '13 at 11:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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