重新定义 \newcommand 以在自定义宏的开始和结束处插入代码

重新定义 \newcommand 以在自定义宏的开始和结束处插入代码

我希望能够将代码插入到我的自定义宏的定义中,而不必修改每个宏定义。这看起来像是重新定义的简单情况\newcommand,但结果却不那么容易(至少对我来说)。我最终得到:

\MacroB 定义中的参数编号非法。
需要重新读取
1 l.45 \newcommand*{\MacroB}[1]{b#1b}

我猜想有些地方需要加倍,#但我不明白为什么,而且猜测哪些需要加倍也没有用。所以也许这不是问题所在。

笔记:

  • 下面的 MWE 编译得很好——需要取消注释\def\EnableTrace{}才能看到问题。
  • 这样做的目的是在宏被调用时建立它们的堆栈跟踪。

代码:

%\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]{%
        \addtocounter{NestingDepth}{-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}

答案1

您的代码中存在奇怪的条件。此外,对于或\IfBooleanTF类型的参数, 是,而对于 不是。sto

\documentclass{article}
\usepackage{xparse}

\newif\ifEnableTrace
\newcounter{NestingDepth}
\let\latexnewcommand\newcommand

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

\NewDocumentCommand{\NewCommand}{smO{0}om}{%
  \IfBooleanTF{#1}
    {\IfNoValueTF{#4}{\latexnewcommand*{#2}[#3]}{\latexnewcommand*{#2}[#3][#4]}}%
    {\IfNoValueTF{#4}{\latexnewcommand{#2}[#3]}{\latexnewcommand{#2}[#3][#4]}}%
  {\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}

这是终端输出

**** 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'.

答案2

你用

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

检查完之后,\newcommand\…[1]你会得到类似的结果\newcommand\…[0]

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

作品。

顺便问一下……为什么这\ifnum完全是必要的?只需保留“else”分支\ifnum就足够了:

%\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]{%
        \addtocounter{NestingDepth}{-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}

顺便问一下,为什么不将参数数量默认为,[0]以便它能够像原始一样工作\newcommand

相关内容