[Tex/LaTex] How to use \patchcmd with commands with optional arguments

etoolboxmacrosoptional arguments

This question led to a new package:
xpatch

This is likely a simple question, but I'm getting stuck on the small details. I have an internal command, defined by a package as

\newcommand\foo@[3][]{...stuff...}

and somewhere in there I'd like to patch the command to include a call to \label{#1}. I'm already using etoolbox, so I planned to use

\patchcmd{\foo@}{hook}{\label{#1}hook}{success}{failure}

Unfortunately this doesn't work, because \foo@ is defined as

> \foo@=macro:
->\@protected@testopt \foo@ \\foo@ {}.
l.359 \show\foo@

Which has no parameters and therefore the patching code fails. So I tried to figure out how to get \\foo@  (note the doubled slash and the trailing space) as a csname, and I got snarled up.

So then I tried using the old-style approach of \let\my@foo@=\foo@\renewcommand\foo@[3][]{...\my@foo@[#1]{#2}{#3} — I know the original idiom is to use \let and \def, but since \foo@ was defined with \newcommand I tried using \renewcommand. But this doesn't work, and fails with a TeX capacity exceeded error, and the log file appears to show an endless cycle of macro expansion.

So. What is the correct name for the internal macro defined by \newcommand when optional arguments are present, and how do I build the csname for that so I can patch it with \patchcmd?

Best Answer

You can get \\foo@ using \csname\string\foo@\endcsname. The space shown behind it with \show is not part of the macro name (but could be added using a \space before \endcsname). So you can use:

\expandafter\patchcmd\csname\string\foo@\endcsname{hook}{\label{#1}hook}{success}{failure}

Full example:

\documentclass{article}

\usepackage{etoolbox}

\begin{document}

\makeatletter
\newcommand\foo@[3][]{stuff (#1) more stuff :#2: hook even more stuff = *#3*}

\expandafter\patchcmd\csname\string\foo@\endcsname{hook}{\label{#1}hook}{success}{failure}

\texttt{\string\foo@ = \meaning\foo@}

\texttt{\expandafter\string\csname\string\foo@\endcsname = \expandafter\meaning\csname\string\foo@\endcsname}

\makeatother


\section{Test}
\makeatletter
\foo@[test]{One}{Two}
\makeatother

\section{Test2}
See section \ref{test}!

\end{document}

Result

Related Question