3

In the following MWE, I have a macro \mytest that tests whether its argument is a or b, returning "yes"/"no", respectively. This macro uses (a) \ifthenelse and (b) \equal from the if then package.

Works fine, except…

When I place the call in a section heading, I get:

Undefined control sequence. \equal {a}{a} l.9 ...tion{Troublesome construction: \mytest{a}.}

\documentclass{article}
\usepackage{ifthen}
\newcommand{\mytest}[1]{%
    \ifthenelse{\equal{#1}{a}}{yes}{%
    \ifthenelse{\equal{#1}{b}}{no}{%
}}}
\begin{document}
Troublesome construction: \mytest{a}.
%\section{Troublesome construction: \mytest{a}.}
\end{document}

I've commented out the problematic \section{Troublesome construction: \mytest{a}.} so that it will compile.

Why is this not working in a section heading?

5
  • 3
    Try \section{Troublesome construction: \protect\mytest{a}.}.
    – user121799
    Sep 6 '18 at 15:51
  • 3
    \ifthenelse is not expandable. You can try \section{Troublesome construction: \protect\mytest{a}.} and that should do the right thing in most situations. Another possibility would be to try and re-write the conditional expandably.
    – moewe
    Sep 6 '18 at 15:51
  • 1
    As for the explanation, here is one. Whether or not there is an earlier one, I do not know.
    – user121799
    Sep 6 '18 at 15:54
  • 1
    The suggestions to prepend with protect (i.e., rewrite as \section{Troublesome construction: \protect\mytest{a}.) worked completely. Moreover, the link to xstring functions in section/paragraph headers was also helpful in particular because it pointed to Fragile and Robust commands. I rewrote the macro using \DeclareRobustCommand\mytest[1]{… and that also worked, and that's better for the end user. Sep 6 '18 at 16:20
  • 2
    Depending on the exact definition of the macro a robust command may not give the same output in the TOC and the heading. Compare \documentclass{article} \newcounter{myfoo} \begin{document} \tableofcontents \stepcounter{myfoo} \section{Foo \themyfoo} \end{document} to \section{Foo \protect\themyfoo} in the TOC. So you should check carefully whether a robust command is appropriate or whether you are better of trying to get \mytest to be expandable.
    – moewe
    Sep 6 '18 at 19:55
3

As discussed in the comment the problem is that \ifthenelse is not expandable, so it does not yield the expected result when it is expanded in an \edef (or \write). That makes the entire macro \mytest fragile, which means it can't be used in moving argument like section headings or captions. Much more background can be found in What is the difference between Fragile and Robust commands?

One solution would be to \protect the macro or define it to be robust in the first place. See xstring functions in section/paragraph headers. That should work in many cases, but it can be a problem for macros that depend not only on their input, but also on some global 'variable'. For example

\documentclass{article}
\newcounter{myfoo}
\begin{document}
\tableofcontents
\stepcounter{myfoo}
\section{Foo \protect\themyfoo}
\end{document}

gives

In the TOC: 1 Foo 0//In the heading 1 Foo 1

while the unprotected \section{Foo \themyfoo} would do the right thing (because \themyfoo is expandable) and would show "Foo 1" in both instances.

In this case the unprotected (and expandable) macro is "evaluated" immediately and its expansion is used in all later steps. If the macro is protected it is not immediately expanded, instead it is passed on in its macro form until it really needs to be evaluated for typesetting. But the context for the two evaluations (one in the TOC the other in the heading) differs and then so does the result.

So if your macro relies on more than just its input to determine its output, you might be better off trying to create an expandable version. Instead of ifthen's conditionals you can use etoolbox's \ifstrequal

\documentclass{article}
\usepackage{etoolbox}
\newcommand{\mytest}[1]{%
  \ifstrequal{#1}{a}
    {yes}
    {\ifstrequal{#1}{b}
       {no}
       {}}}

\begin{document}
\tableofcontents
\section{Nothing problematic here}
Troublesome construction: \mytest{a}.
\section{Troublesome construction: \mytest{a}.}
\end{document}

TOC: 1 Nothing problematic here//2 Troublesome construction: yes.////Section heading: 1 Nothing problematic here////Body text: Troublesome construction: yes.////Section heading: 2 Troublesome construction: yes.

5
  • 1
    although I agree it is difficult to say in few words, you first paragraph sort of implies that non-expandable constructs are fragile per nature; but actually moving arguments need most of the time to be protected against premature expansion, so the general rule is to make the macros protected against expansion, which is sort of the opposite of expandable.... (I know I don't help :))
    – user4686
    Sep 7 '18 at 6:51
  • @jfbu Thank you for the comment. I will have to think of a way to salvage that first paragraph ... It might take a while.
    – moewe
    Sep 7 '18 at 7:01
  • 1
    @jfbu I have tried to save it, please let me know what you think.
    – moewe
    Sep 7 '18 at 7:08
  • congrats on the effort... I never found Lamport's concept of "fragile" that useful/clear, but I am also of the opinion everyone should have a working knowledge of TeX by Topic, so I may be biased :). BTW I since took the time to read all of your answer, and I think it is a nice one.
    – user4686
    Sep 7 '18 at 7:31
  • @moewe, not only did this solve the MWE I posed, it satisfies the additional condition that I need in real life: compatibility with hyperref. @Herbert's solution using \ifx also has that property. However, since I'm not a TeXtician, the syntax of \ifstrequal suits my way of thinking better than the more-TeXish \ifx, giving yours my nod. Sep 7 '18 at 21:30
4

The command \ifthenelse is fragile. What's a good definition of the term? A command is fragile when it produces uncanny error messages when used in a moving argument. Moving arguments are:

  1. the mandatory argument to a title (\chapter, \section and similar) or to \caption, unless the optional argument is used;
  2. the optional argument to a title or to a \caption, if used.

The standard method for coping with fragile commands in moving arguments is to prefix them with \protect.

A user defined command can be made robust by defining it with \DeclareRobustCommand.

\documentclass{article}
\usepackage{ifthen}

\newcommand{\mytest}[1]{%
    \ifthenelse{\equal{#1}{a}}{yes}{%
    \ifthenelse{\equal{#1}{b}}{no}{%
}}}
\DeclareRobustCommand{\myrobusttest}[1]{%
    \ifthenelse{\equal{#1}{a}}{yes}{%
    \ifthenelse{\equal{#1}{b}}{no}{%
}}}

\begin{document}

Troublesome construction: \mytest{a} and \mytest{b}.

Troublesome construction: \myrobusttest{a} and \myrobusttest{b}.

\section{Troublesome construction: \protect\mytest{a} and \protect\mytest{b}.}

\section{Troublesome construction: \myrobusttest{a} and \myrobusttest{b}.}

\end{document}

For hyperref, you can use \protected\def instead:

\documentclass{article}
\usepackage{ifthen}
\usepackage{hyperref}

\protected\def\mytest#1{%
    \ifthenelse{\equal{#1}{a}}{yes}{%
    \ifthenelse{\equal{#1}{b}}{no}{%
}}}

\begin{document}

Troublesome construction: \mytest{a} and \mytest{b}.

\section{Troublesome construction: \mytest{a} and \mytest{b}.}

\end{document}

If the argument to \mytest is expected to be a string (no special character or command), the following will work also with hyperref:

\documentclass{article}
\usepackage{xparse}
\usepackage{hyperref}

\ExplSyntaxOn
\NewExpandableDocumentCommand{\mytest}{m}
 {
  \str_case:nn { #1 }
   {
    {a}{yes}
    {b}{no}
   }
 }
\ExplSyntaxOff

\begin{document}

Troublesome construction: \mytest{a} and \mytest{b}.

\section{Troublesome construction: \mytest{a} and \mytest{b}.}

\end{document}

The advantage is that you get no warning at all from hyperref about Token not allowed and that the list of cases is easily extended to three or more.

With a further level of abstraction, you can define as many similar tests as you want.

\documentclass{article}
\usepackage{xparse}
\usepackage{hyperref}

\ExplSyntaxOn
\NewDocumentCommand{\definetest}{mm}
 {
  \NewExpandableDocumentCommand{#1}{m}
   {
    \str_case:nn { ##1 } { #2 }
   }
 }
\ExplSyntaxOff

\definetest{\mytest}
 {
  {a}{yes}
  {b}{no}
 }

\begin{document}

Troublesome construction: \mytest{a} and \mytest{b}.

\section{Troublesome construction: \mytest{a} and \mytest{b}.}

\end{document}
3
  • this did indeed solve the MWE I posed. What I didn't realize when posing my MWE, was that it was going to matter that in my real-life situation I also use hyperref. Even after \DeclareRobustCommand, and adding hyperref, I continued to get a fatal "Undefined control sequence. <argument> \equal {a}{1} l.8 ... is a section heading with \jdrcircled{a}.}" It turns out that the other solutions, with \ifx and \ifstrequal also fix the hyperref problem. Sep 7 '18 at 21:25
  • 1
    @JimRatliff I added a couple of different versions.
    – egreg
    Sep 7 '18 at 21:41
  • the fact that your solution doesn't trigger warning from hyperref about Token not allowed is HUGE! (I'm avoiding LaTeX3 for the time being because it's not compatible with Overleaf v1 (but is with v2), and it will be a while before the migration of that user base is complete.) But when it is, I'll definitely re-write with your solution. Sep 7 '18 at 21:49
2

With a simple \ifx:

\documentclass{article}
\newcommand\mytest[1]{%
    \ifx#1a\relax yes\else\ifx#1b\relax no\fi\fi}
\begin{document}
Troublesome construction: \mytest{a}.
\section{Troublesome construction: \mytest{a}.}

Troublesome construction: \mytest{b}.
\section{Troublesome construction: \mytest{b}.}

Troublesome construction: \mytest{c}.
\section{Troublesome construction: \mytest{c}.}
\end{document}

enter image description here

If more than one letter is possible, but only the first one is important for the if clause:

\newcommand\mytest[1]{\myTEST#1!}
\def\myTEST#1#2!{%
    \ifx#1a yes\else\ifx#1b no\fi\fi}

And if a whole word should be tested then use:

\def\Bee{b}\def\Aih{a}%% Change to your "words"
\newcommand\mytest[1]{%
  \def\TEMP{#1}%
  \ifx\TEMP\Aih yes\else\ifx\TEMP\Bee no\fi\fi}
1
  • not only did this solve the MWE I posed, it satisfies the additional condition that I need in real life: compatibility with hyperref. I'm not a TeXtician, so I follow the syntax of \ifstrequal better, and make fewer mistakes with it, than the more-TeXish \ifx, but I appreciate your solution. Sep 7 '18 at 21:32

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.