4

There are several commands in LaTeX that have been made robust so that they can be used in the arguments of other commands that move their contents around (an explanation of the problem is given in What is the difference between Fragile and Robust commands?). LaTeX usually simply defines two commands for this, one that calls a \protected helper command and another one that does the actual stuff. As an example, let's look at the definion of \textbf:

\textbf:
macro:->\protect \textbf
\textbf :
\long macro:#1->\ifmmode ... \fi

This is pretty straightforward and easy to understand. Another command, which is not protected by default, is \(. The fixltx2e package provides a robustified command, though:

\(:
macro:->\x@protect \(\protect \(
\( :
macro:->\relax \ifmmode \@badmath \else $\fi

And here things start to get confusing. \textbf and \( are very similar in their definitions, both wrap some stuff into \ifmmode ... \fi. So one would expect that \( were protected in the same way: by just prefixing it with a \protect. However, \( uses \x@protect and \@x@protect which have a somewhat strange definition:

\x@protect:
macro:#1->\ifx \protect \@typeset@protect \else \@x@protect #1\fi
\@x@protect:
macro:#1\fi #2#3->\fi \protect #1

So could anyone explain why \x@protect is used here and how it works exactly? Are there any advantages over simply using \protect?

  • 1
    note \( is robust in recent latex releases. (last couple of years or so) – David Carlisle Jun 28 '17 at 8:16
5

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 there any reason why \@x@protect is not defined with the signature #1\fi\protect #2, i.e. with \protect instead of #2? – schtandard Jun 12 at 11:02
  • @schtandard Preventative programming? – egreg Jun 12 at 12:20
  • But wouldn't #1\fi\protect #2 be safer (i.e. more preventative)? After all, this would lead to a clearer error more often when \@x@protect is used in inappropriate places, instead of just gobbling some tokens. (Maybe there is no reason. I was just wondering if I am missing anything in thinking that #1\fi\protect #2 would be better.) – schtandard Jun 12 at 13:23

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.