12

Unlike most questions on this site, my problem is that my code compiles without error and I do not want it to.

I want to be able the check whether \command is defined and to do something if it is defined, but nothing if it is not. I do not want \command to be set to \relax if it is not defined. I don't want the code to have any effect at all if it is not defined.

Here's an example. In this case, it is easy to work around the issue, so this is for illustrative purposes only. That is, this particular case is not itself a problem - it is just a simple example to demonstrate the question.

\documentclass{article}
\makeatletter
  \@ifundefined{chapter}{}{\renewcommand\chapter[1]{turtles}}
\makeatother
\usepackage{kantlipsum}
\begin{document}
\chapter{a}
\kant[1-10]
\end{document}

As I mentioned, in this particular case, an easy workaround is available: I can check if \@chapter is defined rather, using its definition as a proxy for \chapter.

But is there a way to check if \chapter is defined, without either setting it to \relax if it is not or checking only indirectly using a proxy?

EDIT

This is the third case mentioned in discussion where \ifdefined...\fi causes problems but the \ifdef{}{}{} method works well as a replacement for the \@ifundefined{}{}{} original. Provided by request.

\documentclass{article}
\makeatletter
% \usepackage{etoolbox}% for \ifdef method
% \ifdef{\chapter}{% Werner's comment on Alan Munn's answer - this method works well
\@ifundefined{chapter}{}{% original - works but \chapter no longer gives an error as it is set to \relax
% \ifdefined\chapter%ref.: Alan Munn's answer at https://tex.stackexchange.com/a/268467/ - unfortunately does not work here (at least, not with the knowledge I have to implement it)
  \renewcommand\chapter{%
          \if@openright
                  \cleardoublepage
          \else
                  \clearpage
          \fi
          \thispagestyle{plain}%
          \global\@topnum\z@
          \@afterindentfalse
          \secdef\@chapter\@schapter}%  end \renewcommand\chapter
  \def\@chapter[#1]#2{%
          \ifnum \c@secnumdepth >\m@ne
                  \if@mainmatter
                          \refstepcounter{chapter}%
                          \typeout{\@chapapp\space\thechapter.}%
                          \addcontentsline{toc}{chapter}%
                          {\protect\numberline{\thechapter}#1}%
                  \else
                          \addcontentsline{toc}{chapter}{#1}%
                  \fi
          \else
                  \addcontentsline{toc}{chapter}{#1}%
          \fi
          \chaptermark{#1}%
          \addtocontents{lof}{\protect\addvspace{10\p@}}%
          \addtocontents{lot}{\protect\addvspace{10\p@}}%
          \if@twocolumn
                  \@topnewpage[\@makechapterhead{#2}]%
          \else
                  \@makechapterhead{#2}%
                  \@afterheading
          \fi}% end definition of \@chapter
  \def\@makechapterhead#1{%
          \vspace*{50\p@}%
          {%
                  \parindent \z@ \raggedright \normalfont
                  \ifnum \c@secnumdepth >\m@ne
                          \if@mainmatter
                                  \fontsize{\@xivpt}{14}\mdseries\centering\scshape{\@chapapp\space \thechapter}%
                                  \par\nobreak
                                  \vskip 20\p@
                          \fi
                  \fi
                  \interlinepenalty\@M
                  \fontsize{\@xivpt}{18}\bldsc\centering #1\par\nobreak
                  \vskip 40\p@
          }%
  }%    end definition \@makechapterhead
  \def\@schapter#1{%
          \if@twocolumn
                  \@topnewpage[\@makeschapterhead{#1}]%
          \else
                  \@makeschapterhead{#1}%
                  \@afterheading
          \fi}% end definition \@schapter
  \def\@makeschapterhead#1{%
          \vspace*{50\p@}%
          {%
                  \parindent \z@ \raggedright
                  \normalfont
                  \interlinepenalty\@M
                  \fontsize{\@xivpt}{18}\bldsc\centering #1\par\nobreak
                  \vskip 40\p@
          }%
  }%    end definition \@makeschapterhead
}%      end of stuff to do only if \chapter is defined - \@ifundefined method
% }{}% \ifdef method
% \fi% \ifdefined method
\makeatother
\begin{document}
  \chapter{a}
\end{document}
  • 1
    That's one of the more interesting subject lines I've seen on TeX.sX :P – Sverre Sep 20 '15 at 13:31
  • what is the problem of \ifx\chapter\mtOrcfrundef\else\renewcommand\chapter[1]{turtles}\fi? – touhami Sep 21 '15 at 7:15
  • @touhami It doesn't work in the actual code e.g. the 'third case' given above. I agree it will work for the (obviously too) minimal example I originally posted. – cfr Sep 21 '15 at 12:11
  • @cfr excuse me, I think the problem is \@ifundefined's definition, one can define new command \def\ifmtundefined#1{% \ifx#1\mtundef \expandafter\@firstoftwo \else \expandafter\@secondoftwo \fi} – touhami Sep 21 '15 at 17:29
  • @touhami Similar to egreg's answer? I don't understand this well enough to know, so this is just a question! – cfr Sep 21 '15 at 19:36
10

The simplest is to use the e-TeX \ifdefined macro instead:

\documentclass{article}
\ifdefined\chapter\renewcommand\chapter[1]{turtles}\fi
\usepackage{kantlipsum}
\begin{document}
\chapter{a}
\kant[1-10]
\end{document}

This won't work if you have other conditionals inside it (thus the source of your larger problem) in which case Werner's solution using etoolbox is preferred:

\documentclass{article}
\usepackage{etoolbox}
\makeatletter
  \ifdef{\chapter}{
    \renewcommand{\chapter}[1]{%
    \if@openright
        \cleardoublepage
     \else
        \clearpage
      \fi
    turtles}
}{}
\makeatother
\usepackage{kantlipsum}
\begin{document}
\chapter{a}
\kant[1-10]
\end{document}
  • 1
    etoolbox also provides a host of other stuff, including \ifcsdef or \ifdef. Then there's also \ifcsname. – Werner Sep 20 '15 at 0:32
  • Presumably \ifdefined... \else...\fi also works? Thanks. (I know I said I wanted it to do nothing in the question, but sometimes I do want it to do something - just not defining the command!) – cfr Sep 20 '15 at 0:44
  • @cfr Yes, it's a regular \if... expression. – Alan Munn Sep 20 '15 at 0:46
  • Unfortunately, this breaks stuff in my real document. It works in 2 out of 3 places, but \chapter still ends up getting defined by the third :(. – cfr Sep 20 '15 at 0:57
  • @cfr: What about a more comprehensive example? – Werner Sep 20 '15 at 1:00
6

You lose expandability, of course, but this is the “easy” way:

\documentclass
%{book}
{article}

\makeatletter
\begingroup
\@ifundefined{chapter}{\endgroup}{\endgroup\renewcommand\chapter[1]{turtles}}
\makeatother

\usepackage{kantlipsum}

\begin{document}

\chapter{a}
\kant[1-10]

\end{document}

The method with \ifdef from etoolbox may give wrong results if a command has already been appeared in the scope of \@ifundefined and so it has been made equivalent to \relax.

An expandable test should branch with \ifdefined and, in this case, seeing if the command is equivalent to \relax.

\makeatletter
\newcommand{\x@ifundefined}[3]{%
   \ifcsname #1\endcsname
     %either defined or \relax
     \expandafter\@firstoftwo
   \else
     %undefined
     \expandafter\@secondoftwo
   \fi
   {\x@ifrelax{#1}{#2}{#3}}{#2}%
 }
\newcommand{\x@ifrelax}[3]{%
  \expandafter\ifx\csname #1\endcsname\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {#2}{#3}%
}
\makeatletter

A test will show we're OK:

\makeatletter
\edef\foo{\x@ifundefined{bbcccddd}{undef}{def}}\show\foo
\edef\foo{\x@ifundefined{relax}{undef}{def}}\show\foo
\edef\foo{\x@ifundefined{mbox}{undef}{def}}\show\foo
\show\bbcccddd
\makeatother

will output

> \foo=macro:
->undef.
l.26 ...fundefined{bbcccddd}{undef}{def}}\show\foo

? 
> \foo=macro:
->undef.
l.27 ...x@ifundefined{relax}{undef}{def}}\show\foo

? 
> \foo=macro:
->def.
l.28 ...\x@ifundefined{mbox}{undef}{def}}\show\foo

? 
> \bbcccddd=undefined.
l.29 \show\bbcccddd
  • Hmmm.... Thank you. Wouldn't it be simpler to use \ifundef rather than spinning a custom test? Or will that not work? – cfr Sep 20 '15 at 20:39
  • @cfr Maybe \ifundef doesn't leave a command defined. But this is package free. ;-) – egreg Sep 20 '15 at 20:53
  • Sure. But packages are just code ;). Besides, I'm already using etoolbox. The only thing using \ifundef changes is that I need to load it a little earlier. Unless you also have code for \AtEndPreamble{...}? ;) Frankly my code is such a mess, that loading a few dozen packages is really neither here nor there. – cfr Sep 20 '15 at 22:21

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.