我对 etoolbox 包的奇怪行为有疑问。粗略地说,我想定义一个命令来定义宏,以便用户说\mynewcommand\acommand{some code}
,它将定义\acommand
执行一些设置操作,运行some code
然后进行一些清理。我想使用\patchcmd
将任何出现的\options
in转换为参数(例如 #1)。但是,由于空格的一些奇怪困难,我在说some code
时遇到了一些困难。\patchcmd
macro cannot be retokenized cleanly
解决此问题的一个方法是禁用etoolbox
扫描安全检查,方法是说\def\etb@ifscanable##1{\@firstoftwo}
。我不太喜欢这个解决方案,因为它深入到etoolbox
包中。
如果我能理解下面这个例子中发生的事情,我想我就可以修复我的定义而不这样做。那么有没有etoolbox
专家可以告诉我为什么下面的代码会这样运行?
\documentclass{article}
\usepackage{etoolbox}
\tracingpatches
\makeatletter
\begin{document}
% Breaks sometimes:
\def\mynewcmda#1#2{
\gdef#1{%
\acommand
#2%
\options
}
\patchcmd#1{\options}{\athing}
{\message{replacement worked}}
{\message{replacement failed}}
}
% Always works:
\def\mynewcmdb#1#2{
\gdef#1{%
\acommand
#2%
\options
}
\bgroup
% Skip retokenization check, so "++ macro can be retokenized cleanly":
\def\etb@ifscanable##1{\@firstoftwo}
\patchcmd#1{\options}{\athing}
{\message{replacement worked}}
{\message{replacement failed}}
\global\let#1#1
\egroup
}
% Always works:
\def\mynewcmdc#1#2{
\gdef#1{%
\acommand{}%
#2%
\options
}
\patchcmd#1{\options}{\athing}
{\message{replacement worked}}
{\message{replacement failed}}
}
% Doesn't work for some mysterious reason
\mynewcmda\testa{
do things
}
% All the rest of these work:
\mynewcmda\testaprime{%
do things
}
\mynewcmdb\testb{
do things
}
\mynewcmdc\testc{
do things
}
\end{document}
日志文件显示:
[debug] tracing \patchcmd on input line 50
[debug] analyzing '\testa'
[debug] ++ control sequence is defined
[debug] ++ control sequence is a macro
[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
replacement failed
然后出现一堆消息说其他的\patchcmd
都成功了。
答案1
这并不奇怪。问题是,如果宏的替换文本的字符串表示包含类似以下内容的内容,则无法安全地重建宏
\macro<space><space>
因为这可以指的是\macro
后面跟着一个空格或者指的是\macro<space>
(名称中带有尾随空格)。
定义名称中带有尾随空格的宏总是会进行:\DeclareRobustCommand
例如,每个宏都会创建一个。
禁用检查非常危险,因为最终宏可能会执行与预期截然不同的操作。
坏空间从哪里冒出来?
\mynewcmda\testa{
do things
}
后面的行尾{
才是罪魁祸首。