We don't want that a space is added after \(
when it's written in the aux file. That's what \x@protect
is for. It should never be used either in macro definitions or in documents.
Let's see what happens with \(
(which, by the way, has been robust in the kernel for a couple of years).
I'll use one line for expansion step; a •
denotes a space in the macro name. A part in 《》
denotes tokens that have already been sent to the stomach.
Normal typesetting
Here \protect
is \@typeset@protect
, which is the same as \relax
.
\(
\x@protect\(\protect\(•
\ifx\protect\@typeset@protect\else\@x@protect\(\fi\protect\(•
\protect\(•
《\protect》\relax\ifmmode\@badmath\else$\fi
and here it's clear what happens.
Writing to files or doing \protected@edef
Here \protect
is not \@typeset@protect
. The definition of \@x@protect
is
% latex.ltx, line 988:
\def\@x@protect#1\fi#2#3{%
\fi\protect#1%
}
Now the expansions
\(
\x@protect\(\protect\(•
\ifx\protect\@typeset@protect\else\@x@protect\(\fi\protect\(•
\@x@protect\(\fi\protect\(•
\fi\protect\(
\protect\(
(The expansion of \fi
is empty.) At this point the execution depends on what \protect
really is (either \noexpand\protect\noexpand
or \string
). Note that TeX won't add a space after \(
when writing to a file.
Why?
As said at the beginning, we don't want spaces creep in after control symbols such as \(
or \@
(or active characters). If the same path as for control words were followed, the result would be writing \(•
which is not wanted.
See Moving arguments and \protect: coming to grips with the definitions for other important aspects of \protect
.
Just for completeness, \DeclareRobustCommand
is defined to be \@star@or@long\declare@robustcommand
; the first macro tests for a *
(consuming it if present), setting a conditional that will be used by \new@command
later on and does not concern us. More important is to see what \declare@robustcommand
does:
% latex.ltx, line 963:
\def\declare@robustcommand#1{%
\ifx#1\@undefined\else\ifx#1\relax\else
\@latex@info{Redefining \string#1}%
\fi\fi
\edef\reserved@a{\string#1}%
\def\reserved@b{#1}%
\edef\reserved@b{\expandafter\strip@prefix\meaning\reserved@b}%
\edef#1{%
\ifx\reserved@a\reserved@b
\noexpand\x@protect
\noexpand#1%
\fi
\noexpand\protect
\expandafter\noexpand\csname
\expandafter\@gobble\string#1 \endcsname
}%
\let\@ifdefinable\@rc@ifdefinable
\expandafter\new@command\csname
\expandafter\@gobble\string#1 \endcsname
}
Taming the monster is not really so difficult. First the argument is tested for being defined or not, in the former case the “Redefining” info message is issued.
Now we have to distinguish two cases: the behavior will be different for a control symbol (example \?
) from a control word (example \foo
).
The macro \reserved@a
will contain the stringified version of the argument, nothing particular here. Then \reserved@b
is defined to expand to #1
and then redefined (with \edef
) in a peculiar way. Let's see what results in the two cases
\? → \?
\foo → \foo•
(as before, •
denotes a space; however, the result stored in \reserved@a
and \reserved@b
are just strings of characters).
Now comes the definition of #1
, using \edef
. For \?
we'll have
\x@protect\?\protect\?•
For \foo
we'll get just
\protect\foo•
(now the tokens are control sequences and the •
denotes a space in the name)
Then the sequence with the trailing space in its name will be defined, with \new@command
.
This shows why \x@protect
should not be used anywhere: it does nothing useful except when working with robusted commands and this happens at a level the user/programmer need not be aware of.
\(
is robust in recent latex releases. (last couple of years or so) – David Carlisle Jun 28 '17 at 8:16