\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
}