9

Is there a general way to find the depth of a macro evaluation? For example, consider the following script,

\documentclass{beamer}
\usepackage{mathtools}
\usepackage{calc}

\begin{document}

\begin{frame}
  \newcommand*{\ofSize}[2]{\hphantom{####2}\llap{####1}}
  x \alt<1>{\ofSize{apples}{oranges}}{oranges} \uncover<1-2>{done.}

\end{frame}

\end{document}

I wanted to alternate between two expressions so that it would not cause surrounding text to move around during animations, with a more general solution then that given in Animated equation in Beamer. As you can see, I needed to escape the first argument, #1, three more times (####1) to get this to work.

The process I used was arbitrary; I just added # till it stopped giving me an error. Clearly, this could fail with horrendous side-effects if #1 is a meaningful expansion in a "deeper" nested macro level. Is there a best practice / methodology when writing such commands?

  • 2
    Try putting the command declaration in the preamble. Then you need only one #. – percusse Jun 12 '13 at 07:47
  • @percusse Ah, I did not know that happened. Thank you! If only I had known that 2 hours ago... – Arun Chaganty Jun 12 '13 at 07:50
  • 2
    beamer reads the frame's contents as an argument, which makes #### into ##; then it sees a definition in this argument and by general rules this wants ## instead of #. It's really better to put such \newcommand instructions in the preamble. – egreg Jun 12 '13 at 07:54
  • @egreg The floor is yours on this one. – percusse Jun 12 '13 at 11:44

2 Answers2

6

The details are pretty involved. But basically beamer reads the contents of a frame environment as an argument and this process reduces a pair of ## into one #; but now your \newcommand is given in the argument to another command, so the # marks need to be doubled.

The pairing means that you need four # characters to mean one of them.

The solution is to do it in the preamble.

\documentclass{beamer}
\usepackage{mathtools}
\usepackage{calc}

\newcommand*{\ofSize}[2]{\leavevmode\hphantom{#2}\llap{#1}}

\begin{document}

\begin{frame}

x \alt<1>{\ofSize{apples}{oranges}}{oranges} \uncover<1-2>{done.}

\end{frame}

\end{document}

Don't forget the initial \leavevmode, because \hphantom doesn't start a paragraph, so if \ofSize is not preceded, like in the example application, by something that starts a paragraph, you wouldn't get the expected result.

egreg
  • 1,121,712
  • Thanks for the comment about \leavevmode. That was indeed a problem I faced, but I switched to using a \makebox instead for other reasons.

    After your comment, I'll be sure to define any such macros in the preamble, but for the sake of academic inquiry, my original question still stands: how would you find the "macro expansion call chain" without having a prior knowledge of how each macro is defined? For example, if I did \hphantom{apples}, could I observe exactly how this macro is being expanded in order escape the arguments appropriately?

    – Arun Chaganty Jun 12 '13 at 16:33
  • 1
    @arunchaganty That's a difficult question, with beamer, because it does many shufflings with the contents of frame before it decides to typeset something, and so to execute commands. You could add \tracingmacros=1 and look at every expansion in the .log file, but it will be quite a long reading with beamer. – egreg Jun 12 '13 at 17:03
  • Ok, thanks. I'll take \tracingmacros as the general answer, and note the reduced efficacy when used with beamer. – Arun Chaganty Jun 12 '13 at 20:11
2

egreg has already explained pretty well what is happening when beamer processes a frame. Besides moving the macro definition out of the frame environment, there is also the alternative to mark the frame as [fragile]:

\documentclass{beamer}
\usepackage{mathtools}
\usepackage{calc}


\begin{document}

\begin{frame}[fragile]

  \newcommand*{\ofSize}[2]{\leavevmode\hphantom{#2}\llap{#1}}

x \alt<1>{\ofSize{apples}{oranges}}{oranges} \uncover<1-2>{done.}

\end{frame}

\end{document}

With fragile, beamer processes the frame in a different way: Basically, it gobbles the content and writes it to an external file (with the extension .vrb). I then passes the file as argument to the "engine" macro, which read it back as many time as needed. Thereby, all kind of problems related to macro arguments (verbatim material, for instance), are avoided.

The drawback is a slightly longer processing time. The advantage is that you can keep macro definitions local to a frame where appropriate. I tend to do this a lot, as it reduces the risk of forgetting some macro when reusing a frame in another presentation.

Daniel
  • 37,517