LaTeX's protection mechanism only protects the macro, not its arguments. In LaTeX macro can be used/executed in different contexts:
Normal typesetting context (\set@typeset@protect
).
\protect
has the meaning of \relax
. It does not touch the following
macro which is executed normally.
Display context (\set@display@protect
).
Macros are displayed inside messages, errors, warnings, or \typeout
. \protect
becomes \string
that converts the following macro into simple character tokens that cannot be executed anymore.
Expansion context (\@unexpandable@protect
).
\protect
is \noexpand
here that prevents the following macro from the next expansion.
\string
and \noexpand
only affects the next token. They do not protect whole arguments with many tokens. Nowadays most TeX engines support e-TeX, which comes with \detokenize
and \unexpanded
. Both work on token lists and can be used to protect arguments.
To some degree the definition of \aaaa
can be extended to support the protection of its argument, e.g.:
\documentclass{article}
\makeatletter
\newcommand*{\aaaa}{%
\ifx\protect\@typeset@protect
\csname aaaa \expandafter\endcsname
\else
\ifx\protect\@unexpandable@protect
\protect@unexpand@cmd@arg\aaaa
\else
\ifx\protect\string
\protect@string@cmd@arg\aaaa
\else
\expandafter\protect@unknown@cmd@arg
\csname aaaa \endcsname
\fi
\fi
\fi
}
\expandafter\newcommand\csname aaaa \endcsname[1]{%
\begingroup
\def\bbbb{bbbb}%
#1%
\endgroup
}
% unexpanded protect
\def\protect@unexpand@cmd@arg#1\else#2\fi\fi\fi#3{%
\fi\fi
\ifx\thepage\relax
\detokenize
\else
\unexpanded
\fi
{#1{#3}}%
}
% display protect
\def\protect@string@cmd@arg#1\else#2\fi\fi\fi#3{%
\fi\fi\fi
\detokenize{#1{#3}}%
}
% unknown protect
\def\protect@unknown@cmd@arg#1\fi\fi\fi{%
\fi\fi\fi
\protect#1%
}
\makeatother
\begin{document}
\listoffigures
% test typeset context
\aaaa{\bbbb}
%
\makeatletter
\protected@edef\x{\aaaa{\bbbb}}
\typeout{\meaning\x}
\makeatother
\typeout{\aaaa{\bbbb}}
\begin{figure}
\caption{long \aaaa{\bbbb}}
\end{figure}
\end{document}
However a serious problem remains with deferred writing, which is used for the index, for example. If an index entry is written, \thepage
is prevented from expanding; the write node will therefore contain the macro \thepage
instead of the current page number and may thus be wrong. At shipout time, the write node expands its contents and flushes it to the file. At this time the page number is known and \thepage
expands to the right page number.
Unfortunately this causes an ambiguity for \aaaa
:
If it is written to a file, then it should use \detokenize
for itself and its argument to prevent any early expansions.
It should use \unexpanded
if it is used inside a macro definition by \protected@edef
and the macro is not written to a file, but executed later.
Unfortunately there is no safe and robust method to distinguish both cases from inside the macro \aaaa
.
The example above uses a heuristics and assumes that if \thepage
is disabled from expansion (via \let\thepage\relax
), then the macro is probably written to a file.
\bbbb
doesn't exist when you call\caption
. One way to avoid premature expansion is to use\aaaa{\protect\bbbb}
.\aaaa
's argument should only be expanded by\aaaa
itself and\caption
should not alter it in any way.\protect
certain components that you want to not expand, since\caption
does some legwork in the background to check for the size/width of the caption and whether or not to center/justify it.\ce
? This would make the problem much clearer.