我通常使用 LaTeX\newcommand
来定义宏。但是,在使用分隔宏时,我使用\def
。有没有办法让 LaTeX 在由 定义的命令\def
被另一个包重新定义时触发错误,即如果有人试图重新定义该命令,就会触发 LaTeX 错误消息。
答案1
不管怎样,我认为 Yiannis 的答案(使用\newcommand
before \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}