2

I have this little example to check if a key is empty:

\documentclass[]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\makeatletter

\newcount\cnt\cnt=0\relax
\tikzset{dir/.is family}
\tikzset{dir/t0/.initial={}}

\begin{document}

% \tikzset{dir/t0={\small SMALL}}

Key \the\cnt is: \pgfkeysvalueof{/tikz/dir/t\the\cnt}

\edef\mykey{x\pgfkeysvalueof{/tikz/dir/t\the\cnt}}
\edef\plainx{x}
\ifx\plainx\mykey
    key t\the\cnt{} is empty
\else
    key t\the\cnt{} is not empty
\fi

\end{document}

and it works unless I put a font-changing command in the key itself (uncomment the tikzset):

! TeX capacity exceeded, sorry [input stack size=5000].
\@setfontsize #1#2#3->\@nomath #1
                                 \ifx \protect \@typeset@protect \let \@curr...
l.18 ...ykey{x\pgfkeysvalueof{/tikz/dir/t\the\cnt}
                                                  }
!  ==> Fatal error occurred, no output PDF file produced!

How can I check if the key is set to {} in a safe way? I need to do that using core packages, the code must be LaTeX and ConTeXt compatible...

  • 1
    \protected@edef instead of \edef should work... – Phelype Oleinik Dec 17 '19 at 12:39
  • @PhelypeOleinik yes, it works... if you write it in an answer I'll happily accept it! Is \protected@edef available in ConTeXt? – Rmano Dec 17 '19 at 12:57
  • Loading all of tikz to only use pgfkeys is a bit inefficient. – Henri Menke Dec 18 '19 at 8:10
  • @HenriMenke yes ;-) but this was a minimal example stripped down from the innards of circuitikz... – Rmano Dec 18 '19 at 8:53
  • 1
    @Dr.ManuelKuehner yes! Sorry, I forgot to mark it! – Rmano Mar 2 at 19:17
4

Font change commands are (now) robust, so if you use \protected@edef instead of plain \edef, then LaTeX's protection mechanism will kick in and prevent the expanson of \small. However this will only work if the key contains robust or expandable commands (which is most often not the case).

A more robust way of doing this exploits the implementation details of \pgfkeysvalueof, which is just \csname pgfk@<full path to key>\endcsname (after one expansion). The \csname expands whatever is in the path and makes a control sequence (after a second expansion), then one more expansion (third) yields the contents of the key, so you can use:

\def\unexpandedvalueof#1{%
  \unexpanded
    \expandafter\expandafter\expandafter\expandafter
    \expandafter\expandafter\expandafter{\pgfkeysvalueof{#1}}}

to expand \pgfkeysvalueof exactly 3 times and leave the result inside \unexpanded, so it doesn't expand further in \edef.

Note that, since \pgfkeysvalueof uses directly \csname, in case the key didn't exist previously, TeX will leave \relax after the expansion and the emptiness test will result false. If you want it to return true for an undefined key, then you need to check for the \relax before expanding the actual control sequence that stores the key:

\def\unexpandedvalueof#1{%
  \unexpanded\expandafter\expandafter
    \expandafter\rmano@valueof@chk\pgfkeysvalueof{#1}}
\def\rmano@valueof@chk#1{%
  \ifx\relax#1%
    \expandafter\pgfutil@firstoftwo
  \else
    \expandafter\pgfutil@secondoftwo
  \fi
    {{}}%
    {\expandafter{#1}}}

Here's a compilable example:

\documentclass[]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\usepackage{unravel}
\makeatletter

\newcount\cnt\cnt=0\relax
\tikzset{dir/.is family}
\tikzset{dir/t0/.initial={}}

\begin{document}

\tikzset{dir/t0={\small SMALL}}

Key \the\cnt is: \pgfkeysvalueof{/tikz/dir/t\the\cnt}

\makeatletter
\newcommand\unexpandedvalueof[1]{%
  \unexpanded\expandafter\expandafter
    \expandafter\rmano@valueof@chk\pgfkeysvalueof{#1}}
\def\rmano@valueof@chk#1{%
  \ifx\relax#1%
    \expandafter\pgfutil@firstoftwo
  \else
    \expandafter\pgfutil@secondoftwo
  \fi
    {{}}% #1 is \relax, so consider empty
    {\expandafter{#1}}}% otherwise, leave the key after one more expansion
\makeatletter

\edef\mykey{x\unexpandedvalueof{/tikz/dir/t\the\cnt}}
\edef\plainx{x}
\ifx\plainx\mykey
    key t\the\cnt{} is empty
\else
    key t\the\cnt{} is not empty
\fi

\edef\mykey{x\unexpandedvalueof{/tikz/dir/tBOO}}
\edef\plainx{x}
\ifx\plainx\mykey
    key t\the\cnt{} is empty
\else
    key t\the\cnt{} is not empty
\fi

\end{document}

ConTeXt renames the \unexpanded primitive, so you need to use \normalunexpanded instead. You can use this code to define a generic \pgfutil@unexpanded:

\ifcsname normalunexpanded\endcsname
  \let\pgfutil@unexpanded\normalunexpanded
\else
  \let\pgfutil@unexpanded\unexpanded
\fi
| improve this answer | |
  • Thanks! this is a couple (4!) of \expandafter over my skills... – Rmano Dec 17 '19 at 13:17
  • ...and I think that using \def instead of \newcommand makes it ConTeXt-compatible. Will check anyway – Rmano Dec 17 '19 at 13:20
  • 1
    @Rmano The rule is 2^n-1 \expandafter, where n is the number of expansions you want. Or you can use expl3 and this to give the exact number of expansions ;-) To make it work in ConTeXt you'd need to define \long\def\@firstoftwo#1#2{#1} and \long\def\@secondoftwo#1#2{#2} as well (edit: or use the pgfutil versions; I'll edit my answer) – Phelype Oleinik Dec 17 '19 at 13:29
  • 1
    @Rmano ConTeXt renames some primitives, including \unexpanded, so you need to check for that. I added a conditional which defines \pgfutil@unexpanded regardless of format (it works at least in ConTeXt, Plain, and LaTeX). That and \pgfutil@firstoftwo and \pgfutil@secondoftwo should be enough to make that code format-agnostic :-) – Phelype Oleinik Dec 17 '19 at 13:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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