13

I quite often come into a situation, where I need to obtain some text produced by a package macro as a string, like in say:

One thing I bump into here, is trying to \typeout the macros in question, and typically observing a space between the \command and its arguments (hence the title); and I'd usually take this wrongly as a string parsing problem (as in, how do I remove the space between the command and its arguments), and spend quite a bit of time in a pointless battle :)


EDIT: to clarify this:

  • In the 1st link: \lipsum[1] command typesets "Lorem Ipsum..." in the PDF document; one would want to somehow obtain the characters for "Lorem Ipsum..." in a \temp command, such that \typeout{\temp} would print "Lorem Ipsum ..." to the terminal and log output.
  • In the 2nd link: \today from isodate typesets "24 May, 2012" in the PDF document; likewise, one would want to somehow obtain the characters for "24 May, 2012" in a \temp command, such that \typeout{\temp} would print "24 May, 2012" to the terminal and log output.

(whether \edef is used for the purpose is not that much of an issue, I guess; but as far as I can see, it is the only command possibly appropriate)


Now, I finally came up with an MWE that demonstrates this, so I'd like to ask about this in more generic terms - consider:

\documentclass{minimal}

\def\doubleCommand#1#2{testing-#1-#2}

\protected\def\tmpcmd#1{teststring#1} % concatenate, no spaces


\begin{document}

\typeout{--Test:--\doubleCommand{A}{B}--}

\edef\initArg{12}
\edef\dcArg{\tmpcmd\initArg}

\typeout{--Args:--\initArg--\dcArg--}

\edef\myResult{\doubleCommand{\dcArg}{Verbatim}}
\edef\myResult{\detokenize\expandafter{\myResult}}

\typeout{--Out :--\myResult--}

This: \doubleCommand{\dcArg}{Verbatim}

\end{document}

Now, this works fine, and typesets "This: testing-teststring12-Verbatim", as expected. However, if we look at the terminal log output from pdflatex, we get:

--Test:--testing-A-B--
--Args:--12--\tmpcmd 12--
--Out :--testing-\tmpcmd 12-Verbatim--

That is, the \edef didn't expand the \tmpcmd command - and its name is printed instead, along with the arguments (and notably, there is the space between the command and the first argument :)).

However, since the content is typeset fine, it is obviously accesible somewhere - and it is this content that I would like to have as a "string" in a variable. To show this, all I need to do is remove the \protected command, so I have:

\def\tmpcmd#1{teststring#1} % concatenate, no spaces 

... and then the terminal log shows:

--Test:--testing-A-B--
--Args:--12--teststring12--
--Out :--testing-teststring12-Verbatim--

... that is, the command output ("teststring12-Verbatim") is now a "string" value in a "variable" (command).

Now, I'm pretty sure there are good reasons for \protected; however, many times I just want to get to the data which is typeset anyways. So is there a strategy that one could employ in these cases? The above links suggest there is not, and a solution - if possible - depends on a given package's command, and its implementation... However, I'd like a method that I could employ, without changing package style files (like, say, removing \protected from diverse macros I might need, which even without further knowledge, I can tell is a bad idea).

I can already guess such a general method is not available - but could anyone explain why (maybe in terms of the above MWE)?

Many thanks in advance for any answers,
Cheers!

4
  • 2
    Perhaps I'm missing something here: \protected macros are used precisely because the do not expand inside \edef or similar. That's usually because they contain material which will fail to \edef correctly. Are you creating these macros, are are they from 'somewhere else'. If the later, it might be useful to say where.
    – Joseph Wright
    May 24, 2012 at 19:30
  • @JosephWright - currently I'm trying to capture (using \edef) the return of a \citefield (or \printfield) in biblatex, from a \newbibmacro hook; the items typeset at the spot where I want them as command contents, but because of the \protected I cannot capture them. However, I've met this in other contexts as well, which is why I'd like to have a better understanding of this generally. Cheers!
    – sdaau
    May 24, 2012 at 19:39
  • Also, maybe it's expressed in these terms: if commands are protected so I cannot get their contents in say, \typeout - how come those very same contents are typeset in the document, if I just issue the command normally at the very same spot in the code?
    – sdaau
    May 24, 2012 at 19:42
  • 2
    Notice that \protect and \protected are very different beasts and mustn't be confused with each other. The protection of commands with optional arguments or defined via \DeclareRobustCommand doesn't rely on \protected.
    – egreg
    May 24, 2012 at 19:53

2 Answers 2

12

David has explained this one way, I will take a slightly different tack.

First, what is going on? When e-TeX finds a protected macro, it will not expand it inside an \edef, \write and some similar circumstances, which usually exhaustively expand everything. That is to ensure you can see the protected token in the result. For example,

\protected\def\a{}
\typeout{%
  \a%
  b%
}

will show \a b, even though there is no space between \a and b as far as TeX is concerned. That's because the alternative is \ab, which as we can't see the tokens would be misleading (does it mean \a followed by b or a different macro \ab?).

You can 'force' expansion of a protected macro by using the fact that they do respect \expandafter:

\protected\def\a{b}
\edef\test{\expandafter\empty\a}
\show\a

using \empty as something to expand after which will not result in anything remaining behind.

Second, why do you need protection? Some operations in TeX simply will not work within an \edef as they use TeX primitives which are not 'expandable'. The classic ones here are assignments (\def, \let and so on). If you try

\let\a\undefined
\def\b{a}
\edef\test{\let\a\b}

you will not find that \a ends up defined to give a. Instead, you'll get an error: in this case 'Undefined control sequence'. That is because \let is not expandable. So TeX simply 'leaves it alone' inside the \edef, and tries to expand \a. That's not possible, and so an error arises. Thus in general it is not a good idea to try to expand protected macros.

3
  • Many thanks for clearing up (my) mystery of the space after command name, @JosephWright! :) Thanks for the \empty trick; I note that using it as \edef\test{\expandafter\empty\today} on isodate's \today, will crash with ! Undefined control sequence. \equal.., so I wouldn't be able to get to the "May 24, 2012" as characters anyways. Good example on the need for \protected too - moved accept here; cheers!
    – sdaau
    May 24, 2012 at 20:20
  • 2
    @sdaau \today with isodate is not protected nor robust. The problem there is that after two steps of expansions it presents TeX with \ifthenelse, which wants to perform assignments that are not executed in an \edef.
    – egreg
    May 24, 2012 at 21:38
  • Many thanks for that clarification, @egreg - cheers!
    – sdaau
    May 24, 2012 at 21:49
13

\protected stops expansion in \edef and \write and (unlike the LaTeX protection mechanism) it's not so easy to take off once it is applied. It's a bit hard to know what you need here as you ask for the "string" but TeX doesn't have strings.

you can do

\makeatletter

\protected\def\foo#1{---{\large#1}---}


\edef\x{\expandafter\strip@prefix\meaning\foo}
\show\x

which produces

> \x=macro:
->---{\large #1}---.
l.8 \show\x
        

but note here that \x does not contain the \large command in its definition, it has the 6 tokens \ l a r g e (plus other tokens---{}---).

To see why you need protection, compare

\documentclass{article}

\makeatletter

\def\foo#1{---{\large#1}---}


\edef\x{\foo 1}
\show\x

putting a fragile unprotected command in an edef produces

! TeX capacity exceeded, sorry [input stack size=5000].
\@nomath ...e \@font@warning {Command \noexpand #1
                                                  invalid in math mode}\fi 
l.8 \edef\x{\foo 1
                  }
!  ==> Fatal error occurred, no output PDF file produced!




   
1
  • 1
    Fantastic, @DavidCarlisle - many thanks for that! It both explains how to get to some of these contents (and what is the problem with that); and why \protected is needed in the first place. I'm still kinda bugged that I can, say, get the \today typeset; but at the same instance in the doc, I cannot get "Thu 24 May" as (expanded) characters in a command I'd use elsewhere (that would be the "string" or "variable value"; with "string" I just try to point out part of my misunderstanding could be due C/OOP approach). But at least I have a good reference now for this - thanks again; cheers!
    – sdaau
    May 24, 2012 at 20:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .