10

This is a modified version of "Unused global options(s)" when using arguments in options of class. The question is how to create a parasitic class (i.e. one which loads another) which accepts key-value options specific to that class, but does not generate warnings about unknown options.

Consider,

\begin{filecontents}[overwrite]{\jobname.cls}
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{\jobname}
\DeclareOption{bertha}{}
\DeclareKeys[testwork]{%
  test .code ={\newcommand{\foo}{#1}},
  test .usage = load,
}
\ProcessKeyOptions[testwork]
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}

\ProcessOptions\relax
\LoadClass{article}

\endinput
\end{filecontents}
\documentclass[test=wtf,bertha]{\jobname}

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

Compiling this example produces the typeset result expected, but the messages printed to the terminal and recorded in the log are not quite as expected.

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

In the original question, the key-value option is created using xkeyval. I could not figure out how to avoid the warning in that case, but I hoped that using the built-in framework for declaring key-value options would be handled better.

According to clsguide.pdf only options not declared by the parasitic class should be passed to article. This is true, for example, of bertha. But it is not, apparently, true of test. Moreover, if test=wtf was passed to article, I'd expect it to be passed together with its value i.e. I'd expect a warning about test=wtf as opposed to test.

Obviously, I'm not setting this up correctly, but I'm not sure what I'm doing wrong. How is this supposed to be done exactly?

11
  • Thats indeed a very interesting question. The warning is not a big thing if you're the only one who uses the class. But if you're writing a .cls file which might be used in your company, is distributed via GitHub/Lab etc. or will be submitted to CTAN, those warnings could confuse other users.
    – lukeflo
    Oct 14, 2023 at 9:52
  • 1
    You should use a more reliable package that actually documents that you can use \CurrentOption like expkv-opt :P
    – Skillmon
    Oct 14, 2023 at 16:12
  • @Skillmon Or I should use a more reliable source of information, but those comments have been disappeared ;).
    – cfr
    Oct 14, 2023 at 19:58
  • @cfr btw. expkv separates between keys taking a value and keys not taking a value, so bertha could be an option that really doesn't want a value (and would throw an error accordingly if given one -- you could use the .value_forbidden:n property to get the same behaviour in l3keys).
    – Skillmon
    Oct 14, 2023 at 20:01
  • 1
    @Skillmon I know you're part of the team ;). I trust your code. I'm just trying to avoid tangling with yet another key-value framework. I've already got xkeyval, pgfkeys, the l3 stuff and maybe some older interfaces, while packages I use load kvoptions etc., too. I can't avoid multiple methods entirely, but I'd like to keep the setup as simple as practicable. (And I'm sure if I tried your package, I'd want to use it, so I'd best not ;).)
    – cfr
    Oct 14, 2023 at 20:16

3 Answers 3

10

You are mixing two option systems and that confuses the setting of the unused lists handling as for every system a different option is "unknown" and so unused.

Stick to one system (that one can use \CurrentOption in \DeclareUnknownKeyHandler is not documentated, we should change that ;-)).

\begin{filecontents}[overwrite]{testoption.cls}
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{testoption}
\DeclareKeys[testwork]{%
  bertha .code={},
  test .code ={\newcommand{\foo}{#1}},
  test .usage = load  
}
\DeclareUnknownKeyHandler [testwork]
 {\PassOptionsToClass{\CurrentOption}{article}}

\ProcessKeyOptions[testwork]
\LoadClass{article}
\endinput
\end{filecontents}
\documentclass[test=wtf,bertha,12pt]{testoption}

\begin{document}
test \foo
  
\end{document}
3
  • @cabohah probably. large sets of generic keyval options in class options are imho problematic anyway. I would avoid to stress the system here too much. Oct 14, 2023 at 10:33
  • @cabohah But wouldn't you want it to result in errors in that case?
    – cfr
    Oct 14, 2023 at 12:00
  • Or is the problem that = gets added? In my initial example, I didn't actually have bertha, but only test=wtf (following the linked question). So does any use of the key-value interface here require treating all options as key-value to avoid warnings? I.e. even if the base class doesn't itself use key-value syntax?
    – cfr
    Oct 14, 2023 at 12:05
6

The problem seems to be using \ProcessKeyOptions and \ProcessOptions. Instead, I would use:

\begin{filecontents}[overwrite]{\jobname.cls}
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{\jobname}
\DeclareKeys[testwork]{%
  test .code ={\newcommand{\foo}{#1}},
  test .usage = load,
  bertha .code = {},
  bertha .value_forbidden:n = true,
}
\DeclareUnknownKeyHandler[testwork]{%
  \PassOptionsToClass{\CurrentOption}{article}%
}
\ProcessKeyOptions[testwork]\relax

\LoadClass{article}

\endinput
\end{filecontents}
\documentclass[test=wtf,bertha,alpha,gamma=true,12pt]{\jobname}

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

The

(/usr/local/texlive/2023/texmf-dist/tex/latex/base/size12.clo)

in the log-file shows, that option 12pt is correctly passed to and recognized by article.cls.

The message:

 LaTeX Warning: Unused global option(s):
     [alpha,gamma].

shows, that the two unknown options are correctly result in warning messages.

Note: For key-value options, LaTeX always reports only the key as unknown. This is a feature.


Addendum: Using this suggestion does also work, if the loaded class itself processes key-value options:

\begin{filecontents}[overwrite]{test.cls}
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{\jobname}
\DeclareKeys[testwork]{%
  test .code ={\newcommand{\foo}{#1}},
  test .usage = load,
  bertha .code = {},
  bertha .value_forbidden:n = true,
}
\DeclareUnknownKeyHandler[testwork]{%
  \PassOptionsToClass{\CurrentOption}{test-base}%
}
\ProcessKeyOptions[testwork]\relax

\LoadClass{\jobname-base}

\endinput
\end{filecontents}
\begin{filecontents}[overwrite]{test-base.cls}
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{\jobname-base}
\DeclareKeys{%
  gamma .code= \typeout{Gamma=`#1'},
  gamma .value_required:n =true
}
\DeclareUnknownKeyHandler{%
  \PassOptionsToClass{\CurrentOption}{article}
}
\ProcessKeyOptions
\LoadClass{article}
\end{filecontents}
\documentclass[test=wtf,bertha,alpha,gamma=test,gamma=,12pt]{test}

\begin{document}
test
  
\end{document}

Here you can also see, that gamma= is passed as gamma= from test to test-base. (If is would be passed as gamma, an error would be reported, because gamma requires a value.)

8
  • ah, right one could use \CurrentOption for the whole option in the key handler too ;-) Oct 14, 2023 at 10:38
  • So both key-value and non-key-value options would be passed to the base class in this case @UlrikeFischer ? Perhaps I didn't read the documentation carefully enough ....
    – cfr
    Oct 14, 2023 at 12:02
  • 1
    @cfr The \DeclareUnknownKeyHandler does handle all unknown keys, because it does not know, whether an unknown option would be used as a key-value option without passing a value or as a non key-value option. I've shown this in my answer with options alpha and 12pt. Both are passed to article. Maybe I should extend it by another option gamma=true and show the resulting log …
    – cabohah
    Oct 14, 2023 at 13:33
  • @cabohah I think it's fine. If this is the way to do it (or whatever the way to do it is), I think it should be in the documentation. Do you know where \declarekeyoption is documented? David mentions it in a comment above, but I can't find it. I don't think you should delete this answer ;).
    – cfr
    Oct 14, 2023 at 13:48
  • @cfr no hit in my entire TeX Live 2023 installation for \declarekeyoption in any file of the types tex, sty, cls, or ltx.
    – Skillmon
    Oct 14, 2023 at 20:04
4

EDIT: The first code block using \ProcessKeyOptions doesn't really work, as \ProcessKeyOptions will add all the unknown options to it to the unused-globals list, so if you use an option known to article but not your class you'd get a false positive. This is not the case for the expkv-opt using code block below, \ekvoProcessGlobalOptions only ever removes, never adds to the list of unused global options.

EDIT2: If one uses an unknown handler with the first code, one should get the desired behaviour, but currently (2023-11-20) there is a bug in the option handling (reported #1183), that'll be likely fixed at least by LaTeX 2024-06-01.


I'd suggest to invert the logic. Instead of first parsing your class's options and forwarding the unknown ones to article, you could as well load article with the entire list, and afterwards parse your options. As long as your class doesn't define options which are valid for article as well this would essentially have the same behaviour with less struggle for your code (see the documentation of \LoadClassWithOptions in clsguide.pdf). The unused options list would still be the same:

\begin{filecontents}[overwrite]{\jobname.cls}
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{\jobname}
\LoadClassWithOptions{article}

\DeclareKeys[testwork]{%
  test .code  = \newcommand{\foo}{#1},
  test .usage = load,
  % the following three lines give an equivalent definition of your bertha
  % option as defined with `\DeclareOption{bertha}{}`
  bertha .code = {},
  bertha .value_forbidden:n = true,
  bertha .usage = load,
  # ignore unknown options to not add them to the unused options list
  unknown .code = {}
}
\ProcessKeyOptions[testwork]

\endinput
\end{filecontents}
\documentclass[test=wtf,bertha]{\jobname}

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

And since I have to advertise, this is the equivalent code using expkv:

\begin{filecontents}[overwrite]{\jobname.cls}
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{\jobname}
\LoadClassWithOptions{article}
\RequirePackage{expkv-opt,expkv-def}
\ekvdefinekeys{testwork}
  {%
    % `store` isn't really equivalent to your newcommand-using code, that
    % would've been:
    %   code test = \newcommand\foo{#1}
    % Instead `store` is equivalent to ltkeys' `.store` handler.
     store test    = \foo
    ,noval bertha  = {}
  }
% if a standard class was already loaded by \LoadClassWithOptions in a parasitic
% class the unused options list is already filled, we don't need to parse local
% options any more, and only having the package's default handling of global
% options is fine.
\ekvoProcessGlobalOptions{testwork}

\endinput
\end{filecontents}
\documentclass[test=wtf,bertha]{\jobname}

\begin{document}
test \foo
  
\end{document}
9
  • Thanks. Why doesn't this give a warning when article processes the options? Will the parasitic class still get options if article offers the same options?
    – cfr
    Oct 14, 2023 at 23:39
  • 1
    @cfr both classes will completely digest all options given to your class. The standard class (article) will set the unused option list to contain all the options it doesn't know, and the option processing done by your class will remove the options it knows from the unused option list.
    – Skillmon
    Oct 15, 2023 at 7:55
  • 1
    @cfr I just realised, there is an issue with my suggested approach if you're using \ProcessKeyOptions: your class will add the keys not known to it to the unused globals list (even if that list already has contents). The expkv-opt variant doesn't have that issue though, as you're using \ekvoProcessGlobalOptions.
    – Skillmon
    Oct 15, 2023 at 19:20
  • It seems to me there isn't a great built-in solution here. All the answers feel like workarounds. Or not exactly workarounds, but there should be a standard, documented way of doing this which can be easily applied. The current dual-system setup in LaTeX seems non-optimal.
    – cfr
    Oct 15, 2023 at 19:25
  • 2
    @cfr it is non-optimal, agreed, but it is optimal without breaking all the existing documents, classes and packages using the old system. It seems as in LaTeX2e's original design no one thought that one might want to use key=value options. Things might even be considered worse: With the raw option-list available, edge cases become actually harder to input, as your option has to survive an \edef without throwing errors, but is processed without an \edef-expansion applied. Something that expkv-opt doesn't do better -- I have no idea how that should be done better.
    – Skillmon
    Oct 15, 2023 at 19:29

You must log in to answer this question.

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