5

I'm struggling (again) with expansions. This time I have a generic function \misdirection that creates a TikZ environment subject to a few options. Then, I create a macro \freezeStyle that creates a new macro that has frozen some of the options to \misdirection. So \freezeStyle receives the style that I ultimately want passed to \misdirection.

I tried a few \edefs and couldn't get it working, so I opted to put the code in a state that demonstrates something that compiles but has undesirable behavior. This attempt was inspired by How to submit a set of tikz command with pgfkeys?.

So: How should I modify this so that it works as desired? Please note that my actual misdirection call will involve \csname, so I'd appreciate expandafter solutions that also show how to adapt your solution to that case as well (see code for details).

For cross-references, here is a similar question: Macros that write macros and unexpected side effects, with the same type of undesirable behavior, but the solution there has to do with defining customized \csnames, which doesn't apply to this problem.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}

\newcommand{\misdirection}[2][]{
\begin{tikzpicture}
    \node (tree) at (0,0) {tree};
    \node (apple) at (3,0) {#2};
    \draw[blue,#1] (tree) -- (apple);
\end{tikzpicture}
}

\pgfkeys{
    /tikz/.cd,
        execute style/.style = {#1},
        execute macro/.style = {execute style/.expand once=#1},
    /demo/.cd,
        name/.store in=\savedname,
        style/.store in=\savedstyle
}

\newcommand{\freezeStyle}[1][]{%
    \pgfqkeys{/demo}{#1}
    \expandafter\def\csname\savedname\endcsname##1{%
        % Incorrect because \savedstyle is not expanded until runtime.
        % I want the value of \savedstyle hardcoded into this macro.
        \misdirection[execute macro = \savedstyle]{##1}

        % This doesn't work either...
        %\expandafter\misdirection\expandafter[\savedstyle]{##1}

        % In my real code, the misdirection call looks more like:
        %\csname someArgumentDependentStuff \endcsname[put style here]{args}
    }
}

\begin{document}
    \freezeStyle[name=helloMom, style={red,thick}]
    \freezeStyle[name=helloDad, style={-{>[length=5mm]},green}]
    \helloMom{4} % Undesirably, this is green with a big arrow.
    \helloDad{5}
\end{document}
  • Would you consider an expl3 answer for comparison? – Sean Allred Oct 22 '14 at 2:40
  • Sure, though I can't say I'll understand it until I dig into expl3 a bit. – Tom Oct 22 '14 at 2:41
  • I added one with comments about what's going on. It seems a bit verbose for this use, but it's a much more consistent syntax when you're dealing with more complicated cases. – Sean Allred Oct 22 '14 at 4:19
5

I'd just use a "helper" macro so the \savedstyle can be expanded (once):

\documentclass[varwidth,border=5]{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}

\newcommand{\misdirection}[2][]{%
  \begin{tikzpicture}
    \node (tree) at (0,0) {tree};
    \node (apple) at (3,0) {#2};
    \draw [blue,#1] (tree) -- (apple);
  \end{tikzpicture}\ignorespaces% or \par?
}

\pgfkeys{demo/.cd,
  name/.store in=\savedname,
  style/.store in=\savedstyle
}

\newcommand\freezeStyle[1][]{%
  \pgfqkeys{/demo}{#1}%
  \expandafter\FreezeStyle\expandafter{\savedstyle}{\savedname}}

\def\FreezeStyle#1#2{%
  \expandafter\def\csname#2\endcsname##1{%
     \misdirection[#1]{##1}}}

\begin{document}
    \freezeStyle[name=helloMom, style={red,thick}]
    \freezeStyle[name=helloDad, style={-{>[length=5mm]},green}]
    \helloMom{4} 

    \helloDad{5}
\end{document}

Note, I added an \ignorespaces in the the \misdirection command.

enter image description here

  • I ended up using this one! But the other solutions are very nice as well! – Tom Oct 22 '14 at 19:08
4

With expl3, just for fun :)

\documentclass{article}
\usepackage{xparse} % automatically loads expl3
\usepackage{tikz}
\usetikzlibrary{arrows.meta}

\newcommand{\misdirection}[2][]{%
  \begin{tikzpicture}
    \node (tree) at (0,0) {tree};
    \node (apple) at (3,0) {#2};
    \draw[blue,#1] (tree) -- (apple);
  \end{tikzpicture}}

\ExplSyntaxOn

% Declare our variables; it's good practice :)
\tl_new:N \l_demo_name_tl
\tl_new:N \l_demo_style_tl

% Set up our keys (`tl` is short for `token list`)
\keys_define:nn { demo } {
  name  .tl_set:N = \l_demo_name_tl,
  style .tl_set:N = \l_demo_style_tl,
}

% Create a macro to behave as if we would call
% \demo_freeze_style:Nn \helloMom { red, thick }...
\cs_new:Nn \demo_freeze_style:Nn {
  \cs_new:Npn #1 ##1 {
    \misdirection[#2]{##1}
  }
}
% ...but then add support for saner syntax in our use case
\cs_generate_variant:Nn \demo_freeze_style:Nn { cV }

% Now, set our keys and use our command :)
\NewDocumentCommand \freezeStyle { O{} } {
  \group_begin:
  \keys_set:nn { demo } { #1 }
  \demo_freeze_style:cV { \tl_use:N \l_demo_name_tl } \l_demo_style_tl
  \group_end:
}

\ExplSyntaxOff


\begin{document}
    \freezeStyle[name=helloMom, style={red,thick}]
    \freezeStyle[name=helloDad, style={-{>[length=5mm]},green}]
    \helloMom{4}
    \helloDad{5}
\end{document}

output

  • I'm in doubt myself here: I would \demo_freeze_style:Nn and then generate the :cn and :cV variants; in case you think the first argument shouldn't be c but n (as it is in your code), there is no need to :VV, only :nV. Or am I wrong? I mean, would there be any problem with using \l_demo_name_tl inside a \csname …\endcsname? Altough I see the point of using V. – Manuel Oct 22 '14 at 8:14
  • @Manuel I would say you're spot on with making the prototype :Nn (since in the same vein as \tl_const:Nn). I'm now a bit on the fence myself about using a V type argument here as well; this isn't really seen in the existing expl3 interfaces. No, there wouldn't be any problem at all—just more code (technically, you should wrap it with \tl_use:N). – Sean Allred Oct 22 '14 at 12:32
  • @Manuel It turns out that, when used correctly, you can't go from an N-type to a V-type argument. While the syntax was nice, it was semantically incorrect. I've fixed that in an edit :) Thanks! – Sean Allred Oct 22 '14 at 12:41
1

Here's how to do it without expl3:

\newcommand\freezeStyle[1][]{%
  \pgfqkeys{/demo}{#1}%
  \expandafter\edef\csname\savedname\endcsname##1{%
    \noexpand\misdirection[\savedstyle]{##1}}}

Note that \edef is used here and \noexpand is used to protect macros that should not be expanded.

\typeout{\meaning\helloMom}
macro:#1->\misdirection [execute macro=red,thick]{#1}

output

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}

\pgfkeys{
    /demo/.cd,
        name/.store in=\savedname,
        style/.store in=\savedstyle
}

\newcommand{\misdirection}[2][]{%
  \begin{tikzpicture}
    \node (tree) at (0,0) {tree};
    \node (apple) at (3,0) {#2};
    \draw[blue,#1] (tree) -- (apple);
  \end{tikzpicture}}

\newcommand\freezeStyle[1][]{%
  \pgfqkeys{/demo}{#1}%
  \expandafter\edef\csname\savedname\endcsname##1{%
    \noexpand\misdirection[\savedstyle]{##1}}}

\begin{document}
    \freezeStyle[name=helloMom, style={red,thick}]
    \freezeStyle[name=helloDad, style={-{>[length=5mm]},green}]
    \helloMom{4}
    \helloDad{5}
\end{document}
  • Aha! Thanks. So if I'm understanding this now, I shouldn't even need to have execute macro= in there. Just: \noexpand\misdirection[\savedstyle]{##1}}} – Tom Oct 22 '14 at 3:14
  • @Tom Whoops, yes, you're right :) – Sean Allred Oct 22 '14 at 3:18

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.