9

The MWE below creates a \savebox within a group which results in several blank lines:

enter image description here

If I comment out the\begingroup and \endgroup I get the desired results:

enter image description here

My thinking was that adding the \global should have fixed this issue.

So, how do I create and define \savebox within a group and yet be able to use the outside of the group?

Notes:

  • While the MWE here does not need grouping my actual use case does, so I would really prefer to not have to eliminate the grouping.

Code:

\documentclass{article}
\usepackage{etoolbox}

\newcommand{\MyLink}[2]{% \begingroup \ifcsdef{MyBox #1-#2}{% }{% \typeout{** Defined savebox for #1-#2}% \global\expandafter\newsavebox\csname MyBox #1-#2\endcsname% \global\expandafter\savebox\csname MyBox #1-#2\endcsname{#2: #1}% }% \expandafter\usebox\csname MyBox #1-#2\endcsname% \endgroup }%

\begin{document} \MyLink{http://www.wikipedia.org}{W}

\MyLink{http://www.wikipedia.org}{W} 

\MyLink{http://www.apple.org}{A} 

\MyLink{http://www.google.com}{G} 

\MyLink{http://www.wikipedia.org}{W} 

\MyLink{http://www.wikipedia.org}{W} 

\end{document}

Peter Grill
  • 223,288
  • why a box here (allocating a box every use is not usually recommended, even though etex means you have rather more than in classic tex) – David Carlisle Dec 16 '15 at 12:10
  • \global\savebox is definitely not going to work. – egreg Dec 16 '15 at 12:13
  • @DavidCarlisle: My actual use case is more involved, so the reason is for efficiency to save doing things thousands of times. – Peter Grill Dec 16 '15 at 12:13
  • @PeterGrill are you sure it's that much more efficient than a macro? – David Carlisle Dec 16 '15 at 12:18
  • @DavidCarlisle: Just did a small test. Using the solution you posted required less than 1/3 the elapsed time. – Peter Grill Dec 16 '15 at 12:33
  • @PeterGrill but perhaps you can optimise the macros (edef instead of def (essentially)) also is that saving 1/3 second or 1/3 of an hour? But anyway if you accept mine and not egreg's you are forgiven any such sins. – David Carlisle Dec 16 '15 at 12:36
  • @DavidCarlisle: Test Results: no caching: 182466, using \xdef 177718, using your \setbox solution 54390. Not sure what the time units are. – Peter Grill Dec 16 '15 at 12:53
  • With this, you can't use links in footnotes, or rather they will not obey any font changing context. –  Dec 16 '15 at 13:10

2 Answers2

7

Let's see what \global\savebox\foo does:

  1. \global expands \savebox so the input stream is

    \global\@ifnextchar ({\@savepicbox \foo}{\@ifnextchar [{\@savebox #1}{\sbox \foo}}
    
  2. \global expands \@ifnextchar, so the input stream is

    \global\let\reserved@d=( [...]
    

    (where [...] denote irrelevant tokens).

  3. \global is applied to \let.

Note that \global doesn't disappear until it finds a token it can apply to (\def, \let, a register address and so on; in general, an assignment).

This explains why you don't get errors, but also why you don't get a global setting for your box.

Doing \global\sbox\foo{tokens} would work, because the first expansion step triggered by \global is

\global\setbox\foo\hbox{\color@setgroup tokens\color@endgroup}

However, you may be using a stripped down example and perhaps you need the full force of \savebox, instead of \sbox, so you can use a temporary box register for the construction and then globally set the new register to be the same as the built box.

\documentclass{article}
\usepackage{etoolbox}

\newsavebox{\mylinkbox}
\newcommand*{\MyLink}[2]{%
\begingroup
    \ifcsdef{MyBox #1-#2}{%
    }{%
        \typeout{*** Defined savebox for #1-#2}%
        \global\expandafter\newsavebox\csname MyBox #1-#2\endcsname
        \savebox\mylinkbox{#2: #1}%
        \global\expandafter\setbox\csname MyBox #1-#2\endcsname\box\mylinkbox
    }%
    \expandafter\usebox\csname MyBox #1-#2\endcsname
\endgroup
}


\begin{document}
    \MyLink{http://www.wikipedia.org}{W}

    \MyLink{http://www.wikipedia.org}{W}

    \MyLink{http://www.apple.org}{A}

    \MyLink{http://www.google.com}{G}

    \MyLink{http://www.wikipedia.org}{W}

    \MyLink{http://www.wikipedia.org}{W}
\end{document}

enter image description here

egreg
  • 1,121,712
  • Yes, example is heavily stripped down, but I am not sure I know what the "full force of \savebox" is. Is there a question here that explains what \savebox will do that \sbox can't? – Peter Grill Dec 16 '15 at 12:37
  • 1
    @PeterGrill \savebox accepts optional arguments the same way as \makebox does (after the mandatory first argument to \savebox, of course). So you can do \savebox{\foo}[2\width][l]{...}, for instance. – egreg Dec 16 '15 at 12:39
  • @PeterGrill it has optional arguments (for length etc, or picture mode) – David Carlisle Dec 16 '15 at 12:40
5

I suspect that using a locally allocated box here is the wrong thing to do but to do it, just use the primitive commands to set the content:

\documentclass{article}
\usepackage{etoolbox}

\newcommand*{\MyLink}[2]{%
\begingroup
    \ifcsdef{MyBox #1-#2}{%
    }{%
        \typeout{*** Defined savebox for #1-#2}%
        \global\expandafter\newsavebox\csname MyBox #1-#2\endcsname%
        \global\expandafter\setbox\csname MyBox #1-#2\endcsname\hbox{{#2: #1}}%
    }%
    \expandafter\usebox\csname MyBox #1-#2\endcsname%
\endgroup
}%


\begin{document}
    \MyLink{http://www.wikipedia.org}{W} 

    \MyLink{http://www.wikipedia.org}{W} 

    \MyLink{http://www.apple.org}{A} 

    \MyLink{http://www.google.com}{G} 

    \MyLink{http://www.wikipedia.org}{W} 

    \MyLink{http://www.wikipedia.org}{W} 
\end{document}
David Carlisle
  • 757,742