0

This is a followup to my previous question Why does pgfkeys .initial not work in these cases?. In that question I created a macro with a key-value interface using pgfkeys and it works perfectly. Now, I'm implementing the same options with a package such that when the package is loaded with the appropriate option, the functionality of the macro in the previous question is replicated.

With a macro, if I invoke that macro without providing any options, I can invoke the key's .initial handler, fetch the key's value with \pgfkeysvalueof{...} and do what needs to be done with no problems. In other words, the macro "knows" what to do if no options are provided. My problem is that I can't seem to replicate that functionality with the package example below.

If I load the package without any keys using \usepackage{selectunits} I expect the package to behave as though [sunits=alternate] had been given at load time, which I cannot get to work. Every other possible key-value combination seems to work perfectly. I can almost get everything to work perfectly by adding sunits=alternate, inside the \pgfkeys{...} blocks (presently commented out in the MWE), but this causes that key and value to always be used regardless of any other keys and values provided when the package is loaded.

So ultimately the question is: Is there a way to make \usepackage{selectunits}, with no keys used, work as though sunits=alternate had been given at load time, preferably without more than one key-value combination being invoked? It should also work with either of the two \pgfkeys{...} blocks.

Here is the MWE:

% !TEX TS-program = lualatexmk
% !TEX encoding = UTF-8 Unicode

\documentclass{article}

\begin{filecontents}[overwrite,noheader]{selectunits.sty}
% !TEX encoding = UTF-8 Unicode

\ProvidesPackage{selectunits}[2021-01-22 v1.0 Example package]
\NeedsTeXFormat{LaTeX2e}

\RequirePackage{pgfopts}

\newcommand*{\perpusebaseunits}{\typeout{You'll get base units.}}
\newcommand*{\perpusederivedunits}{\typeout{You'll get derived units.}}
\newcommand*{\perpusealternateunits}{\typeout{You'll get alternate units.}}

% Either one of the two following blocks should work.
%  This block does not use .is choice
\pgfkeys{%
  /selectunits/.is family, /selectunits,%
  sunits/.initial=alternate,%
  sunits/.default=derived,%
  sunits/.code={%
    \ifcsname perpuse#1units\endcsname
      \csname perpuse#1units\endcsname
    \else
      \GenericError{}
        {\MessageBreak settheunits: Illegal key value}
        {Key 'sunits' can only base, derived, or alternate.}
        {Read the documentation for help.}
    \fi
  },%
  %sunits=alternate, % almost works, key used even in the presence of other keys
}%
%  This block uses .is choice
%\pgfkeys{%
%  /selectunits/.is family, /selectunits,%
%  sunits/.is choice,%
%  sunits/.initial=alternate,%
%  sunits/.default=derived,%
%  sunits/base/.code={\perpusebaseunits},%
%  sunits/derived/.code={\perpusederivedunits},%
%  sunits/alternate/.code={\perpusealternateunits},%
%  %sunits=alternate, % almost works, key used even in the presence of other keys
%}%

\ProcessPgfPackageOptions{/selectunits}

\NewDocumentCommand{\selectunitssetup}{ o }{%
  \IfValueTF {#1}
    { \pgfqkeys{/selectunits}{#1} }
    { \csname perpuse\pgfkeysvalueof{/selectunits/sunits}units\endcsname }
}%

\end{filecontents}
\usepackage{selectunits}                   % should set alternate units, doesn't work
%\usepackage[sunits]{selectunits}           % should set derived units, works
%\usepackage[sunits=base]{selectunits}      % should set base units, works
%\usepackage[sunits=derived]{selectunits}   % should set derived units, works
%\usepackage[sunits=alternate]{selectunits} % should set alternate units, works
%\usepackage[sunits]{selectunits}           % should set derived units, works
%\usepackage{selectunits}                   % should set alternate units, doesn't works
%\usepackage[sunits=blubb]{selectunits}     % should throw an error and stop, works

\begin{document}

Hello.
\selectunitssetup\par                   % should set alternate units, works
\selectunitssetup[sunits]\par           % should set derived units, works
\selectunitssetup[sunits=base]\par      % should set base units, works
\selectunitssetup[sunits=derived]\par   % should set derived units, works
\selectunitssetup[sunits=alternate]\par % should set alternate units, works
\selectunitssetup[sunits]\par           % should set derived units, works
\selectunitssetup\par                   % should set alternate units, works
%\selectunitssetup[sunits=blubb]\par    % should throw an error and stop, works

\end{document}
2

If it were my package, I would make \selectunitssetup a command that:

  • takes a mandatory argument;

  • does not modify the setup in case this argument is empty.

However, you requested otherwise, and the code below implements what you asked for. Please also note that using:

  1. /some key/.initial=... followed by

  2. /some key/.code={<some code>}

doesn't make much sense in general. Indeed, one of the main purposes of (1) is to allow one to store a value in /some key using \pgfkeys{/some key=<value>}, whereas (2) will make it so that \pgfkeys{/some key=<value>} executes <some code> with #1 replaced by <value>.

\begin{filecontents}[noheader]{selectunits.sty}
\ProvidesPackage{selectunits}[2021-01-22 v1.0 Example package]
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{expl3} % for \NewDocumentCommand when LaTeX format < 2020-10-01
\RequirePackage{pgfopts}

\newcommand*{\perpusebaseunits}{\typeout{You'll get base units.}}
\newcommand*{\perpusederivedunits}{\typeout{You'll get derived units.}}
\newcommand*{\perpusealternateunits}{\typeout{You'll get alternate units.}}

\pgfkeys{
  /selectunits/options/.cd,
  %
  initial@setup/.style={
    /selectunits/options/buffered@sunits/.initial=alternate,
  },
  initial@setup,
  %
  sunits/.is choice,
  sunits/.default=derived,
  sunits/alternate/.style={/selectunits/options/buffered@sunits=alternate},
  sunits/base/.style={/selectunits/options/buffered@sunits=base},
  sunits/derived/.style={/selectunits/options/buffered@sunits=derived},
}

\ProcessPgfPackageOptions{/selectunits/options}

\NewDocumentCommand{\selectunitssetup}{ o }{%
  \pgfkeys{/selectunits/options/initial@setup}%
  \IfValueT{#1}{\pgfqkeys{/selectunits/options}{#1}}%
  %
  \selectunits@do@setup
}

\NewDocumentCommand{\selectunits@do@setup}{}{%
  \csname
    perpuse\pgfkeysvalueof{/selectunits/options/buffered@sunits}units%
  \endcsname
}

\selectunits@do@setup
\endinput
\end{filecontents}

\documentclass{article}
\usepackage{selectunits}

%\usepackage{selectunits}                   % sets alternate units
%\usepackage[sunits]{selectunits}           % sets derived units
%\usepackage[sunits=base]{selectunits}      % sets base units
%\usepackage[sunits=derived]{selectunits}   % sets derived units
%\usepackage[sunits=alternate]{selectunits} % sets alternate units
%\usepackage[sunits=blubb]{selectunits}     % throws an error and stops

\begin{document}

\selectunitssetup                   % sets alternate units
\selectunitssetup[sunits]           % sets derived units
\selectunitssetup[sunits=base]      % sets base units
\selectunitssetup[sunits=derived]   % sets derived units
\selectunitssetup[sunits=alternate] % sets alternate units
\selectunitssetup[sunits]           % sets derived units
\selectunitssetup                   % sets alternate units
%\selectunitssetup[sunits=blubb]    % throws an error and stops

\end{document}
4
  • I appreciate the logic of your solution. – LaTeXereXeTaL Jan 24 at 19:35
  • I ended up changing the setup command as you described. – LaTeXereXeTaL Jan 27 at 14:07
  • Good, I think this will avoid confusing your users. – frougon Jan 27 at 14:23
  • My target users are beginners. They find everything about LaTeX confusing. – LaTeXereXeTaL Jan 27 at 15:20

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.