9

I am trying to test an if-condition, where the name of the condition depends on a macro argument. To do this, I use \csname ifcondition#1 \endcsname. This works fine in a simple macro like this:

\newif\ifconditionA
\conditionAtrue
\def\test#1{
    \csname ifcondition#1\endcsname
        A: True
    \fi
}
\test{A}

However, in my real use case, this test is part of an \ifcase branch, like so:

\documentclass{article}
\begin{document}
\newif\ifconditionA
\conditionAtrue

\def\test#1#2{
\ifcase#2
    Case 0
\or
    %\ifconditionA % This works
    \csname ifcondition#1\endcsname
        Case 1, A: true
    \fi
\fi
}

\test{A}{0}
%\test{A}{1} % This works

\end{document}

This fails with the error message ! Extra \fi. \test ...tion#1\endcsname Case 1, A: true \fi \fi.

If I use the name of the condition directly, without \csname, it works fine, and it also works if the branch containing the condition is executed.

What's going on, and how do I fix this?

8

The restriction is that the conditions must match, otherwise the skipping of unused branches does not work as expected.

There are several methods. The following exampe uses \ifx and compares the constructed conditional with \iftrue. Because we have already \ifx, the latter is also masked by \csname:

\documentclass{article}
\begin{document}
\newif\ifconditionA
\conditionAtrue

\def\test#1#2{%
  \ifcase#2
    Case 0
  \or
    \expandafter
    \ifx\csname ifcondition#1\expandafter\endcsname
        \csname iftrue\endcsname
      Case 1, A: true
    \fi
  \fi
}

\test{A}{0}
\test{A}{1}

\end{document}

If you want to have something clearer to use inside macro \test, then the magic can be hidden in a macro, e.g.:

\documentclass{article}
\begin{document}
\newif\ifconditionA
\conditionAtrue

\newcommand*{\getcondition}[1]{%
  T\expandafter
  \ifx\csname ifcondition#1\endcsname\iftrue
    \expandafter T%
  \else
    \expandafter F%
  \fi
}

\def\test#1#2{%
  \ifcase#2
    Case 0%
  \or
    \if\getcondition{#1}%
      Case 1, #1: true%
    \else
      Case 1, #1: false%
    \fi
  \else
    Other case%
  \fi
}

[\test{A}{0}]
[\test{A}{1}]
[\test{B}{0}]
[\test{B}{1}]
[\test{A}{2}]

\end{document}

Result

2
  • Ah, excellent, thank you! It seems like using \iftrue directly (without the \csname) also works?
    – Jake
    Jul 16 '13 at 15:03
  • @Jake: No, it will not work, if the \or branch is skipped. TeX would see two \if... commands and would steal the \fi of \ifcase to match them. Jul 16 '13 at 15:05
7

You can use the fact that \if expands tokens until finding unexpandable ones:

\documentclass{article}
\begin{document}
\newif\ifconditionA
\conditionAtrue

\def\testcondition#1{\expandafter\TTfi\csname ifcondition#1\endcsname}
\def\TTfi{TT\fi}

\def\test#1#2{%
  \ifcase#2
    Case 0
  \or
    \if\testcondition{#1}%
      Case 1, A: true
    \fi
  \fi
}

\test{A}{0}
\test{A}{1}

\end{document}

Let's see how it works. If we are in a skipped over text, that is, the false branch of a conditional or an \or branch to be ignored, TeX will match \if with the \fi (and the possible \else) that really corresponds to \ifconditionA. If it's in a true branch, then the expansions are

\if\testcondition{A}
\if\expandafter\TTfi\csname ifconditionA\endcsname
\if\TTfi\ifconditionA
\if TT\fi\ifconditionA
\ifconditionA

so the \if is matched to the first \fi and the entire group \if TT\fi disappears without doing nothing. Then the constructed \ifconditionA is matched to its \else and \fi.

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.