0

I am trying to make a custom package to contain all my personal macros and a custom class for my personal style. I wanted to pass a keyword argument notation which can take a value of math or physics in order to change notations for different courses I take (For example, in math the complex conjugate is often denoted with a bar, and in physics it's denoted with an asterisk). After playing around and following the answers I got here. I got these 3 files:

% demo-pkg.sty
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{demo-pkg}[Demo]

\RequirePackage{xstring}

\DeclareKeys[demo]
{
    notation.choices:nn =
        { physics, math }
        {\ExpandArgs{Nc}\let\@demo@notation{l_keys_choice_tl}},
    notation.usage = load,
    notation.initial:n = physics
}

\ProcessKeyOptions[demo]

\IfStrEqCase{\@demo@notation}{
    {physics}{}%
    {math}{}%
}[
    \PackageError{demo-pkg}{Invalid notation value: \@demo@notation}
    {Choose value of: physics, math}
]

\RequirePackage{xparse}

\IfStrEqCase{\@demo@notation}{%
{physics}{%
\NewDocumentCommand{\Conjugate}{ m }{{##1}^{\ast}}%
}%
{math}{%
\NewDocumentCommand{\Conjugate}{ m }{\overline{##1}}%
}%
}%

% demo-cls.cls
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{demo-cls}[Demo class]

\DeclareKeys[demo-cls]
{
    notation.choices:nn =
        { physics, math }
        {\PassOptionsToPackage{notation=l_keys_choice_tl}{demo-pkg}}, % <- This seems to cause issues
    notation.usage = load,
    notation.initial:n = physics
}

\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}

\ProcessKeyOptions[demo-cls]
\ProcessOptions\relax

\LoadClass[a4paper, 12pt]{article}
\RequirePackage{demo-pkg}

% main.tex
\documentclass[notation=math]{demo-cls}

\begin{document}
\[ z = x + iy \Longleftrightarrow \Conjugate{z} = x - iy \]
\end{document}

Trying to compile this code gives me an error Key 'demo/notation' accepts only a fixed set of choices.. When I use the demo-pkg directly:

\documentclass{article}

\usepackage[notation=math]{demo-pkg}

\begin{document}
\[ z = x + iy \Longleftrightarrow \Conjugate{z} = x - iy \]
\end{document}

it compiles fine, and I can switch notations with the package parameter. Passing the parameter from the class to the package seems to cause issues. I tried using \l_keys_choice_tl, \tl_use:N \l_keys_choice_tl and surrounding this code block with \ExplSyntaxOn/\ExplSyntaxOff. Nothing works. I get the same error. How can I pass the notation parameter from the demo-cls class to the demo-pkg package?

2
  • 2
    I wouldn't mix \ProcessKeyOptions and \ProcessOptions in the same file, use the old mechanism or the new, processing all the options with both mechanisms is bound to lead to weird effects Jan 8 at 14:53
  • 1
    Your issue is that you pass the string l_keys_choice_tl as the value. That's the name of a parameter in the expl3 layer of l3keys. Instead of notation=l_keys_choice_tl use \ExpandArgs{e}\PassOptionsToPackage{notation=\UseName{l_keys_choice_tl}}{demo-pkg}. But I have to agree with @DavidCarlisle don't mix \ProcessKeyOptions and \ProcessOptions.
    – Skillmon
    Jan 8 at 14:58

1 Answer 1

3

You don't have to forward the notation=math from \documentclass to your package. The package uses \ProcessKeyOptions, so parses the global option list, which contains that option.

And you can use the \ProcessKeyOptions mechanism in your class to forward unknown options to article. So the only file needing changes is your class:

demo-cls.cls:

% demo-cls.cls
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{demo-cls}[Demo class]

\DeclareKeys[demo-cls]
  {
    unknown .code = \PassOptionsToClass{\CurrentOption}{article}
  }

\ProcessKeyOptions[demo-cls]

\LoadClass[a4paper, 12pt]{article}
\RequirePackage{demo-pkg}

demo-pkg.sty:

% demo-pkg.sty
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{demo-pkg}[Demo]

\RequirePackage{xstring}

\DeclareKeys[demo]
{
    notation.choices:nn =
        { physics, math }
        {\ExpandArgs{Nc}\let\@demo@notation{l_keys_choice_tl}},
    notation.usage = load,
    notation.initial:n = physics
}

\ProcessKeyOptions[demo]

\IfStrEqCase{\@demo@notation}{
    {physics}{}%
    {math}{}%
}[
    \PackageError{demo-pkg}{Invalid notation value: \@demo@notation}
    {Choose value of: physics, math}
]

\RequirePackage{xparse}

\IfStrEqCase{\@demo@notation}{%
{physics}{%
\NewDocumentCommand{\Conjugate}{ m }{{##1}^{\ast}}%
}%
{math}{%
\NewDocumentCommand{\Conjugate}{ m }{\overline{##1}}%
}%
}%

main.tex:

% main.tex
\documentclass[notation=math]{demo-cls}

\begin{document}
\[ z = x + iy \Longleftrightarrow \Conjugate{z} = x - iy \]
\end{document}

I guess a bit of explanation is in order (also in retrospect for my older answer):

\ExpandArgs is a macro that gives a high-level interface to fine control over argument expansion. I used that to set your package internal \@demo@notation to the value of an L3-variable without using the L3 language directly (so without \ExplSyntaxOn or \ProvidesExplPackage). This was done by using \ExpandArgs{Nc}\let\@demo@notation{l_keys_choice_tl}, which means: Before using \let use two arguments, the first is an unchanged single token (N), the second is a braced group that should form a macro by \csname. So \ExpandArgs{Nc}\let\@demo@notation{l_keys_choice_tl} is a better readable variant of this low-level construction:

\expandafter\let\expandafter\@demo@notation\csname l_keys_choice_tl\endcsname

I hope this explanation helps on explaining why your

\DeclareKeys[demo-cls]
{
    notation.choices:nn =
        { physics, math }
        {\PassOptionsToPackage{notation=l_keys_choice_tl}{demo-pkg}}, % <- This seems to cause issues
    notation.usage = load,
    notation.initial:n = physics
}

Didn't work. Because l_keys_choice_tl doesn't magically become the real choice value, that was done by \ExpandArgs, and would've to be done by similar means here (which my comment suggested).

5
  • It worked. I still don't understand why there is no need to forward the documentclass options to the package. How does my package know to pick them up and how do other packages know not to consume these options?
    – Shai Avr
    Jan 8 at 22:10
  • 1
    @ShaiAvr options provided to \documentclass are so-called global options. Unless you use a package/option-parser which explicitly omits them, every option-parser considers them. The mechanism used is usually "if package X knows option Y it should consider/parse it, and remove the consumed option from the list of unused global options if option Y is still on it, otherwise ignore option Y". expkv-opt for instance provides \ekvoProcessLocalOptions, which ignores global options, other packages provide similar means.
    – Skillmon
    Jan 8 at 22:36
  • Will my package automatically remove these options from the global list after parsing them, or should I do it myself to prevent other packages from accidentally parsing them?
    – Shai Avr
    Jan 9 at 10:27
  • 1
    @ShaiAvr neither, the global option list is exactly that, a global option list.
    – Skillmon
    Jan 9 at 10:29
  • I understand. Thank you
    – Shai Avr
    Jan 9 at 10:30

You must log in to answer this question.

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