使用 etoolbox \apptocmd 修补时出现错误:“修补命令似乎嵌套在其他命令的参数中”

使用 etoolbox \apptocmd 修补时出现错误:“修补命令似乎嵌套在其他命令的参数中”

\apptocmd来自的命令etoolbox给出了一个我无法理解的错误。我知道如何解决此特定情况下的错误,但我想了解它抱怨的是什么,以便将来可以修复类似的错误。

这是我收到的错误:

[debug] tracing \apptocmd on input line 28
[debug] analyzing \H@old@part
[debug] ++ control sequence is defined
[debug] ++ control sequence is a macro
[debug] ++ control sequence is a macro with parameters
[debug] -- nested patching command and parameters in patch
[debug] -> the patching command seems to be nested in the
[debug]    argument to some other command
[debug] -> the patch text seems to contain # characters
[debug] -> either avoid nesting or use # characters with
[debug]    category code 12 in the patch text
[debug] -> simply doubling the # characters will not work

! Package cont Error: can't patch \H@old@part.

See the cont package documentation for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.28     }

? 

以下会产生上述错误:

\documentclass{report}
\usepackage{etoolbox}
\tracingpatches
\usepackage{hyperref}
\makeatletter

% error handler for patching commands
\newrobustcmd{\cont@err}[1]{\PackageError{cont}{can't patch \protect#1}{}}%

% contains the name of the current part
\gdef\cont@name@part{\PackageError{cont}{not in a part}{}}

% patch \part to save the part name to \cont@name@part.  note
% that \part is a parameterless macro that has \@part as its last
% token, which does take the arguments.  Thus, \part can't be
% appended to; \@part has to be appended to instead.
%
% The hyperref package redefines \@part, so when hyperref is
% loaded \H@old@part needs to be patched instead.
\ifdef{\H@old@part}{
  \apptocmd{\H@old@part}{%
    \gdef\cont@name@part{#2}%
  }{}{\cont@err{\H@old@part}}
}{
  \apptocmd{\@part}{%
    \gdef\cont@name@part{#2}%
  }{}{\cont@err{\H@old@part}}
}

\begin{document}
\part{first part}
this part's name is ``\cont@name@part''
\end{document}

以下方法也无效:

\catcode`\#=12
\ifdef{\H@old@part}{
  \apptocmd{\H@old@part}{%
    \gdef\cont@name@part{#2}%
  }{}{\cont@err{\H@old@part}}
}{
  \apptocmd{\@part}{%
    \gdef\cont@name@part{#2}%
  }{}{\cont@err{\H@old@part}}
}
\catcode`\#=6

它会产生以下错误:

[debug] analyzing \H@old@part
[debug] ++ control sequence is defined
[debug] ++ control sequence is a macro
[debug] ++ control sequence is a macro with parameters
[debug] -- macro cannot be retokenized cleanly
[debug] -> the macro may have been defined under a category
[debug]    code regime different from the current one
[debug] -> the replacement text may contain special control
[debug]    sequence tokens formed with \csname...\endcsname;
[debug] -> the replacement text may contain carriage return,
[debug]    newline, or similar characters

我可以通过执行以下操作来解决该问题:

\expandafter\apptocmd\expandafter{%
  \csname\ifdef{\H@old@part}{H@old}{}@part\endcsname%
}{%
  \gdef\cont@name@part{#2}%
}{}{\cont@error{\@part}}

不过,我想知道如何在使用前一种方法(\apptocmd在的参数中\ifdef)时修复错误。

答案1

patch 命令显然使用了 catcode 更改,因此就像\verb你可以做一些事情来使\verb某些参数中的一半工作一样,TeX 的基本规则是使用 catcode 更改的命令在其他命令的参数中不起作用。

原因是 catcode 的变化改变了 TeX 扫描仪将字符转换为标记的方式,但宏参数在扫描参数以寻找右括号时会被扫描和标记,因此传递给宏的内容#1不是#2人物这是一个列表代币并且 catcode 的变化对标记的解释没有影响。

答案2

问题出在#2参数中。使用

\ifdefined\H@old@part
  \apptocmd{\H@old@part}{\gdef\cont@name@part{#2}}{}{\cont@err{\H@old@part}}
\else
  \apptocmd{\@part}{\gdef\cont@name@part{#2}}{}{\cont@err{\@part}}
\fi

我正在开发一个扩展版本xpatch,我很高兴地告诉你

\documentclass{report}
\usepackage{etoolbox} % for \ifdef
\usepackage{regexpatch} % the experimental version of xpatch

\usepackage{hyperref} 

\makeatletter
\ifdef{\H@old@part}
  {%
   \regexpatchcmd{\H@old@part}{$}{\c{gdef}\c{cont@name@part}\cB\{\cP\#2\cE\}}{}{}%
  }
  {%
   \regexpatchcmd{\@part}{$}{\c{gdef}\c{cont@name@part}\cB\{\cP\#2\cE\}}{}{}%
  }
\makeatother

\begin{document}


\part{first part}
this part's name is ``\makeatletter\cont@name@part\makeatother''
\end{document}

似乎可以达到预期效果,无论有没有hyperref。该包已上传至 CTAN。

答案3

解决这个问题的一种可能性是预先定义修补命令,并在#不允许使用普通命令的上下文中以“包装”形式使用它们:

\makeatletter
\newcommand\defappcommand
{%
  \begingroup
    \catcode`\#=12
    \@defappcommand
}
\newcommand\@defappcommand[2]
{%
  \endgroup
  \def#1{#2}%
}
\makeatother

进而

\defappcommand\apptoH@old@part
{\apptocmd{\H@old@part}{\gdef\cont@name@part{#2}}{}{\cont@err{\H@old@part}}}

\defappcommand\appto@part
{\apptocmd{\@part}{\gdef\cont@name@part{#2}}{}{\cont@err{\@part}}}

\ifdef{\H@old@part}{
  \apptoH@old@part
}{
  \appto@part
}

相关内容