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.
}%
}%
}%
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}
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}