66

I'm looking for a LaTeX way to control macro expansion, contingent on some macro being defined or not. I'm looking for something like this (but then actually working):

\newcommand[1]{\checkfor}{
  \if\isdef\csname{#1}
  ... expand this if command exists ...
  \else
  ... expand this if command does not exist ...
  \fi
}

which can then be called using

\checkfor{CommandName}

(this example is, of course, useless. The actual code I want to use this kind of expansion in is a package that dynamically creates a large number of macros from an even larger set of possible macros, with default behaviour for "all macros". Since not "all" macros may exist, I need some way to test whether a macro was declared, before I can expand based on its value).

2
  • I'm interested in answering a variant of this: Define a command differently, depending on the existence of \nameref (like \newcommand{\refn[1]{\nameref{#1}} vs. \newcommand{\refn}[1]{[#1]}). I tried any of the answers, but wasn't successful.
    – U. Windl
    Nov 12, 2019 at 0:04
  • 1
    Remember not to ask new questions in a comment section. If you have a genuinely new question, post it, and then link to this one after explaining what you're not asking about (since this question has an accepted answer, so not pointing out your question isn't quite the same question as this one would probably just get your post marked as a duplicate) Nov 12, 2019 at 0:40

5 Answers 5

56

Is this what you are looking for:

\documentclass[12pt]{article}

\newcommand{\checkfor}[1]{%
  \ifcsname#1\endcsname%
    ... command '#1' exists ...%
  \else%
    ... command '#1' does not exist ...%
  \fi%
}

\begin{document}
\checkfor{CommandName}\par
\checkfor{section}
\end{document}
6
  • 2
    Perhaps mention 'tail safe' approaches using \@firstoftwo/\@secondoftwo.
    – Joseph Wright
    Oct 4, 2011 at 21:31
  • 3
    Could you expand on what 'tail safe' means or give a link? I'd like to learn more. Oct 4, 2011 at 21:44
  • 4
    @PeterGrill Well, if the 'material to insert' needs to potentially use nested conditional, the leaving the \fi unclosed is not ideal (you get a token build up). There is also the case where the 'material' may need other arguments, which it won't 'see' with the \fi still there. In both of those cases, making the two branches into additional arguments of the macro and using \expandafter\@fitstoftwo/\expandafter\@secondoftwo in the branches is preferable. (Of course, for simple cases this is not required, but I'm assuming a general solution is needed.)
    – Joseph Wright
    Oct 5, 2011 at 6:00
  • 4
    For those who want to try this out; take care that {CommandName} is without the preceding \ backslash. Jun 19, 2013 at 8:48
  • 1
    People may also want to look at the documentation here: texfaq.org/FAQ-isdef
    – am70
    Nov 3, 2020 at 13:59
40

The etoolbox provides two macros for this:

\ifdef{<control sequence>}{<true>}{<false>}
Expands to <true> if the <control sequence> is defined, and to <false> otherwise.
Note that control sequences will be considered as defined even if their meaning
is \relax. This command is a LaTeX wrapper for the e-TeX primitive \ifdefined.

\ifcsdef{<csname>}{<true>}{<false>}
Similar to \ifdef except that it takes a control sequence name as its first argument.
This command is a LaTeX wrapper for the e-TeX primitive \ifcsname.
29

The LaTeX kernel standard macro here is \@ifundefined, used as

\@ifundefined{foo}
  {%
    % \foo not defined
  }
  {%
    % \foo defined
  }%

In earlier LaTeX2e releases, this test was not 'expandable' and would cause \foo to be equal to \relax if it had not previously been defined. However, the kernel now uses essentially the same code as in Peter's answer, which means that these issues are no longer present. (The change is possible as the LaTeX2e kernel now requires and uses e-TeX.)

1
  • This solution can be used to determine if the name foo is an existed macro. I wonder if there is a similar way to determine if the macro \foo exists?
    – lyl
    Feb 13, 2022 at 12:05
8

The package xifthen provides \isundefined which apparently has slightly different semantics:

This test differs from \@ifundefined in that it takes a real command—and not a command name—as argument, and also in that command which is let equal to \relax is not considered undefined.

So what you are looking for can be implemented as:

\newcommand[1]{\checkfor}{
  \ifthenelse{\isundefined{#1}}{%
    % expand undefined
  }{%
    % expand defined
  }%
}
0
2

A non-expandable variant of the test which does without whatsoever extensions:

\documentclass{article}
\makeatletter
\DeclareRobustCommand\checkfor[1]{%
  \begingroup
  \expandafter\ifx\csname#1\endcsname\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  {%
    \expandafter\endgroup\expandafter\ifx\csname#1\endcsname\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  }{\endgroup\@firstoftwo}%
}%
\makeatother

\begin{document}

\verb|\LaTeX| is \checkfor{LaTeX}{defined}{undefined}.

\verb|\relax| is \checkfor{relax}{defined}{undefined}.

\verb|\UndefiNED| is \checkfor{UndefiNED}{defined}{undefined}.

\verb|\UndefiNED| is \checkfor{UndefiNED}{defined}{undefined}.

\verb|\UndefiNED| is \checkfor{UndefiNED}{defined}{undefined}.

\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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