5

Consider the following MWE:

\documentclass{article}

\DeclareKeys [ myKeys ]{
  a       .store = \whereA,
  b       .store = \whereB,
}

\SetKeys [ myKeys ] {
  a = AAA,
  b = \pageref{#2},
}

\NewDocumentCommand{\abref}{O{} m}{%
  \SetKeys [ myKeys ] {#1}%
  \whereA\ and \whereB%
}

\begin{document}

\section{Some topic}
  Ref the other page \abref{newpage} % does NOT work

  \vspace{2em}

  Ref the other page \abref[b = \pageref{newpage}]{newpage} % works

  \newpage

  foobar \label{newpage}

\end{document}

It produces the following output:

The problem lies in the fact the default value for parameter b, viz. \pageref{#2}, gets evaluated too early.(*) I have a workaround for this: use a placeholder instead (<pnum> shall we say), and replace it inside the command definition (requires xstring):

\IfSubStr{\whereB}{<pnum>}%
  {\StrSubstitute{\whereB}{<pnum>}{\pageref{#2}}}%

But my question is if there is some way to prevent that early evaluation, thus making the workaround redundant. I have tried, e.g., \noexpand and some such incantations, but with no luck.

Thank you in advance for your help.

(*) In fact it produces the following warning:

LaTeX Warning: Reference `##2' on page 1 undefined on input line 24.

2 Answers 2

5

The problem is that you tried to use #2 (the command argument) outside the command itself—in the global default settings. LaTeX cannot know what #2 refers to until the command is actually called.

Therefore, the solution is not to set b = \pageref{#2} in the preamble, but rather to assign the value of \whereB inside \abref, where #2 is available and represents the label name passed when the command is invoked.

If I understand you correctly.

\documentclass{article}

\DeclareKeys[myKeys]{
  a .store = \whereA,
  b .store = \whereB,
  a .initial:n = AAA,
}

\NewDocumentCommand{\abref}{O{} m}{%
  % Set default values
  \def\whereB{\pageref{#2}}%
  % Apply user-provided settings (if any)
  \SetKeys[myKeys]{#1}%
  \whereA\ and \whereB
}

\begin{document}

\section{Some topic}
Ref the other page \abref{newpage} % now works

\vspace{2em}

Ref the other page \abref[b = \pageref{newpage}]{newpage} % still works

\newpage
foobar \label{newpage}

\end{document}

2
  • Awesome! Thanks! Just a quick question: from what I can glean, this line is redundant, right? \def\whereA{AAA}% Because the value is already set on the .initial:n line, I should think. Commented 2 days ago
  • Yes, thank you—you're absolutely right; the line \def\whereA{AAA} is completely unnecessary. It ended up there by mistake. Commented 2 days ago
3

.store simply stores the value and, as kabenyuk explained, #2 is just #2 in the context you use it.

You could use a .code key here rather than .store and just pass the label name to the key, so the key's definition would get the \pageref.

For example,

\DeclareKeys [ myKeys ]{
  a       .store = \whereA,
  b       .code = {\def\whereB{\pageref{#1}}},
}

Now b will define \whereB as \pageref{#1}, where #1 will get replaced by the value passed to b.

\SetKeys [ myKeys ] {
  a = AAA,
}

Unless you have a standard label you want to reference, it probably does not make sense to set an initial value for b. Instead, you can pass the second argument of your \abref command to b and then process any keys in the optional argument. This way #2 will be used as a default, but a b=<something else> in the optional argument will override it.

\NewDocumentCommand{\abref}{O{} m}{%
  \SetKeys [ myKeys ] {b={#2},#1}%
  \whereA\ and \whereB
}

Then

\abref{newpage}

will typeset AAA and \pageref{newpage}, while

\abref[b=otherpage}{newpage}

will typeset AAA and \pageref{otherpage}. So your example would become

Ref the other page \abref{newpage} %  works

\vspace{2em}

Ref the other page \abref[b = newpage]{newpage} % works

But this only really makes sense if your command has some other use for the required argument. If not, you would not really need b at all and could just write

\DeclareKeys [ myKeys ]{
  a       .store = \whereA,
}

\SetKeys [ myKeys ] {
  a = AAA,
}

\NewDocumentCommand{\abref}{O{} m}{%
  \SetKeys [ myKeys ] {#1}%
  \whereA\ and \pageref{#2}%
}

which would be a more efficient way to achieve the same result.

\documentclass{article}

\DeclareKeys [ myKeys ]{
  a       .store = \whereA,
  b       .code = {\def\whereB{\pageref{#1}}},
}

\SetKeys [ myKeys ] {
  a = AAA,
}

\NewDocumentCommand{\abref}{O{} m}{%
  \SetKeys [ myKeys ] {b={#2},#1}%
  \whereA\ and \whereB
}

\begin{document}

\section{Some topic}
Ref the other page \abref{newpage} % does NOT work

\vspace{2em}

Ref the other page \abref[b = newpage]{newpage} % works

\newpage

foobar \label{newpage}

\end{document}

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.