49

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).

migrated from stackoverflow.com Oct 4 '11 at 21:22

This question came from our site for professional and enthusiast programmers.

  • 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 at 0:04
  • 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) – Mike 'Pomax' Kamermans Nov 12 at 0:40
38

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}
  • 2
    Perhaps mention 'tail safe' approaches using \@firstoftwo/\@secondoftwo. – Joseph Wright Oct 4 '11 at 21:31
  • 2
    Could you expand on what 'tail safe' means or give a link? I'd like to learn more. – Justin Bailey Oct 4 '11 at 21:44
  • @JosephWright: Sorry, but I don't know what 'tail safe' refers to. As a reference this question has a good discussion on: what do firstoftwo and secondoftwo do – Peter Grill Oct 4 '11 at 23:19
  • 3
    @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 '11 at 6:00
  • For those who want to try this out; take care that {CommandName} is without the preceding \ backslash. – Serge Stroobandt Jun 19 '13 at 8:48
26

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.
20

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.)

6

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
  }%
}

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.