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

6
  • 1
    \protected@edef instead of \edef should work... Dec 17, 2019 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, 2019 at 12:57
  • Loading all of tikz to only use pgfkeys is a bit inefficient. Dec 18, 2019 at 8:10
  • @HenriMenke yes ;-) but this was a minimal example stripped down from the innards of circuitikz...
    – Rmano
    Dec 18, 2019 at 8:53
  • 1
    @Dr.ManuelKuehner yes! Sorry, I forgot to mark it!
    – Rmano
    Mar 2, 2020 at 19:17

1 Answer 1

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
4
  • Thanks! this is a couple (4!) of \expandafter over my skills...
    – Rmano
    Dec 17, 2019 at 13:17
  • ...and I think that using \def instead of \newcommand makes it ConTeXt-compatible. Will check anyway
    – Rmano
    Dec 17, 2019 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) Dec 17, 2019 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 :-) Dec 17, 2019 at 13:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .