2

I'm trying to make a macro using the xparse library together with xkeyval for optional key value pairs. Here's a minimal example of what I'm trying to do

cmd.sty

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{cmd}

\RequirePackage{xparse}
\RequirePackage{xkeyval}

\define@key{keyvalTest}{width}{\def\kt@width{#1}}
\define@key{keyvalTest}{caption}{\def\kt@caption{#1}}

\NewDocumentCommand{\keyvalTest}{ o m }{
  \begingroup
    % Set the default values, and override any extra if any optional arguments are present, else just set the default values
    \IfValueTF{#1}
      {\setkeys{keyvalTest}{width={50mm}, caption=\NoValue, #1}}
      {\setkeys{keyvalTest}{width={50mm}, caption=\NoValue}}
      
    \noindent
    Path: #2 \\
    Width: \kt@width \\
    % Should print the caption if specified as an optional arg, else print "No Caption"
    \IfValueTF{\kt@caption}
      {Caption: \kt@caption}
      {No Caption}
  \endgroup
}

main.tex

\documentclass{article}

\usepackage{cmd}

\begin{document}
  \keyvalTest{diagrams/pathToDiagram.pdf} \\
  
  \keyvalTest[caption={hello}, width=3cm]{diagrams/pathToDiagram.pdf}
\end{document}

The example gives me this:

And an error saying that \NoValue{} is not defined. I've only seen it used several places (for example here), but I've never seen it used in actual code. I suppose it might be deprecated, or is only valid for expl3?

I've been through the documentation for xparse without finding any macro that would act as NoValue. I've also tried reading through its source code, but I would like to avoid expl3 if possible.

I'm wondering where I should go from here. Should I rather be using blank, and using some kind of isblank or isempty function, or is there a better approach to this problem? I would think doing if else logic on missing named optional parameters would be quite common, but I can't seem to find any examples anywhere.

2 Answers 2

2

\IfValueTF is checking whether you have a special character sequence, -NoValue- with unlikely category codes set in the argument. Far better is to simply check to see if \kt@caption has been set. You can use the internal LaTeX command \@ifundefined to manage this by rewritig your definition of \keyvalTest as:

\NewDocumentCommand{\keyvalTest}{o m}{
  \IfValueTF{#1}
  {\setkeys{keyvaltest}{width=50mm,#1}}
  {\setkeys{keyvaltest}{width=50mm}}

  \noindent
    Path: #2 \\
    Width: \kt@width \\
    % Should print the caption if specified as an optional arg, else print "No Caption"
    \@ifundefined{kt@caption}
      {No Caption}
      {Caption: \kt@caption}
}

Alternatively, you could use the e-TeX command \ifdefined which uses TeX's low-level if mechanism and avoid issues with pre-2018 LaTeXs. Replace the \@ifundefined sequence with:

\ifdefined\kt@caption
    Caption: \kt@caption
\else
    No Caption
\fi
3

I wouldn't mix xkeyval with expl3, which has its own key-value interface that's in many ways superior to xkeyval.

File cmd.sty

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{cmd}

%\RequirePackage{xparse} % not needed for LaTeX 2020-10-01 or later

\ExplSyntaxOn

\keys_define:nn { hsevenxfour/cmd }
 {
  width .dim_set:N = \l__hsevenxfour_cmd_width_dim,
  width .initial:n = 50mm,
  caption .tl_set:N = \l__hsevenxfour_cmd_caption_tl,
 }

\NewDocumentCommand{\keyvalTest}{ O{} m }
 {
  \group_begin:
  \keys_set:nn { hsevenxfour/cmd } { #1 }
  \noindent
  Path:~#2 \\
  Width:~\dim_eval:n { \l__hsevenxfour_cmd_width_dim } \\
  % Should print the caption if specified as an optional arg, else print "No Caption"
  \tl_if_empty:NTF \l__hsevenxfour_cmd_caption_tl
   {
    No~caption
   }
   {
    Caption:~\tl_use:N \l__hsevenxfour_cmd_caption_tl
   }
  \group_end:
}

\ExplSyntaxOff

File test.tex

\documentclass{article}

\usepackage{cmd}

\begin{document}

\noindent
\keyvalTest{diagrams/pathToDiagram.pdf}

\noindent
\keyvalTest[caption={hello}, width=3cm]{diagrams/pathToDiagram.pdf}

\end{document}

Output

enter image description here

You must log in to answer this question.

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