8

I am making a custom package for all my personal commands, and I am trying to pass some keyword arguments to the package as described in section 4.4 of clsguide. My use case is that I take math and physics courses and I often need to change notations because of that (For example, in math it's common to denote the complex conjugate with a bar while in physics the more common notation is asterik). I made this little template which almost works as I wanted:

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

\RequirePackage{xstring}

\DeclareKeys[demo]
{
    notation.store = \@demo@notation,
    notation.usage = load
}

\SetKeys[demo]{notation=physics} % Default to physics notation

\ProcessKeyOptions[demo]

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

\RequirePackage{xparse}

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

% main.tex
\documentclass{article}

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

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

When giving it a valid value like physics or math, it works as I expected. However, If I give it an invalid value such as engineer, I get an error, but it's not the error I specified:

Runaway argument?
 \PackageError {demo-pkg}{Invalid notation value: \@demo@notation } {\ETC.
! File ended while scanning use of \xs_testcase.
<inserted text> 
\par 
l.4 
  
I suspect you have forgotten a `}', causing me
to read past where you wanted me to stop.
I'll try to recover; but if the error is serious,
you'd better type `E' or `X' now and fix your file.

From this error, I can't see what the invalid value was or see my error message or help. Why does this happen, and how can I throw an error correctly when getting an invalid argument?

0

2 Answers 2

8

You have a space between the comparison cases in \IfStrEqCase which xstring does not like.

\begin{filecontents*}[overwrite]{demo-pkg.sty}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{demo-pkg}[Demo]

\RequirePackage{xstring}

\DeclareKeys[demo]
{
    notation.store = \@demo@notation,
    notation.usage = load
}

\SetKeys[demo]{notation=physics} % Default to physics notation

\ProcessKeyOptions[demo]

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

\RequirePackage{xparse}

\NewDocumentCommand{\Conjugate}{ m }{%
    \IfStrEqCase{\@demo@notation}{%
        {physics}{{#1}^{\ast}}%
        {math}{\overline{#1}}%
    }%
}
\end{filecontents*}
\documentclass{article}

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

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

You can simplify your code a bit by using a choice key (it will also be more efficient as the comparison will not happen for each use of \Conjugate):

\begin{filecontents*}[overwrite]{demo-pkg.sty}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{demo-pkg}[Demo]

\DeclareKeys[demo]
{
    notation.choice:,
    notation/physics.code = \DeclareDocumentCommand\Conjugate{ m }{{##1}^{\ast}},
    notation/math.code = \DeclareCommandCopy\Conjugate\overline,
    notation/unknown.code = 
        \PackageError{demo-pkg}{Invalid notation value: #1}
                     {Choose value of: physics, math},
    notation.initial:n = physics,
    notation.usage = load
}

\ProcessKeyOptions[demo]

\end{filecontents*}
\documentclass{article}

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

\begin{document}
\[ z = x + iy \Longleftrightarrow \Conjugate{z} = x - iy \]
\end{document}
2
  • 1
    I'd expect more than just \Conjugate to be dependent on the value of that choice (so either the .code of each choice will get much longer -- which could be fine -- or one could just set the internal in their codes -- the route I took with my answer). Nevertheless, +1!
    – Skillmon
    Jan 6 at 15:54
  • @Skillmon that's a good point. I guess that's really depends on the OP intent. Since you already covered the other option in your answer (and your comment hint about the defference as well), I'll leave things as is.
    – Udi Fogiel
    Jan 6 at 15:59
8

You can use more key properties than documented in clsguide, the other's are documented in interface3.pdf section 27.1, one of which is .choices:nn, for which a usage example is given in section 27.3.

The error message will not be exactly as the one you specified in your code, but I hope will be good enough.

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

\SetKeys[demo]{notation=physics} % Default to physics notation
\ProcessKeyOptions[demo]

% \RequirePackage{xparse} % not needed with recent kernel versions

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

Similar to @UdiFogiel this here proposes a slightly different usage of the value, and also converts your package into using the L3 programming language instead of xstring.

% demo-pkg.sty
\NeedsTeXFormat{LaTeX2e}
\ProvidesExplPackage{demo-pkg} {2024-01-06} {0.0} {Demo}

% assuming that you always need only two different notations, the following
% changes it to a boolean variable
\bool_new:N \l__demopkg_notation_physics_bool
\DeclareKeys[demo]
  {
    notation .choice:
    ,notation / physics .code:n =
      \bool_set_true:N  \l__demopkg_notation_physics_bool
    ,notation / maths   .code:n =
      \bool_set_false:N \l__demopkg_notation_physics_bool
    ,notation .usage:n   = load
    ,notation .initial:n = physics
  }

\ProcessKeyOptions[demo]

% since `load` doesn't allow changes after the package got loaded, we can just
% hard code the implementations here, instead of deciding on each call
\bool_if:NTF \l__demopkg_notation_physics_bool
  { \NewDocumentCommand \Conjugate { m } { #1 \sp { \ast } } }
  { \NewDocumentCommand \Conjugate { m } { \overline {#1} } }

\begin{advertisement}

You could also use the expkv-family of packages for your option parsing (of which I'm the author). Some things might be easier in expkv, others are harder (or rather not built in). Else it's more about which syntax you prefer.

% demo-pkg.sty
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{demo-pkg}[2024-01-06 v0.0 Demo]
\RequirePackage{expkv-def,expkv-opt}

\ekvdefinekeys{demo}
  {
     choice notation =
      {
        % expkv allows much freedom regarding the protected prefix because it
        % can work by expansion only (if it's not processing package
        % options...), therefore we need to set the protected prefix ourselves
        % here if we want to play nice.
         protected physics = \let\@demo@physicsTF\@firstoftwo
        ,protected maths   = \let\@demo@physicsTF\@secondoftwo
      }
    ,initial notation = physics
  }
\ekvoProcessGlobalOptions{demo} % options provided to \documentclass
\ekvoProcessLocalOptions{demo}  % options provided while loading this package
% future options will throw an option conflict error, since we only have a
% single load-time only option this is to be preferred over manually handling
% this. Else we could use `\ekvoProcessFutureOptions` (or combine all three
% calls by using `\ekvoProcessOptions` instead), but we'd need to redefine all
% the keys we don't want to be usable on a second time usage, because there is
% nothing like the `.usage`-property in `expkv`. This could look like the
% following:
%   \newcommand\@demo@aux[1]
%     {%
%       \protected\long\ekvdef{demo-pkg}{#1}
%         {%
%           \PackageError{demo-pkg}
%             {Option `#1' only usable on first package load}{}%
%         }%
%     }
%   \ekvcsvloop\@demo@aux{notation}% if you 
%   \let\@demo@aux\@demo@undefined

% since `load` doesn't allow changes after the package got loaded, we can just
% hard code the implementations here, instead of deciding on each call
\@demo@physicsTF
  {\NewDocumentCommand \Conjugate { m } {#1^{\ast}}}
  {\NewDocumentCommand \Conjugate { m } {\overline{#1}}}

\end{advertisement}

You must log in to answer this question.

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