17

Please consider the following minimal example:

\documentclass{article}
\tracingrestores=1

\def\foo{\bar\foobar}
\def\bar{\gdef\testA{blabb}}
\def\foobar{\expandafter\gdef\csname testB\endcsname{blubb}}

\begin{document}
{\foo}

\testA
\testB

\end{document}

The resulting log file contains an entry {retaining \testB=macro:->blubb}, but no corresponding entry for \testA. As far as I understand TeX, this means that \csname...\endcsname defines the macro \testA as \relax locally, which I immediately after reassign globally as "blubb", which, in turn, adds an retaining-entry on my save_stack.

In my real-world application, i need to be able to define a potentially infinite number of individually named macros using csname-constructions, which now has lead to a stack overflow because of this. (well, maybe not infinite, but definitely more than 80000s…)

My question now is: Is there a way to initialize a \csname…\endcsname-construct globally to avoid them being stacked on the save-stack? Or some kind of workaround which keeps my save-stack clean?

8
  • luatex a possibility? Aug 16, 2019 at 16:27
  • why are you defining so many global variables in a local context? Aug 16, 2019 at 16:50
  • xmltex… in a nutshell i'm reading the information from style- and class attributes in html tables and store that information in control sequences to render them later after some calculations and processing. For each cell. Individually.... And we have books that consist only of tables… And no, luatex is not an option: too slow for professional typesetting.
    – Lupino
    Aug 16, 2019 at 16:56
  • I still don't see why this should means that you need groups. Also it sounds as if you would gain a lot by using lua to do the processing. Aug 16, 2019 at 17:12
  • 1
    xmltex, ooh, a user:-) Aug 16, 2019 at 17:41

2 Answers 2

21
+100

Instead of making the \csname assignment global, you can make it even more local:

\documentclass{article}
\tracingrestores=1

\def\foo{\bar\foobar}
\def\bar{\gdef\testA{blabb}}
\def\foobar{\begingroup\expandafter\endgroup\expandafter\gdef\csname testB\endcsname{blubb}}

\begin{document}
{\foo}

\testA
\testB

\end{document}

Now the \expandafter is executed in a group, so the \csname defined \testB to \relax in a group which ends before \gdef starts. So \testB is undefined when the global definition occurs which should avoid the retaining entry.

6
  • 1
    I dont quite understand why this is working, but it works. Thanks.
    – Lupino
    Aug 16, 2019 at 17:31
  • 2
    This is absolutely genius! This allows to have a fully-expandable \ifcsname test without e-TeX using \begingroup\expandafter\endgroup\expandafter\ifx\csname foo\endcsname\undefined. Bounty will be offered when eligible. Aug 17, 2019 at 9:02
  • 2
    @HenriMenke Heiko uses this quite a bit in his packages too, but it's not really fully expandable, it leaves \begingroup\endgroup before the test (so you can't use it at start of a table cell without preventing \omit etc) Aug 17, 2019 at 10:04
  • 1
    Just for the sake of completeness: I got a hint from a colleague that \ltx@LocalExpandAfter from Oberdiek's ltxcmds.sty seems to do the same thing.
    – Lupino
    Aug 20, 2019 at 12:43
  • 2
    @Lupino I've attempted a (simplistic) explanation of why this works, on a follow-up question here. Aug 22, 2019 at 20:39
10

I don't know squat about (La)TeX stacks (other than stackengine, LOL), but if the goal is to avoid having the \csname inside a \def...

Expand the \csname before executing the outer \def.

\documentclass{article}
\tracingrestores=1

\def\foo{\bar\foobar}
\def\bar{\gdef\testA{blabb}}
\expandafter\def\expandafter\foobar\expandafter{\expandafter\gdef\csname testB\endcsname{blubb}}

\begin{document}
{\foo}

\testA
\testB

\end{document}
5
  • good trick, but I expect that's cheating, as the OP would probably need 80000 top level instances of that in the stated use case. (+1anyway:-) Aug 16, 2019 at 16:27
  • Gaining that extra-level of depth (height?), actually helps for most of my cases, but not for all :/ Am I assuming correctly that i would need one level of \expandafter for each "layer" of embeded macro? That woudl be bad, because \foo could itself be embedded in another macro, or not. And since I'm using (forced to use) xmltex, i probably never will reach "real" top-level with that method… Thanks anyways for the quick reply.
    – Lupino
    Aug 16, 2019 at 16:51
  • @Lupino As I said, I don't profess to understand what is under TeX's hood, such as stacks. However, if the csname is the issue, you would not need additional layers of \expandafter...only on the layer employing the \csname. If, on the other hand, the issue is one of \def vs. \gdef, then I am not sure. But if David thinks I am cheating, I probably am. Aug 16, 2019 at 17:03
  • @Lupino The key would be if you can think of a problem case that uses no \csnames... Aug 16, 2019 at 17:05
  • 1
    @StevenB.Segletes If i use \def\baz{\expandafter\def\expandafter\foobar\expandafter{\expandafter\gdef\csname testB\endcsname{blubb}}} and later {\baz\foo} i would need to extend the \expandafters outwards to \def\baz to raise the csname to the same level as my call to \testB, otherwise i have the same problem as before…
    – Lupino
    Aug 16, 2019 at 17:16

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.