7

The following MWE yields a compilation error:

\documentclass{article}

\usepackage[english]{babel}

\begin{document}
\def\AAA#1{\bgroup\def\contents{AAA}#1\egroup}
\def\BBB#1{\bgroup\def\contents{BBB}#1\egroup}

% Without this line, compilation fails
\def\contents{\relax}

\section{\AAA{\contents}}
\section{\BBB{\contents}}
\section{\AAA{\contents}}
\section{\BBB{\contents}}
\end{document}

ERROR: Undefined control sequence.

--- TeX said ---
\AAA #1->\bgroup \def \contents 
                                {AAA}#1\egroup 
l.12 \section{\AAA{\contents}}

However, when I uncomment the top-level \def\contents line, the example seems to work.

Why is this top-level \def needed? What are the downsides of adding it? Is there a cleaner way to do macro-local definitions (definitions that are only active in the body of the macro)?

The same issue happens with the following:

\begin{figure}
   \caption{\AAA{\contents}}
\end{figure}
  • Did you try \gdef? (But what is the point of the extra group?) I also don't really understand your last questions. How local do you need it to be? The same thing would happen if you defined a command inside (say) the center environment. – jon Nov 1 '16 at 4:44
  • I did, but ideally I'd like the definition of \contents to not leak. – Clément Nov 1 '16 at 4:47
  • Well, you can \let\contents\relax or use something like etoolbox's \undef (or \csundef). But whether this is really a good idea is unclear from this example. What's the concern with the leak? – jon Nov 1 '16 at 4:52
  • @jon gdef doesn't actually work; the leak was a secondary concern. It does work with a section, but it doesn't with a \caption, for example. – Clément Nov 1 '16 at 5:04
  • Hmm, well, I wonder if this example is too minimal. I'm still unclear as to why you are trying to bury (but then partially disinter) the command in an extra group. (Also what do you mean by 'it doesn't work'? You get an error or it isn't doing what you want it to do?) – jon Nov 1 '16 at 5:09
11

Section and caption titles are moving arguments. They go to the .aux file and from there to the .toc or .lof/.lot file. Section titles also can appear in header lines.

When macros are written to a file, they are expanded and Werner's answer shows this quite comprehensive. Assignments or definitions in a macro usually leads to fragile macros, they break when expanded, from Werner's answer:

\@writefile{toc}{\contentsline {section}{\numberline {1}\bgroup \def {AAA}\egroup }{1}}    

This is an invalid definition, because it tries to define {.

Or with \def\contents{\relax}:

\bgroup \def \relax {AAA}\relax \egroup

Then \relax is redefined, which should never be done.

A better top level definition for \contents is:

\let\contents\relax

Package calc uses the unexpandable \ignorespaces for this purpose:

\let\widthof\ignorespaces
\let\heightof\ignorespaces
\let\depthof\ignorespaces
\let\totalheightof\ignorespaces

Then \contents is not expanded, when written to a file:

\bgroup \def \contents {AAA}\contents \egroup

Also the definitions \AAA can be protected to avoid that the full definition text is written:

\let\contents\relax
\section{\protect\AAA{\contents}}

Then the .aux file contains:

\@writefile{toc}{\contentsline {section}{\numberline {1}\AAA {\contents }}{1}}

and the .toc file:

\contentsline {section}{\numberline {1}\AAA {\contents }}{1}

Alternatively, macro \AAA can be made robust:

\DeclareRobustCommand*{\AAA}{...}% The LaTeX2e way
\let\contents\relax
...
\section{\AAA{\contents}}

.aux file entry:

\@writefile{toc}{\contentsline {section}{\numberline {1}\AAA  {\contents }}{1}}

Or eTeX's \protected can be used:

\protected\def\AAA#1{...}
\let\contents\relax
...
\section{\AAA{\contents}}

Neither \def nor \DeclareRobustCommand checks, if the macro to be defined is already defined. A dummy \newcommand helps to get an error message, if this is the case:

\newcommand{\AAA}{}
\DeclareRobustCommand*{\AAA}[1]{...}

or \newcommand{\AAA}{} \protected\def\AAA#1{...}

LaTeX's interpretation of undefined macros include commands with meaning \relax. Therefore, \contents would be overwritten without error:

\let\contents\relax
\newcommand*{\contents}{foobar}

And macros \AAA, \BBB with arguments containing \contents inside moving arguments would be broken again. Package calc avoids this with the meaning \ignorespaces instead.

Summary:

\newcommand{\contents}{}% Check that \contents is undefined
\let\contents\ignorespaces

\newcommand{\AAA}{}%
% \DeclareRobustCommand*{\AAA}[1]{...}
\protected\def\AAA#1{\bgroup\def\contents{AAA}#1\group}

% ...
\section{\AAA{\contents}}
% ...
\caption{\AAA{\contents}}
9

Whether you want to or not, sectional units write their arguments to the .aux in case you may want to use \tableofcontents (say). The same would hold for \caption that would write content to the .aux for when you use \listoftables or \listoffigures. For that reason, your local definition of \contents survives the actual setting, but not writing to the .aux. To get around it, you'll have to disable that feature, or supply an alternative ToC-related entry:

\documentclass{article}

\begin{document}

\def\AAA#1{\bgroup\def\contents{AAA}#1\egroup}
\def\BBB#1{\bgroup\def\contents{BBB}#1\egroup}

\section[AAA]{\AAA{\contents}}
\section[BBB]{\BBB{\contents}}
\section[CCC]{\AAA{\contents}}
\section[DDD]{\BBB{\contents}}

\end{document}

Here's what's written to the .aux if you don't do the above:

\relax 
\@writefile{toc}{\contentsline {section}{\numberline {1}\bgroup \def {AAA}\egroup }{1}}
\@writefile{toc}{\contentsline {section}{\numberline {2}\bgroup \def {BBB}\egroup }{1}}
\@writefile{toc}{\contentsline {section}{\numberline {3}\bgroup \def {AAA}\egroup }{1}}
\@writefile{toc}{\contentsline {section}{\numberline {4}\bgroup \def {BBB}\egroup }{1}}

Note how \def is missing its control sequence argument - \contents in your case - which doesn't exist at the time of writing to the .aux.

Even if you were to use your "fix"

\def\contents{\relax}

the expansion upon writing to the ToC would still lead to something odd:

\@writefile{toc}{\contentsline {section}{\numberline {1}\bgroup \def \relax {AAA}\relax \egroup }{1}}
\@writefile{toc}{\contentsline {section}{\numberline {2}\bgroup \def \relax {BBB}\relax \egroup }{1}}
\@writefile{toc}{\contentsline {section}{\numberline {3}\bgroup \def \relax {AAA}\relax \egroup }{1}}
\@writefile{toc}{\contentsline {section}{\numberline {4}\bgroup \def \relax {BBB}\relax \egroup }{1}}
  • Is there a generic way to work around this problem? In my case, that \AAA macro may end up being used in captions as well as titles, and possibly in other places. – Clément Nov 1 '16 at 4:53
  • @Clément: I guess there should be. Perhaps one can define conditionals that may be expandable and execute only the branch you need. However, I'm not sure of the entire context, and without that, I don't know how to supply a "generic work around". – Werner Nov 1 '16 at 4:56
  • Thanks. Then context is that I have a file containing a small amount of LaTeX, and I have to macros \AAA and \BBB that implement different "styles". \contents appears in the file, and it is one of the things that \AAA and \BBB can change. I'm having trouble defining \AAA and \BBB in a way that makes them usable in captions and titles. – Clément Nov 1 '16 at 5:06
  • @Clément: So, do you want \AAA and \BBB to do nothing within captions/titles, but something else when they're not in there? Or, do you want them to do something inside captions/titles, but just not cause errors when writing content to the .aux? As you can see, it's still not clear to me. – Werner Nov 1 '16 at 5:27
  • 1
    @Clément: I assume \contents is therefore defined usually? See this; perhaps using \DeclareRobustCommand{\defcontents}{\def\contents} would allow you to redefine \contents whether it's in the title or in a ToC-related entry. – Werner Nov 1 '16 at 5:47
6

Others have explained the reason for the errors.

If you're already using xparse, there's a simple way out.

\NewDocumentCommand\contents{}{} % placeholder

\NewDocumentCommand\AAA{m}{%
  \begingroup
  \RenewDocumentCommand\contents{}{AAA}%
  #1%
  \endgroup
}

This is based on the fact that commands defined with \NewDocumentCommand are automatically robust.

You can set the default definition of \contents to anything you want.

You can also abstract the repeating parts:

\NewDocumentCommand{\defcontentscommand}{mm}{%
  % #1 = macro name, #2 = tokens
  \NewDocumentCommand{#1}{m}{%
    \begingroup
    \RenewDocumentCommand\contents{}{#2}##1%
    \endgroup
  }%
}
\NewDocumentCommand{\contents}{}{} % placeholder
\defcontentscommand{\AAA}{AAA}
\defcontentscommand{\BBB}{BBB}

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.