Detect if a unit is defined with siunitx

The reason for this different behavior seem to be how siunitx handles new units that aren't called in the scope of one of the \si/\SI commands. If we make LaTeX \show the definitions immediately after the beginning of the document, we get

> \milliliter=\protected\long macro:
->\ERROR .
l.27 \show\milliliter

> \centiliter=undefined.
l.28 \show\centiliter

So \milliliter is defined to an undefined control sequence, while \centiliter is actually undefined, strangely. This additional layer of indirection isn't recognized by your test.

A simple solution is to call your test in the context of the \si command when all the new units are in scope:

\newcommand*{\TestIfUnitExists}[1]{%
    \par
    \si{%
        \ifcsdef{#1liter}{%
            #1liter exists.
        }{%
            #1liter does NOT exist.
        }%
    }%
}%

enter image description here


The defined units are tracked internally by siunitx in a sequence. this is not public but as your use case seems somewhat odd anyway I guess you might do

\ExplSyntaxOn
\NewDocumentCommand \TestIfUnitExists { m }
  {
    \seq_if_in:NxTF \l__siunitx_declare_list_seq { \exp_not:c { #1 liter } }
      { #1liter~exists. }
      { #1liter~does NOT exist. }
  }
\ExplSyntaxOff

This is how the test ought to be defined:

\documentclass{article}
\usepackage{siunitx}

\DeclareSIUnit\milliliter{\textnormal{mL}}

\AtBeginDocument{%
    \DeclareSIUnit\centiliter{\textnormal{cL}}%
}

\ExplSyntaxOn
\NewDocumentCommand{\testifunitexistsTF}{smmm}
 {
  \IfBooleanTF{#1}
   { \grill_test_siunit:cTF {#2}{#3}{#4} }
   { \grill_test_siunit:NTF {#2}{#3}{#4} }
 }
\cs_new_protected:Nn \grill_test_siunit:NTF
 {
  \seq_if_in:NnTF \l__siunitx_declare_list_seq { #1 } { #2 } { #3 }
 }
\cs_generate_variant:Nn \grill_test_siunit:NTF { c }

\ExplSyntaxOff

\newcommand{\test}[1]{%
  \testifunitexistsTF*{#1liter}{#1liter exists}{#1liter doesn't exist}%
}

\begin{document}

\section{General command}

\texttt{\string\millimeter} \testifunitexistsTF{\milliliter}{exists}{doesn't exist}

\texttt{\string\millimeter} \testifunitexistsTF*{milliliter}{exists}{doesn't exist}

\section{Test}

\test{giga}%

\test{milli}% <--- milliliter exists

\test{centi}% <--- centiliter also exists (but test fails)

\test{}%      <--- liter exists as well

\end{document}

enter image description here

Unfortunately, the sequence variable where all the units are stored is not public, so this cannot be really used. Please, file a feature request to Joseph Wright for making the list of units searchable.

Here's a different implementation, based on another answer, but not typesetting anything in the scope of \si. Differently from the previous solution, the macro \testifunitexists only accepts a name as argument, not a control sequence.

\documentclass{article}
\usepackage{etoolbox}
\usepackage{siunitx}

\DeclareSIUnit\milliliter{\textnormal{mL}}

\AtBeginDocument{%
  \DeclareSIUnit\centiliter{\textnormal{cL}}%
}

\newtoggle{unitexists}

\newcommand{\testifunitexistsTF}[1]{%
  \global\togglefalse{unitexists}%
  \sbox0{\si{%
    \ifcsdef{#1}{\global\toggletrue{unitexists}}{}%
  }}%
  \iftoggle{unitexists}%
}

\newcommand{\test}[1]{%
  \testifunitexistsTF{#1liter}{#1liter exists}{#1liter doesn't exist}%
}

\begin{document}

\section{General command}

\texttt{\string\millimeter} \testifunitexistsTF{milliliter}{exists}{doesn't exist}

\section{Test}

\test{giga}%

\test{milli}% <--- milliliter exists

\test{centi}% <--- centiliter also exists (but test fails)

\test{}%      <--- liter exists as well

\end{document}