How do you test whether a known key value has been set in pgfkeys
The following code provides the two handlers
.initial without value
and.unset
.
Both set the key to the content of the \pgfkeys@notset
macro which raises an error messge (or worse) if it is used anyway. (The \pgfkeysvalueof
macro doesn’t check for a defined key.)
The .initial without value
handler, much like the .initial
handler, simply overwrites the key whereas the .unset
key checks if the key is actual defined (more a handler for an user which should be able to “unset” keys).
Code
\documentclass{article}
\usepackage{pgfkeys}
\makeatletter
\def\pgfkeys@@notset{\PackageError{pgfkeys}
{The \pgfkeyscurrentpath\space key has not been set to a value.}{}}
\begingroup
\lccode`\Q=`\-
\lccode`\N=`\N
\lccode`\V=`\V
\lowercase{\endgroup
\def\pgfkeys@notset{QNoValue-\pgfkeys@@notset}}
\def\pgfkeys@firstoftwo#1#2{#1}
\def\pgfkeys@secondoftwo#1#2{#2}
\pgfqkeys{/handlers}{%
.initial without value/.code/.expand once=%
\expandafter\pgfkeyssetvalue\expandafter{\expandafter\pgfkeyscurrentpath\expandafter}\expandafter{\pgfkeys@notset},%
.unset/.code=%
\pgfkeysifdefined{\pgfkeyscurrentpath}
{\pgfkeys{\pgfkeyscurrentpath/.initial without value}}
{\PackageError{pgfkeys}
{The \pgfkeyscurrentpath\space key has not been initialized.}{}}%
}
\def\ifpgfkeyssetbyuser#1{%
\expandafter\ifx\csname pgfk@#1\endcsname\pgfkeys@notset
\expandafter\pgfkeys@secondoftwo
\else
\expandafter\pgfkeys@firstoftwo
\fi}
\makeatother
\def\testkey#1{%
\texttt{#1} is \ifpgfkeyssetbyuser{/#1}{set to ``\pgfkeysvalueof{/#1}''}{not set}.}
\begin{document}
% The \pgfkeyssetbyuser macro doesn't test
% whether the key it is actually defined, this results in "\relax"
\testkey{test} (\texttt{\char`\\relax})
\pgfkeys{test/.initial without value}
\testkey{test} % -> not set
\pgfkeys{test=1}
\testkey{test} % -> set to "1"
\pgfkeys{test=}
\testkey{test} % -> set to ""
\pgfkeys{test/.unset}
\testkey{test} % -> not set
\pgfkeys{test=-NoValue-}
\testkey{test}% -> set to "-NoValue-"
\pgfkeys{test/.unset}
\pgfkeysvalueof{/test} % -> "The /test key has not been set to a value."
% -> Output "-NoValue-"
\pgfkeys{testme/.unset}% -> "The /testme key has not been initialized."
\end{document}
Output
I think a slightly simpler approach is sufficient for this via using \empty
when it is not set and the value is in some macro when it is controlled by an \if
. However it doesn't have to be \empty
. Whatever the default value is you can test for it. It might not be a good idea to look for undefined macro for general error handling and debugging cases.
\documentclass{article}
\usepackage{pgfkeys}
\newcounter{mytestenvcounter}
\newif\ifaevalueisset
\pgfkeys{
/ae/mykeys/mykey/.code={\ifx#1\empty\else%
\aevalueissettrue
\edef\mytempval{\ignorespaces#1}% optional ignorespaces
\fi},
/ae/mykeys/mykey/.default=\empty,
}
\newenvironment{mytestenv}[1][]
{\pgfkeys{/ae/mykeys/.cd,#1}
\begin{minipage}[t]{2in}%
\stepcounter{mytestenvcounter}\themytestenvcounter.)\hspace*{0.5em}%
\ifaevalueisset\mytempval\aevalueissetfalse\else\fi
}
{\end{minipage}%
}
\begin{document}\pagestyle{empty}
\begin{mytestenv}[mykey={testing}]
\end{mytestenv}
\begin{mytestenv}[mykey]a
\end{mytestenv}
\begin{mytestenv}
\end{mytestenv}
\begin{mytestenv}[mykey=different combos]
\end{mytestenv}
\end{document}
I personally use the command \pgfkeysifdefined
to test if a key is defined. In order for it to work, you need to manually set the key with \pgfkeyssetvalue
, which can be done automatically thanks to the key handler .code
.
Be careful to add braces {
and }
around the macro to set the key locally as explained in this answer about pgfkeys scope.
Code
\documentclass[varwidth,margin=0.5cm]{standalone}
\usepackage{pgfkeys}
\pgfkeys{%
a/.code={\pgfkeyssetvalue{a}{#1}\pgfkeysgetvalue{a}{\a}},%
b/.code={\pgfkeyssetvalue{b}{#1}\pgfkeysgetvalue{b}{\b}}}
\newcommand{\isDefined}[1][]{{% two braces to set the key locally
\pgfkeys{#1}
#1:\\
\pgfkeysifdefined{a}{a is defined (a = \a)}{a is not defined}\\
\pgfkeysifdefined{b}{b is defined (b = \b)}{b is not defined}}}
\begin{document}
\isDefined[a=3]\smallskip\\
\isDefined[b=2]\smallskip\\
\isDefined[a]
\end{document}