2

I've been trying on and off for a few days to wrap my head around the following. I want to insert options defined in a macro alongside options statically defined into the option list of another macro. I seem to be able to do this provided that my macro is the first "option" in the macro list (similar to that seen in pass to \geometry{} options using a macro - keyval error); but if I try to pass it as the second option, it does not work. I'm confused as to why this is.

Example code that show what works:

\def\opts{align=left,minimum width=10em}
\begin{tikzpicture}
\expandafter\node\expandafter[\opts,draw=none] at (0,0) {
    Some fancy text.
};
\end{tikzpicture}

However, this doesn't work:

\def\opts{align=left,minimum width=10em}
\begin{tikzpicture}
\expandafter\node\expandafter[draw=none,\opts] at (0,0) {
    Some fancy text.
};
\end{tikzpicture}

There's obviously a good reason for this; but I just don't get it. I think it probably has something to do with the way \expandafter is working. Can anyone explain this to me?

  • In the second instance, the \expandafter only expands the letter "d" of "draw", and never gets as far as \opts. – Steven B. Segletes Nov 5 '14 at 12:22
  • However, if you wanted to expand it at the end of the options, you could 8^) do it this way: \begin{tikzpicture} \def\tmp{draw=none,} \expandafter\expandafter\expandafter\node\expandafter\expandafter\expandafter[\expandafter\tmp\opts] at (0,0) {Some fancy text.}; \end{tikzpicture} – Steven B. Segletes Nov 5 '14 at 12:27
  • Okay; so I'd have to define it all in macros, rather than a mixture of both. Is that right? I am also confused about the chains of \expandafter's (which I have seen in other StackExchange posts... and still don't get)... but I'll muddle through. Thanks so much! – Matthew Nov 5 '14 at 12:31
  • In my explanation, the 1,3,4,6,7 expands operate on \opts, which then leaves the 2,5 expands to operate on \tmp, so that by the time you get to \node, both \opts and \tmp are expanded. – Steven B. Segletes Nov 5 '14 at 12:33
  • Thanks again Steven. I think I get it now. I'll see if I can't reproduce that sequence by hand. Really appreciate it. – Matthew Nov 5 '14 at 12:37
3

\expandafter reads the two tokens that follow it, puts the first aside and causes one level expansion of the second one; then the first token is evaluated as usual as if the expansion of the second token had been there to begin with.

Thus your

\expandafter\node\expandafter[\opts,draw=none]

expands \opts because the second \expandafter is expanded by virtue of the first one. On the contrary,

\expandafter\node\expandafter[draw=none,\opts]

can't reach \opts, because the two tokens after the second \expandafter are [ and d. You need a different strategy, as

\expandafter\node\expandafter[\expandafter d\expandafter r\expandafter a\expandafter w\expandafter =\expandafter n\expandafter o\expandafter n\expandafter e\expandafter ,\opts]

doesn't seem a viable option. ;-)

What you can do is invoking a “global” one level expansion:

\begingroup\edef\x{\endgroup
\noexpand\node[draw=none,\unexpanded\expandafter{\opts}]}\x

The temporary macro \x will be forgotten as soon as it is expanded because the first token in its expansion is \endgroup; \node will remain untouched by \edef because of \noexpand, while \opts will be expanded once by \expandafter and the resulting token list won't be expanded further because of \unexpanded.

Without \unexpanded\expandafter{\opts}, but with just \opts is dangerous if \opts contains macros such as \bfseries; in any case you want its one level expansion, not the “all the way” one.

  • The only problem with \bfseries is that it should be robust but it isn't, or am I mistaken? – Manuel Nov 5 '14 at 12:52
  • @Manuel It is robust, and this is why it can't go in \edef. Unfortunately, \protected\def wasn't available when LaTeX2e was released. – egreg Nov 5 '14 at 13:01
  • @egreg Ah, okey, that's why \protected@edef exists. – Manuel Nov 5 '14 at 13:11
4

assuming (as is most likely the case) your options are just plain text and safe in an \edef you can do

 \edef\tmp{\noexpand\node[draw=none,\opts] at (0,0)}\tmp

as \edef will expand all the tokens in its argument (\node is prevented from expanding so when \tmp is expanded its expansion will be

\node[draw=none,align=left,minimum width=10em] at (0,0) 

If \opts does have tokens that may not be fully expanded in this way an alternative is

\def\tmp{\node[draw=none,}
\expandafter\tmp\opts] at (0,0)

Then \opts is only expanded once by the \expandafter rather than being fully expanded in an \edef.

  • 1
    What about a possible font=\bfseries option inside \opts? – egreg Nov 5 '14 at 12:49
  • 1
    @egreg Then the code needs to be more complicated. But I chose to give simple code together with a note of when it is appropriate. Someone can always come along and try to steal the tick with a fully defensive solution:-) but I'll add a (simpler) version that can cope with that to make you happy:-) – David Carlisle Nov 5 '14 at 12:52

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.