Command \ifcsname not expanding correctly when used in macro

In your example

\customif{other_variable}
    \customif{other_variable}
        This fails.
    \fi
\fi

the inner \customif is not expanded, so the \ifcsname in its replacement text is not seen. So the first \fi is paired with the \ifcsname resulting from the expansion of the outer \customif and the next one is dangling alone.

More detailed: the code, after expansion of \customif{other_variable} becomes

\ifcsname other_variable\endcsname
    \customif{other_variable}
        This fails.
    \fi
\fi

The test returns false, so everything up to the first \fi is skipped over.

You can do it in a different way:

\def\custom#1{TT\fi\ifcsname #1\endcsname}

and then call it as

\if\custom{some_variable}
  <true text>
\else
  <false text>
\fi

which will work also nested because of the \if. (Don't forget to protect the line endings when necessary.)


If you're worried about a dangling initial \fi (that will however disappear immediately upon expansion) you can do

\def\custom#1{T\expandafter X\expandafter X\fi\ifcsname #1\endcsname}

A different approach is to use “braced branches”:

\makeatletter
\newcommand*\vardefTF[1]{%
  \ifcsname #1\endcsname
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}
\makeatother

to be called like

\vardefTF{some_var}{True}{False}

This has the disadvantage that the true and false branches are read in as arguments, but has less problems when nested.

With expl3 this is predefined, so

\usepackage{expl3}

\ExplSyntaxOn
\cs_set_eq:NN \vardefTF \cs_if_exist:cTF
\ExplSyntaxOff

would provide \vardefTF behaving like before.


A different idea, suggested by Bruno Le Floch:

\def\custom#1{%
  T\ifcsname#1\endcsname
  \expandafter T\else\expandafter F\fi
}

If the code

\if\custom{some_variable}<True>\else<False>\fi

is inside a skipped conditional branch, the \if will be balanced by \else (optional as usual) and \fi. On the other hand, when \if is not skipped and expanded, when \ifcsname returns true, \if will find TT, otherwise it will find TF, choosing accordingly the following branches.

This has the advantage that one can prefix \if with \unless.

(The old TT\fi trick was born before \unless became available.)


I am using different approach than TT for such situations. I define \is... macro which realizes the test and it has one more parameter than is really needed. This (ignored) parameter is \iftrue when the macro is used. Our example looks like:

\def\isdefined#1#2{\ifcsname #1\endcsname}

and usage:

\isdefined{some_text}\iftrue
   <true text>
\else
   <false text>
\fi

Note that you need not to put % at the end of the first line.


Another possibility is to use a LaTeX-style conditional. For example, in the code below we define a command \CustomIfDef, with should be used as follows

\CustomIfDef{<cs-name>}{<if-defined-code>}{<if-undefined-code>}

and can be nested without problems. Note, however, that in case of nesting it is less efficient than @egreg approach, because the code for the various cases needs to be copied between the levels as an argument.

% My standard header for TeX.SX answers:
\documentclass[a4paper]{article} % To avoid confusion, let us explicitly 
                                 % declare the paper format.

\usepackage[T1]{fontenc}         % Not always necessary, but recommended.
% End of standard header.  What follows pertains to the problem at hand.

\makeatletter

\newcommand*\CustomIfDef[1]{%
    \ifcsname #1\endcsname
        \expandafter\@firstoftwo
    \else
        \expandafter\@secondoftwo
    \fi
}
\newcommand*\CustomDefEmpty[1]{\@namedef{#1}{}}

\makeatother

\nofiles



\begin{document}

\CustomDefEmpty{Foo_bar_123.$}

\CustomIfDef{Pluto!}{
    \typeout{==> Outer branch true.}
}{
    \typeout{==> Outer branch false.}
    \CustomIfDef{Foo_bar_123.$}{
        \typeout{----> Mid-level branch true.}
        \CustomIfDef{relax}{
            \typeout{......> Inner branch: \relax is defined.}
        }{
            \typeout{......> Inner branch: \relax is NOT defined.}
        }
    }{
        \typeout{----> Mid-level branch false.}
    }
}

\end{document}