2

In the following minimal example, I am able to define a custom \if* command based on ifcsname, however when I use it inside \loop construct I get Extra \fi errors

\documentclass{article}

\makeatletter
\def\defMyCmd#1{%
  \expandafter\def\csname @@included@entry@#1 \endcsname{Defined}}
\def\ifMyCmdDefined#1{%
    \ifcsname @@included@entry@#1 \endcsname%
}
\makeatother

\begin{document}

\newcount\X
\X=10

\defMyCmd{temp}

% Works
\ifMyCmdDefined{temp} Hello\else Goodbye\fi{} World

\loop\unless\ifnum \X<0
  \advance\X by -1
  % Works
  \ifcsname @@included@entry@temp \endcsname\else Goodbye\fi{} World
  %
  % Failes with Extra \fi.
  \ifMyCmdDefined{temp} Hello\else Goodbye\fi{} World
\repeat
\end{document}

I am not sure what the reason is, or how to fix it. Any ideas?

2
  • 1
    Speaking of which, this is precisely what the \ifcondition command in LuaTeX is designed for.
    – user202729
    Commented May 7 at 1:35
  • Pretty much a duplicate of link"nested \ifx with extra \else error" Commented May 7 at 5:15

3 Answers 3

5

With your code \ifMyCmdDefined is a macro, not one of the \if../\else/\fi-primitives. Thus proper \if..-\else-\fi-matching is not ensured for cases where \ifMyCmdDefined itself is not expanded.

Instead of a macro \ifMyCmdDefined you can combine the usage of the \if-primitive with the usage of a macro \MyCmdDefined so that proper \if..-\else-\fi-matching is ensured also for cases where \if\MyCmdDefined itself is not expanded:

\documentclass{article}

\makeatletter
\def\defMyCmd#1{%
  \expandafter\def\csname @@included@entry@#1 \endcsname{Defined}%
}
\def\MyCmdDefined#1{%
  a\ifcsname @@included@entry@#1 \endcsname\expandafter a\else\expandafter b\fi
}
\makeatother

\begin{document}

\newcount\X
\X=10

\defMyCmd{temp}

% Works
\if\MyCmdDefined{temp}Hello \else Goodbye \fi World

% The following loop does 11 iterations as starting with \X=10 it does 
% things (decrementing and other stuff) per iteration while not/until
% \X < 0.
% I.e., it does these things while the value of \X is in the interval
% [0, 10] while that interval contains 11 integer numbers.

\loop\unless\ifnum \X<0 %
  \advance\X by -1 %
  % Works
  \ifcsname @@included@entry@temp \endcsname Hello \else Goodbye \fi World
  %
  % Works
  \if\MyCmdDefined{temp}Hello \else Goodbye \fi World
\repeat

\end{document}

However, "hiding" the entire \if..-\else-\fi-expression within a macro whose expansion yields
\if..\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi{⟨true⟩}{⟨false⟩}
, as suggested in the answer of David Carlisle, is definitely the better approach in situations where things can be processed as macro arguments.

5

It is never a good idea to define a syntax such as

\ifMyCmdDefined{temp} Hello\else Goodbye\fi

As it has an unmatched \fi.

Consider

\iffalse
  \ifMyCmdDefined{temp} Hello\else Goodbye\fi
\fi

Where you might hope that the whole block is skipped, but \ifMyCmdDefined is just a normal macro so while TeX starts skipping text after the \iffalse if skips over that and stops skipping at the \fi so the above indentation is misleading, Tex sees it as

\iffalse
  \ifMyCmdDefined{temp} Hello\else Goodbye
\fi
\fi

and it will give an error at the unmatched \fi.

Define the command such that the whole primitive if is skipped or not skipped:

\documentclass{article}

\makeatletter
\def\defMyCmd#1{%
  \expandafter\def\csname @@included@entry@#1 \endcsname{Defined}}
\def\ifMyCmdDefined#1{%
    \ifcsname @@included@entry@#1 \endcsname
      \expandafter\@firstoftwo
    \else
      \expandafter\@secondoftwo
     \fi
}
\makeatother

\begin{document}

\newcount\X
\X=10

\defMyCmd{temp}

% Works
\ifMyCmdDefined{temp}{Hello}{Goodbye} World

\loop\unless\ifnum \X<0
  \advance\X by -1
  % Works
  \ifcsname @@included@entry@temp \endcsname\else Goodbye\fi{} World
  %
   \ifMyCmdDefined{temp}{Hello}{Goodbye} World
\repeat
\end{document}
1
  • Thanks for the explanation on where my defined command fails. It all makes sense now.
    – Tohiko
    Commented May 7 at 8:23
2

Don't use a conditional!

\documentclass{article}

\makeatletter
\newcommand{\defMyCmd}[1]{%
  \@namedef{@@included@entry@#1}{Defined}%
}
\newcommand{\MyCmdDefinedTF}[3]{%
  \@ifundefined{@@included@entry@#1}{#3}{#2}%
}
\makeatother

\begin{document}

\newcount\X \X=10

\defMyCmd{temp}

\loop\unless\ifnum \X<0
  \advance\X by -1
  $\the\X$: \MyCmdDefinedTF{temp}{Hello}{Goodbye} World
\repeat

\end{document}

enter image description here

You might prefer

\newcommand{\MyCmdDefinedTF}[3]{%
  \ifcsname @@included@entry@#1\endcsname
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi{#2}{#3}%
}

Take your pick.

I'd avoid the space you use before \endcsname.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .