奇怪的 \patchcmd 行为

奇怪的 \patchcmd 行为

我对 etoolbox 包的奇怪行为有疑问。粗略地说,我想定义一个命令来定义宏,以便用户说\mynewcommand\acommand{some code},它将定义\acommand执行一些设置操作,运行some code然后进行一些清理。我想使用\patchcmd将任何出现的\optionsin转换为参数(例如 #1)。但是,由于空格的一些奇怪困难,我在说some code时遇到了一些困难。\patchcmdmacro 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
}

后面的行尾{才是罪魁祸首。

相关内容