8

How would you test whether we are in an expansion only context (i.e., \edef, \xdef, \write,...) or not? The only ways I could think of leave things behind (but the full expansion of that extra stuff is empty).

Is there a clean way of testing this?

\documentclass{article}
\def\expiffalse{\expandafter\iffalse}
\protected\def\mygobble#1{\ifx\iftrue#1\expiffalse\fi}
\def\ifexpanding{\mygobble\iftrue\relax}

\begin{document}
We test that it sort of works: first, we are
\ifexpanding expanding\else not expanding\fi. 

Then, we are%
\edef\foo{\ifexpanding expanding\else not expanding\fi}
\show\foo
\foo.
\end{document}
  • FWIW, my take on this was along the lines of \let\voodoo\voodoo with \voodoo starting with \relax\relax and ignoring its first argument, which does pretty much the same thing, but would leave a \let\relax\relax in an expanding context. (The whole story can be found in nag.dtx. Ah, the days we were young...) – Ulrich Schwarz Jan 27 '11 at 09:16
  • @Ulrich: that's simpler than what I had! Although it has the same drawbacks, and in my case we obtain a protected but expandable command (if that makes sense). – Bruno Le Floch Jan 27 '11 at 10:07
  • @Bruno: yes, you're right. I admit to not having the required zen state to understand your solution right now. ;) – Ulrich Schwarz Jan 27 '11 at 14:07
  • I'm curious, when would this come up? – TH. Jan 28 '11 at 02:45
  • 1
    @TH.: I am writing a package that reads a LaTeX source, and does TeXish macro replacements before rewriting itself to another file. For this, I replace each character by a macro, which should have two different behaviours depending on the expansion context. If it is running text in the source (i.e., typesetting, non-expanding), it should write the corresponding character to the output. If it is already in a \write, it should simply expand to the character. --- Since I have full control on what is going on, I can just keep track of things with a boolean, but I was lead to the question above. – Bruno Le Floch Jan 28 '11 at 07:14
  • With your input I get "somethinglink", also if I add an optional argument, so \test is expanded. – egreg Sep 22 '11 at 10:06
  • @egreg: Botheration. You're right. That particular behaviour was coming from my custom driver ... I'll have to investigate that a little closer to see what was going on there. (I think that the actual question is independent of that particular motivation, isn't it?) – Andrew Stacey Sep 22 '11 at 10:18
  • Okay, so that specific case was due to a misuse (and misunderstanding) of \Hy@WrapperDef. – Andrew Stacey Sep 22 '11 at 10:21
  • @egreg: I've edited to remove that example. Thanks for the help on that. – Andrew Stacey Sep 22 '11 at 10:23
  • (I've flagged for a moderator to merge them.) – Andrew Stacey Sep 23 '11 at 06:51
  • Nobody has found a general solution in these 12 years (without leaving extra tokens)? – Javier Bezos Jan 12 '23 at 18:09

3 Answers3

10

I believe not, hence the addition of \ifincsname in pdfTeX (and XeTeX, I think). This came out of discussions way back when on detecting/testing all of the different expansion contexts, but IIRC the csname expansion was the only one that was easy or maybe unambiguous to implement.

Er, sorry for the vague answer; I'll pull up some references if they come to me.

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • 1
    Thanks. I'll leave the question open for a bit more, but I'm afraid that your answer is the right one. Although it is not clear why pdftex couldn't have a primitive for testing "if a protected sequence would expand there or not". – Bruno Le Floch Jan 27 '11 at 09:01
8

Short answer: no. Longer answer: To detect this would require a primitive which 'knows' about expansion. However, writing one is non-trivial: see the issues surrounding \ifincsname for demonstrations of why detection of this sort of thing is hard.

If you look at the LaTeX kernel, the \protect mechanism is about expansion. To work, it all has to be set up with \protect redefined correctly. That's why LaTeX2e 'robust' commands still require \protected@edef and similar to be used: within an \edef it is too late to alter anything.

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
7

Yes and no. My usual trick for this is this:

\def\voodoo#1#2{\relax\relax\Expansionstuff}
[...]
\let\voodoo\voodoo\Realstuff

if I haven't gotten it wrong: in an expanding context, \let\voodoo\voodoo\RealStuff expands left-to-right to \let\relax\relax\Expansionstuff, continuing to expand \Expansionstuff since \let and \relax are primitive, but in either case, you are left with a reasonably non-harmful \let\x\x thingy.

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036