测试命令是否已被另一个包定义

测试命令是否已被另一个包定义

我通常使用 LaTeX\newcommand来定义宏。但是,在使用分隔宏时,我使用\def。有没有办法让 LaTeX 在由 定义的命令\def被另一个包重新定义时触发错误,即如果有人试图重新定义该命令,就会触发 LaTeX 错误消息。

答案1

不管怎样,我认为 Yiannis 的答案(使用\newcommandbefore \def)是最好的方法,或者至少是最简单的方法。

e-TeX 提供了两个新原语来检查控制序列是否已定义。

  • \ifdefined\cs测试是否\cs是定义的控制序列。
  • \ifcsname cs\endcsname测试是否\cs是定义的控制序列。

etoolbox包包含更多适合 LaTeX 的包装器。

  • \ifdef{\cs}{true}{false}true如果\cs定义,则扩展为,false否则扩展为。
  • \ifcsdef{cs}{true}{false}类似,但用于控制序列名称。
  • \ifundef{\cs}{true}{false}与的否定类似,只是已经到的\ifdef控制序列被视为未定义。\let\relax
  • \ifcsundef{cs}{true}{false}类似,但用于控制序列名称。

etoolbox还包含其他有用的测试,例如控制序列是否是宏。

乍一看,使用\ifundef\ifcsundef检查命令是否似乎有些奇怪。但是,LaTeX的比较。原因是当 TeX 通过 构建一个它以前没有见过的控制序列时,它会给它 的值。这有一个缺点,就是无法区分未定义的控制序列的名称和已经 到 的控制序列的名称。它被设计为 LaTeX 内核的 的直接替代品。\relax\@ifundefined\csname cs\endcsname\relax\csname ...\endcsname\relax\let\relax\ifcsundef\@ifundefined

编辑:
我刚刚读了 Caramdir 的评论。没有好的办法可以防止另一个包重新定义您定义的命令。解决这个问题的一种可能方法是使用\AtBeginDocument{\def\cs{...}}比大多数其他宏更晚的定义宏,但这并不是万无一失的。另一个包可以做同样的事情,用户可以随时重新定义任何宏。

答案2

从技术上来说,保护宏名(命令序列)不被将来重新定义或在重新定义时触发错误是不可能的。TeX 甚至不保护其内部命令,也允许您重新定义它们。这是设计使然。

但是,您可以在某些点(例如在文档开始时)检查您的定义是否仍然有效,否则会触发错误:

% Inside your package
\newcommand*\mymcd{}% raises an error if the command is already defined
\def\mycmd ... { ... }  % Real definition of macro with parameter text
\let\my@cmd\mycmd  % Save macro away
\AtBeginDocument{%
  \ifx\mycmd\my@cmd\else
     \PackageError{mypackage}{Macro \string\mycmd\space got redefined!}{}%
  \fi
}

答案3

TeX 没有提供任何类似的功能\newcommand,但您可以按照下面的最小示例进行模拟:

\documentclass{article}
\begin{document}

\makeatletter
\newcommand\test@{}
\def\test@[#1][#2][#3][#4]{%
  ....
}

\newcommand\test@{}
\makeatother
\end{document}

通过首先使用定义命令\newcommand,然后用覆盖它,\def您可以确保如果之前已经定义了具有该名称的命令,LaTeX 会发出错误。

答案4

这是 Martin Scharrer 方案的概括。我们引入了两个命令\cmddef和,\robustcmddef用于定义简单而强大的命令,这些命令可以在任何地方进行一致性/保存性测试。这取自ltxtools-基础包裹。

\documentclass{article}
\usepackage{catoptions}
\makeatletter
\robust@def*\cmddef@teststcl#1{\cpt@testst{\cpt@testcl{#1}}}
\robust@def*\cmddef{\cmddef@teststcl{\cmd@def@\relax}}
\robust@def*\robustcmddef{\cmddef@teststcl{\cmd@def@\protected}}
\new@def\cmd@def@#1#2{%
  \ifcpt@st\let\l@ngrel@x\relax\else\let\l@ngrel@x\long\fi
  \ifcpt@cl\let\gl@balrel@x\global\else\let\gl@balrel@x\relax\fi
  \ifescapedTF{#2}{}{\cpt@notescapederr{#2}}%
  \ifdefTF#2{%
    \@latex@error{Command '\string#2' already defined}\@ehd
    \def\@tempa{\undefcs\@tempb}%
  }{%
    \def\@tempa{%
      \gl@balrel@x\let#2\@tempb
      \gl@balrel@x\letcsntocs{@test@command@\cptremovescape#2}#2%
      \undefcs\@tempb
    }%
  }%
  \afterassignment\@tempa
  \l@ngrel@x#1\def\@tempb
} 
\new@def\testcommand#1{%
  \ifescapedTF{#1}{}{\cpt@notescapederr{#1}}%
  \cptexpanded{%
    \ifmacroFT#1{%
      \noexpand\@secondoftwo
    }{%
      \expandafter\noexpand\csname @\expandafter\ifxTF
        \csname @test@command@\cptremovescape#1\endcsname
        #1{first}{second}oftwo\endcsname
    }%
  }%
}
\makeatother

测试:

\begin{document}
\def\y#1{Command `\string\x' has#1 changed}
\begingroup\tt
% Define command \x (* -> short; ! -> global):
\cmddef*!\x#1{#1}
% Test if command \x has changed since definition:
\testcommand\x{\y{n't}}{\y{}}
% Redefine command \x with \def:
\def\x{}
\par\medskip
% Test if command \x has changed since definition with \cmddef:
\testcommand\x{\y{n't}}{\y{}}
% The next one will produce and error:
% \cmddef!\x#1{#1}
\endgroup
\end{document}

相关内容