Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I normally use the LaTeX \newcommand to define macros. However, when using delimited macros I use \def. Is there a way that I can get LaTeX to trigger an error if the command defined by \def is being re-defined by another package, i.e, if someone tries to re-define the command a LaTeX error message is triggered.

share|improve this question
4  
Do you want to check whether a command is already defined (which is what all the answers are about), or do you want to make it impossible for someone else to overwrite your definitions? –  Caramdir Mar 1 '11 at 4:06
    
Indeed, half of the answers are answering one of these questions and half the other. –  Matthew Leingang Mar 1 '11 at 13:39

5 Answers 5

For what it's worth, I think that Yiannis' answer (using \newcommand before \def) is the best way to do it, or at least the simplest.

e-TeX provides two new primitives to check if a control sequence is defined.

  • \ifdefined\cs tests if \cs is a defined control sequence.
  • \ifcsname cs\endcsname tests if \cs is a defined control sequence.

The etoolbox package contains more LaTeX-friendly wrappers.

  • \ifdef{\cs}{true}{false} expands to true if \cs is defined, false otherwise.
  • \ifcsdef{cs}{true}{false} is similar but for control sequence names.
  • \ifundef{\cs}{true}{false} is similar to the negation of \ifdef except that control sequences that have been \let to \relax are considered undefined.
  • \ifcsundef{cs}{true}{false} is similar but for control sequence names.

etoolbox also contains other useful tests, for example if a control sequence is a macro.

Having \ifundef and \ifcsundef check if a command is \relax may seem odd at first. However, LaTeX's \@ifundefined compares \csname cs\endcsname to \relax. The reason for this is that when TeX constructs a control sequence via \csname ...\endcsname that it has not seen before, it gives it the value of \relax. This has the downside that there's no way to differentiate between the name of a control sequence that is undefined and one that has been \let to \relax. \ifcsundef was designed to be a drop-in replacement for the LaTeX kernel's \@ifundefined.

Edit:
I just read Caramdir's comment. There's no good way to prevent another package from redefining a command you define. One possible way around this is to use \AtBeginDocument{\def\cs{...}} to define a macro later than most other macros, but this is not foolproof. Another package can do the same thing and the user can redefine any macro at any time.

share|improve this answer
3  
+1. There are further examples of why the e-TeX way of doing it is good at this TeX FAQ. In particular avoiding littering TeX's memory space with lots of unnecessary \relaxes could be a good thing –  kahen Mar 1 '11 at 10:45
    
@TH Your first two statements (about \ifdefined and \ifcsname) aren't precise. \ifdefined a returns true but a isn't a control sequence. –  Ahmed Musa Feb 7 '12 at 20:39
    
@Ahmed: You're correct that \ifdefined actually tests if the following token is defined (although I'm not sure what its utility is other than testing for control sequences). What was imprecise about the \ifcsname statement? The documentation says, "True if the control sequence \csname...\endcsname would be defined; creates no new hash table entry." –  TH. Feb 9 '12 at 22:34
up vote 13 down vote accepted

TeX does not provide anything similar to \newcommand but you can emulate it as shown in the minimal below:

\documentclass{article}
\begin{document}

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

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

By defining first the command using \newcommand and then overriding it with a \def you ensure that LaTeX issues an error if a command with such a name has been defined earlier.

share|improve this answer
1  
I'm no expert of LaTeX internals, but as far as I can understand, it does not keep track which commands are already defined. Your example would fail to compile even without the first \newcommand. –  Caramdir Mar 1 '11 at 4:09
    
@Caramdir Why you say the code will not compile? As shown it correctly triggers an error, when you attempt to redefine it for the third time. –  Yiannis Lazarides Mar 1 '11 at 4:16
2  
@Caramdir true, but if you add it, it will also let you know if it was defined earlier (similar to your answer). –  Yiannis Lazarides Mar 1 '11 at 4:31
1  
@Martin: All I wanted to point out, that the sentence “using \newcommand [...] you ensure that LaTeX knows that a command with such a name has been defined” is misleading. –  Caramdir Mar 1 '11 at 16:29
1  
@Caramdir, @Yiannis: I edited the answer, hope it's not misleading anymore. –  Hendrik Vogt Mar 1 '11 at 17:52

It is technically impossible to protect a macro name (command sequence) from future redefinitions or trigger an error when it is redefined. TeX doesn't even protect its internal command and allows you to redefine them as well. This is by design.

You can however check at certain points (like at-begin-document) if your definition is still active and trigger an error otherwise:

% 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
}
share|improve this answer

No, \def is a TeX primitive and is destructive. And this is one reason LaTeX has \newcommand. If you and other package authors use \newcommand, then you will be warned against redefining each other's code. But even then, there is \renewcommand to get around that problem if you know what you're doing.

share|improve this answer

Here is a generalization of Martin Scharrer's scheme. We introduce two commands \cmddef and \robustcmddef for defining simple and robust commands that can be tested anywhere for consistency/preservation. This is taken from ltxtools-base package.

\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

Tests:

\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}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.