Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Using the .code handler to my keys and a private macro, I can easily test whether a key has been set when using pgfkeys. For example:

\documentclass{article}
\newcounter{mytestenvcounter}
\usepackage{pgfkeys}
\makeatletter
\let\ae@mykey@test\relax
\def\ae@get@mykey@test{\ifx\ae@mykey@test\relax{I've not been set.}\else{I am set to:\ae@mykey@test}\fi}
\pgfkeys
  {
    /ae/mykeys/set/.cd,
    mykey/.code = {\def\ae@mykey@test{#1}},
    /ae/mykeys/get/.cd,
    mykey/.code = {\ae@get@mykey@test},
  }
\makeatother
\newenvironment{mytestenv}[1]
  {\pgfkeys{ /ae/mykeys/set/.cd, #1}%
   \begin{minipage}[t]{2in}%
   \stepcounter{mytestenvcounter}\themytestenvcounter.)\hspace*{0.5em}
   \pgfkeys{ /ae/mykeys/get/mykey }%
  }
  {\end{minipage}%
  }

\pagestyle{empty}
\begin{document}

  \begin{mytestenv}{mykey={testing}}
  \end{mytestenv}

  \begin{mytestenv}{}
  \end{mytestenv}

\end{document}

But in the comments to an earlier question of mine it was suggested that there's a better approach via .initial and testing for an empty value. But, I don't know how to implement this.

This is as far as I know how to get:

\documentclass{article}
\newcounter{mytestenvcounter}
\usepackage{pgfkeys}
\pgfkeys
  {
    /ae/mykeys/.cd,
    mykey/.initial=,
  }
\newenvironment{mytestenv}[1]
  {\pgfkeys{ /ae/mykeys/.cd, #1}%
   \begin{minipage}[t]{2in}%
   \stepcounter{mytestenvcounter}\themytestenvcounter.)\hspace*{0.5em}
   \pgfkeys{ /ae/mykeys/mykey }%
  }
  {\end{minipage}%
  }

\pagestyle{empty}
\begin{document}

  \begin{mytestenv}{mykey={testing}}
  \end{mytestenv}

  \begin{mytestenv}{}
  \end{mytestenv}

\end{document}

I've tried using \pgfkeysifdefined, but that always seems to test positve.

I've also seen the following question and answer, but don't understand how it relates to what I'm trying to do. Additionally, in this particular question and answer, I don't understand why the need to \detokenize and the syntax for the definition of \pgfkeysifstyleempty@i seems incorrect. So, if you're tempted to point me in that direction, I'd greatly appreaciate comments on what's happening there.

But, is it really necessary to define a macro to test for empty values in pgfkeys? To be able to test whether a key has been set or not in the current environment seems to be something rather reasonable. I've poured over the chapter on pgfkeys in the tikz manual. But nothing there seems very promising, other than what I've already mentioned above.

share|improve this question
    
I think this question is related: How to detect if option provided via \tikzset –  Qrrbrbirlbel Jul 23 '13 at 0:10
    
I don't think that anymore. Instead of \relax (which might not be the ideal macro to check against as we have learned) we can simply use \pgfkeys@notset we can check against. Best would be to create our own handlers for that and an own \ifpgfkeyssetbyuser for a better interface. –  Qrrbrbirlbel Jul 23 '13 at 1:34
    
@Qrrbrbirlbel why did you remove your solution? –  A.Ellett Jul 23 '13 at 3:05
    
The update I just edited in was wrong. Our solution do basically the same. The pgfkeys package uses a similar approach for testing for “no value given” (i.e. using a key without a =). I included a special version of the -NoValue- text from your xparse solution. One can still do /.expand once=\pgfkeys@notset but if a user uses this they should know what they do (or simply use .unset). –  Qrrbrbirlbel Jul 23 '13 at 5:13
add comment

4 Answers

up vote 4 down vote accepted

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

share|improve this answer
add comment

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

share|improve this answer
add comment

Considering the discussion with @Qrrbrbirlbel and how xparse handles undefined values, this is a solution I came up with.

\documentclass{article}
\newcounter{mytestenvcounter}
\usepackage{pgfkeys}

\makeatletter
%%----------
\begingroup
  \lccode`\Q=`\-
  \lccode`\N=`\N
  \lccode`\V=`\V
  \lowercase{\endgroup
    \def\ae@unset@key@value{QNoValue-}}
%%----------
\def\ae@set@keys{\pgfqkeys{/ae/my/test/keys}}
\def\ae@test@getvalue#1{\pgfkeysgetvalue{/ae/my/test/keys/#1}}
\ae@set@keys
  {%
    mytestkey/.initial/.expand once=\ae@unset@key@value,
  }
\newenvironment{mytestenv}[1]
  {\ae@set@keys{#1}%
   \begin{minipage}[t]{2in}%
   \stepcounter{mytestenvcounter}\themytestenvcounter.)\hspace*{0.5em}
   \ae@test@getvalue{mytestkey}\@mytestkey@value
   \ifx\@mytestkey@value\ae@unset@key@value
      {I've not been set:}\else
      {I have been set:}\fi\@mytestkey@value
  }
  {\end{minipage}%
  }
\makeatother
\pagestyle{empty}

\begin{document}

  \begin{mytestenv}{}
  \end{mytestenv}

  \begin{mytestenv}{mytestkey={testing}}
  \end{mytestenv}

  \begin{mytestenv}{mytestkey={-NoValue-}}
  \end{mytestenv}

  \begin{mytestenv}{mytestkey={QNoValue-}}
  \end{mytestenv}

  \begin{mytestenv}{}
  \end{mytestenv}

\end{document}

The idea here is to use \lccode values to create a value that is extremely unlikely to be passed to the key (unless someone else is also doing \lccode magic while using this environment).

enter image description here

share|improve this answer
    
The edit you made makes the same mistake as my previous version. It expands too much. Try: \begin{mytestenv}{mytestkey={aa}}\end{mytestenv} –  Qrrbrbirlbel Jul 23 '13 at 4:36
1  
@Qrrbrbirlbel This is giving me such a headache. :) –  A.Ellett Jul 23 '13 at 4:39
    
And by the way, the previous version could be easily tricked with \makeatletter\begin{mytestenv}{mytestkey=\ae@unset@key@value}\end{mytestenv}\ma‌​keatother. You actually need to protect \ae@unset@key@value and not the content of it (which is irrelevant). –  Qrrbrbirlbel Jul 23 '13 at 4:47
    
@Qrrbrbirlbel That's what I had noticed and was trying to circumvent. I realized that all my lccodes were completely irrelevant to what I was testing. –  A.Ellett Jul 23 '13 at 4:49
    
At this point, I believe my solution is as fixed as it can be. Even with xparse, if you really want you can trick \IfNoValueTF into believing you've passed nothing by a similar trick of changing \lccode and a appropriate peppering of \expandafters. –  A.Ellett Jul 23 '13 at 6:05
add comment

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

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.