# Understanding etoolbox \ifdefempty and \ifdefvoid

I am trying to understand the etoolbox \ifdefempty and \ifdefvoid macros. The documentation says that \ifdefempty

Expands to true if the control sequence is defined and is a parameterless macro whose replacement text is empty, and to false otherwise.

Similarly \ifdefvoid

Expands to true if the control sequence is undefined, if is a macro whose meaning is \relax, or a parameterless macro whose replacement text is empty, and to false otherwise.

From these descriptions I thought that \def\myempty{}, \def\myrelax{\relax}, and \def\myemptyempty{\myempty} would all be both "empty" and "void", but \def\myempty{} is the only one that is either "empty" or "void". What am I missing?

\documentclass{minimal}
\usepackage{etoolbox}

\begin{document}

\def\myempty{}
\def\myrelax{\relax}
\def\myemptyempty{\myempty}

\ifdefempty{\myempty}{Empty}{Not empty}

\ifdefempty{\myrelax}{Empty}{Not empty}

\ifdefempty{\myemptyempty}{Empty}{Not empty}

\ifdefvoid{\myempty}{Void}{Not void}

\ifdefvoid{\myrelax}{Void}{Not void}

\ifdefvoid{\myemptyempty}{Void}{Not void}

\end{document}


Assuming \<cs> passes the first two tests of \ifdefempty (is defined and is parameterless), the next test - is the replacement text empty - is performed within \etb@ifdefempty. This test checks whether the \meaning of \<cs> is "blank":

\def\etb@ifdefempty#1{%
\expandafter\expandafter
\expandafter\ifblank
\expandafter\expandafter
\expandafter{%
\expandafter\strip@prefix\meaning#1}}


The "blank" here is checked against a \strip@prefix of the \meaning of #1. This just strips the macro:-> part from the output of \meaning (so macro:->\relax is changed to \relax). So, in the case of

• \myempty, the check is \ifblank{} which is true.
• \myrelax, the check is \ifblank{\relax} which is false.
• \myemptyempty, the check is \ifblank{\myempty}, which is false.

The following updated version of \ifdefempty actually shows you what the comparisons are performed against:

\makeatletter
\renewcommand{\ifdefempty}[1]{
\ifundef{#1}
{\@secondoftwo}
{\ifdefmacro{#1}
{\ifdefparam{#1}
{\@secondoftwo}
{\etb@ifdefempty{#1}}}
{\@secondoftwo}}}
\makeatother


The inner-workings of \ifdefvoid is similar since it uses the same inner-most function for testing, namely \etb@ifdefempty. The following updated version of \ifdefvoid highlights what the tests are performed against:

\makeatletter
\renewcommand{\ifdefvoid}[1]{%
\ifundef{#1}
{\@firstoftwo}
{\ifdefmacro{#1}
{\ifdefparam{#1}
{\@secondoftwo}
{\etb@ifdefempty{#1}}}
{\@secondoftwo}}}
\makeatother


With \def\myrelax{\relax} macro \myrelax is a parameterless macro with replacment text \relax. You get a command sequence with meaning \relax with \let:

\let\myrelax\relax


Also \def\myemptyempty{\myempty} defines a parameterless macro \myemptyempty that is not empty, whose replacement text is \myemptyempty. There is \edef that expands the replacement text first:

\edef\myemptyempty{\myempty}


Summary:

\ifdefempty catches the case:

• \myempty: \def\myempty{}

and \ifdefvoid the cases:

• \UnDeFiNeDCoMmAnD
• \myrelax: \let\myrelax\relax
• \myempty: \def\myempty{}

Rather than digging into the internals, let's just parse the documented behavior.

\ifdefempty: Expands to true if the control sequence is defined and is a parameterless macro whose replacement text is empty, and to false otherwise.

This means that \ifdefempty{\cs} tests whether \cs is a macro (i.e. a non-primitive control sequence that is expandable) taking no parameters, and that its first-level expansion is empty. That is, it should have been created via

\def\cs{}
\edef\cs{<stuff expanding to empty text>}
\let\cs<control sequence for which \ifdefempty{} is true>


\ifdefvoid: Expands to true if the control sequence is undefined, if is a macro whose meaning is \relax, or a parameterless macro whose replacement text is empty, and to false otherwise.

This extends the test of \ifdefempty to include control sequences that are undefined (as opposed to primitive and unexpandable, which are not void in the sense of this test), or equivalent to \relax. The use of the word "macro" in the latter connection is unfortunate, since they are not macros in the technical sense (not being expandable). Such a control sequence should have been created via:

<any of the above for \ifdefempty>
\let\cs\relax
\csname cs\endcsname %If \cs were previously undefined, this makes it \relax


or, of course, having never been created and thus remaining undefined.