New answer
A LaTeX kernel later than June 2020 will provide the better
\NewCommandCopy
but also \RenewCommandCopy
and \DeclareCommandCopy
. The first one does the same work as \LetLtxMacro
as described in the “original answer”, but can also be employed with commands defined via \NewDocumentCommand
(or siblings).
Differently from \LetLtxMacro
, it will raise an error if the alias we want is already defined. In this case one can use \RenewCommandCopy
(that raises an error if the alias is not defined) or \DeclareCommandCopy
(no check is performed). Be careful when using these two forms.
Do prefer \NewCommandCopy
over \let
, unless you precisely know that \let
is OK.
Original answer
There are two main cases when \LetLtxMacro
should be used (and it doesn't harm if used in other cases).
Case 1
The command to get a copy of has been defined with \DeclareRobustCommand
, like
\DeclareRobustCommand{\foo}[1]{-#1-}
(with any number of arguments, even zero). There are other commands defined in different ways, but at the end come up as being in this same case.
What happens here is that LaTeX defines two commands, in a way similar to this
\def\foo{\protect\foo•}
\newcommand\foo•[1]{-#1-}
where by •
I mean a space in the name of the second command. How this trick is achieved is unimportant. We can realize if a command is of this kind by asking texdef
: with the command line
> texdef -t latex texttt
we get
\texttt:
macro:->\protect \texttt
\texttt :
\long macro:#1->\ifmmode \nfss@text {\ttfamily #1}\else \hmode@bgroup \text@command {#1}\ttfamily \check@icl #1\check@icr \expandafter \egroup \fi
This shows precisely what's happening: texdef
prints first the meaning of \texttt
, which is \protect\texttt•
(the space here is invisibile), but then know about the nature of this command and so prints also the meaning of \texttt•
and this is shown by the space before the colon.
What would be the problem in doing
\let\oldfoo\foo
\renewcommand\foo[1]{!\oldfoo{#1}!}
for changing the working of \foo
? Let's see what happens (I'll continue to use •
for denoting the space in the name of a macro).
\foo{BAR}
!\oldfoo{BAR}!
!\protect\foo•{BAR}!
!-BAR-!
which seems pretty good: the renewed macro does what we want. But wait! We use \DeclareRobustCommand
to be sure that \foo
in moving arguments is treated correctly. So let's see what happens when \foo{BAR}
is used in a moving argument, such as a section title; the annotation in the .toc
file will contain
!\foo {BAR}!
which in the end will result in printing !!-BAR-!!
, which is not what we want. Why? Because the writing process expands unprotected macros. So LaTeX does
\foo{BAR}
!\oldfoo{BAR}!
!\protect\foo•{BAR}!
!\foo• {BAR}!
Since \foo•
was preceded by \protect
, the last line is what's written. But when the .toc
file is read in, TeX only sees a space after \foo
, because it inputs a text file, so it will dutily expand \foo
.
If one uses
\LetLtxMacro{\oldfoo}{\foo}
LaTeX will essentially do
\def\oldfoo{\protect\oldfoo•}
\let\oldfoo•\foo•
so that the problem above will not appear any more.
Case 2
If the command we want to save the meaning of has been defined with \newcommand
to have an optional argument, then there are risks like in the previous case; they are explained in this answer. The situation is of the form
\newcommand{\xyz}[2][!]{#1-#2}
and, for instance, \smash
(as redefined by amsmath
) falls in this case. With
texdef -t latex -p amsmath smash
we'd get
\smash:
macro:->\@protected@testopt \smash \\smash {tb}
and this is a clear sign that \LetLtxMacro
should be used.
Other cases
If the command to save has been defined with \newrobustcmd
from the etoolbox
package, then \LetLtxMacro
should be used in order to safely copy its meaning.
Warning
Don't try \LetLtxMacro
with commands defined by \NewDocumentCommand
(or similar commands) from xparse
. The letltxmacro
package doesn't support them.
xparse
definitions, nor does it contain the detail provided by his answer.