@u30 群主提出了一个cleveref宏包的疑似bug:
\documentclass{book}
\usepackage{cleveref}
\renewcommand{\thepage}{\arabic{chapter}\textbf{/}\arabic{page}}
\begin{document}
Text.\label{bug}
\end{document}这将会导致
! Use of \label@optarg doesn't match its definition.
\text@command #1->\edef \reserved@a {
\unexpanded {#1}}\ifx \reserved@a \@emp.感觉这是个展开的问题,于是尝试添加抑制展开的\unexpanded后正常:
\documentclass{book}
\usepackage{cleveref}
\renewcommand{\thepage}{\unexpanded{\textbf{abc}}}
\begin{document}
Text.\label{bug}
\end{document}我又尝试了其他的宏如\aaa,又一切正常。
\documentclass{book}
\def\aaa{234}
\usepackage{cleveref}
\renewcommand{\thepage}{\aaa}
\begin{document}
Text.\label{bug}
\end{document}除此之外联想到之前的提问,我有测试了ReNewDocumentCommand和ReNewExpandableDocumentCommand,前者正常而后者与\renewcommand报相同的错误.
\documentclass{book}
\usepackage{cleveref}
\RenewDocumentCommand{\thepage}{}{\arabic{page}\textbf{/}\arabic{page}} % works
% \RenewExpandableDocumentCommand{\thepage}{}{\arabic{page}\textbf{/}\arabic{page}} % fails
\begin{document}
Text.\label{bug}
\end{document}结合报错信息找到了cleveref.sty中的\label@optarg定义如下:
% Line 82-96
\def\label@optarg[#1]#2{%
\cref@old@label{#2}%
\@bsphack%
\edef\@tempa{{page}{\the\c@page}}%
\setcounter{page}{1}%
\edef\@tempb{\thepage}%
\expandafter\setcounter\@tempa%
\cref@constructprefix{page}{\cref@result}%
\protected@edef\cref@currentlabel{%
\expandafter\cref@override@label@type%
\cref@currentlabel\@nil{#1}}%
\protected@write\@auxout{}%
{\string\newlabel{#2@cref}{{\cref@currentlabel}%
{[\@tempb][\arabic{page}][\cref@result]\thepage}}}%
\@esphack}%雾月老师提到过,所有DocumentCommand是protect宏,而newcommand与NewExpandableCommand均可以被\edef展开,这应该也是上面为何两者均报错(吧)。
\label@optarg命令定义中,为何需要保护\textbf(看latexdef他已经被\protect了)而不需要保护\aaa或\bbb不被展开。谢谢鱼老师的解释,可是下面的例子却是正常的(?):
\documentclass{book}
\def\bbb#1{txt{#1}txt}
\usepackage{cleveref}
\renewcommand{\thepage}{\bbb{111}}
\begin{document}
Text.\label{bug}
\end{document}另附latexdef \textbf的结果如下是一个protect`宏:
\textbf:
macro:->\protect \textbf
\textbf :
\long macro:#1->\ifmmode \nfss@text {\bfseries #1}\else \hmode@bgroup \text@command {#1}\bfseries \check@icl #1\check@icr \expandafter \egroup \fi
同样是在这个链接的评论里提到,
为了使得 robust cmd 达到应有的效果,必须使用这几个 \protected@ 开头的命令,而 protected 宏则没有这个要求。因为 \label@optarg 在第一次展开 \thepage 时用的是 \edef,而不是 \protected@edef,只有后者才能保护 \protect 宏。问题就在于此。
由于 LaTeX 内核用 begindocument 钩子 patch 了 cleveref 的几个命令,\label@optarg 恰好在其中,所以自己 patch 也必须使用这个钩子才行。可查看 latex2e-first-aid-for-external-files.pdf 这个文件了解 patch 了哪些命令。
啊!我好像当时就是这一段带
\protected@没看懂然后跳过了,后来忘记惹...这下我明白
\edef和\protected@edef的区别了我会去看一下
latex2e-first-aid-for-external-files.pdf,谢谢您!