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

enter image description here


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}

enter image description here


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}

Output

Output