# Redefine \newcommand to insert code at start and end of custom macros

I would like to be able to insert code into the definitions of my custom macros, without having to modify each macro definition. This seemed like a simple case of redefining \newcommand, but this turns out not to be so easy (well for me anyway). I end up with:

Illegal parameter number in definition of \MacroB.
1 l.45 \newcommand*{\MacroB}[1]{b#1b}

My guess is that somewhere I need to double up the # but I don't understand why, and guessing which ones need to be doubled up did not work. So perhaps that is not the problem.

## Notes:

• The MWE below compiles fine -- one needs to uncomment the \def\EnableTrace{} to see the problem.
• The goal of this is to build up a stack trace of macros as they are being called.

## Code:

%\def\EnableTrace{}
\documentclass{article}

%% All packages included here...
\usepackage{xparse}
\usepackage{letltxmacro}

%% Start of custom macros...
\ifdefined\EnableTrace
\newcounter{NestingDepth}
\newcommand*{\StartMacro}[1]{%
\typeout{**** DEBUG: (Depth=\arabic{NestingDepth}) Started macro "\string#1".}%
\stepcounter{NestingDepth}%
}%
\newcommand*{\EndMacro}[1]{%
\typeout{**** DEBUG: (Depth=\arabic{NestingDepth}) Completed macro "\string#1".}%
}%
% --------------
\LetLtxMacro{\OldNewcommand}{\newcommand}%
\RenewDocumentCommand{\newcommand}{%
s%    #1 = * (ignored for now to keep the code below simple)
m%    #2 = macro name
O{1}% #3 = number of paramaters
o%    #4 = default value for first optional parameter (if there is one)
m%    #5 = code to execute
}{%
%
\ifnum#3=1\relax
\OldNewcommand{#2}{\StartMacro{#2}#5\EndMacro{#2}}%
\else
\IfBooleanTF{#4}{%
% first parameter of macro being defined is optional, as default value is provided
\OldNewcommand{#2}[#3][#4]{\StartMacro{#2}#5\EndMacro{#2}}%
}{%
% first parameter of macro being defined is mandatory
\OldNewcommand{#2}[#3]{\StartMacro{#2}#5\EndMacro{#2}}%
}%
\fi
}%
\fi

\newcommand*{\MacroB}[1]{b#1b}%
\newcommand*{\MacroA}[1]{a\MacroB{#1}a}%

\begin{document}
\MacroA{XXX}

\MacroB{YYY}
\end{document}

-
I don't understand what does \ifnum#3=1… do. After checking the number of parameters is 1, you call \OldNewCommand{#2}{…} with 0 arguments. – Manuel Aug 5 '14 at 21:01
If you delete everything between \ifnum and \else and also the corresponding \fi (i.e., leaving only the \IfBooleanTF… it compiles fine here. – Manuel Aug 5 '14 at 21:08
Does reading #5 with v instead of m help? (I can't try it myself for I am on my phone) – Henri Menke Aug 5 '14 at 21:09
For an o argument, the test is not \IfBooleanTF, but \IfNoValueTF or \IfValueTF. Of course, \LetLtxMacro is not needed for \newcommand, that has no argument. – egreg Aug 5 '14 at 21:22

There are strange conditionals in your code. Moreover, \IfBooleanTF is for arguments of type s or t, not for o.

\documentclass{article}
\usepackage{xparse}

\newif\ifEnableTrace
\newcounter{NestingDepth}

\newcommand{\StartMacro}[1]{%
\ifEnableTrace
\typeout{**** DEBUG: (Depth=\arabic{NestingDepth}) Started macro \string#1'.}%
\stepcounter{NestingDepth}%
\fi
}
\newcommand{\EndMacro}[1]{%
\ifEnableTrace
\typeout{**** DEBUG: (Depth=\arabic{NestingDepth}) Completed macro \string#1'.}%
\fi
}

\NewDocumentCommand{\NewCommand}{smO{0}om}{%
\IfBooleanTF{#1}
{\StartMacro{#2}#5\EndMacro{#2}}%
}

% if you want to enable tracing
\let\newcommand\NewCommand

\newcommand*{\MacroB}[1]{b#1b}
\newcommand*{\MacroA}[1]{a\MacroB{#1}a}

\begin{document}

\MacroA{XXX}

\MacroB{YYY}

\EnableTracetrue

\MacroA{XXX}

\MacroB{YYY}

\end{document}


Here's the terminal output

**** DEBUG: (Depth=0) Started macro \MacroA'.
**** DEBUG: (Depth=1) Started macro \MacroB'.
**** DEBUG: (Depth=1) Completed macro \MacroB'.
**** DEBUG: (Depth=0) Completed macro \MacroA'.
**** DEBUG: (Depth=0) Started macro \MacroB'.
**** DEBUG: (Depth=0) Completed macro \MacroB'.

-
@PeterGrill (2) at least that's an extra since you can disable tracing at some point in the document, and reenable it again whenever you want. – Manuel Aug 6 '14 at 6:31
@PeterGrill The first level expansion of \newcommand is \@star@or@long\new@command; no arguments. I added two levels for EnableTrace: you can enable tracing globally and then disable/enable it locally. – egreg Aug 6 '14 at 8:29

You use

\ifnum#3=1\relax
\OldNewcommand{#2}{\StartMacro{#2}#5\EndMacro{#2}}%
\else



\ifnum#3=1\relax
\OldNewcommand{#2}[1]{\StartMacro{#2}#5\EndMacro{#2}}%
\else


works.

By the way… why is it necessary the \ifnum at all? Just leaving the “else” branch of that \ifnum is enough:

%\def\EnableTrace{}
\documentclass{article}

%% All packages included here...
\usepackage{xparse}
\usepackage{letltxmacro}

%% Start of custom macros...
\ifdefined\EnableTrace
\newcounter{NestingDepth}
\newcommand*{\StartMacro}[1]{%
\typeout{**** DEBUG: (Depth=\arabic{NestingDepth}) Started macro "\string#1".}%
\stepcounter{NestingDepth}%
}%
\newcommand*{\EndMacro}[1]{%
\typeout{**** DEBUG: (Depth=\arabic{NestingDepth}) Completed macro "\string#1".}%
}%
% --------------
\LetLtxMacro{\OldNewcommand}{\newcommand}%
\RenewDocumentCommand{\newcommand}{%
s%    #1 = * (ignored for now to keep the code below simple)
m%    #2 = macro name
O{1}% #3 = number of paramaters
o%    #4 = default value for first optional parameter (if there is one)
m%    #5 = code to execute
}{%
%
%       \ifnum#3=1\relax
%           \OldNewcommand{#2}{\StartMacro{#2}#5\EndMacro{#2}}%
%       \else
\IfBooleanTF{#4}{%
% first parameter of macro being defined is optional, as default value is provided
\OldNewcommand{#2}[#3][#4]{\StartMacro{#2}#5\EndMacro{#2}}%
}{%
% first parameter of macro being defined is mandatory
\OldNewcommand{#2}[#3]{\StartMacro{#2}#5\EndMacro{#2}}%
}%
%       \fi
}%
\fi

\newcommand*{\MacroB}[1]{b#1b}%
\newcommand*{\MacroA}[1]{a\MacroB{#1}a}%

\begin{document}
\MacroA{XXX}

\MacroB{YYY}
\end{document}


By the way, why not default the number of arguments to [0] so it works exactly as the original \newcommand?

-
Hmmm... You are right, it should have been \ifnum#3=0 and the the default should be '0'. Those two changes fix this MWE. SO, then I need both the ifnum#3=0 and else branches. – Peter Grill Aug 6 '14 at 4:37