4

When posting snippets of latex code that define or modify commands containing @, one commonly includes the \makeatletter...\makeatother wrapper that's necessary if pasting the snippet into an ordinary document. However, such pasting then has bad effects if it happens to occur somewhere that \makeatletter is already in effect. Is there a good way to "save the state of 'at'", execute some code, and then restore the original state, whatever it might have been?

(Yes, I know that \makeatletter/\makeatother are local to groups. However, certain code doesn't work the same way inside a group, e.g. macro definitions are local to the group by default, so it would be better to avoid having to enclose the whole snippet in a group.)

Mike Shulman
  • 1,557
  • Where are you putting these commands? Unless you are misusing them, I'd think it'd be hard to get confused as to whether you need to use them or not? (E.g., don't use them in a .sty file.) – jon Oct 21 '16 at 04:13
  • Think to \makeatletter and \makeatother as ”snippet delimiters“ – egreg Oct 21 '16 at 07:42
  • To be honest, I don't actually have a use in mind for such commands. I was just curious if it was easy to do, because it seemed to me as though it would have been better if this is what \makeatletter and \makeatother actually did. – Mike Shulman Oct 21 '16 at 08:33

2 Answers2

5

This is what—in some situations—I do:

\begingroup
\makeatletter
\@firstofone{%
  \endgroup
  ⟨Stuff/macro-definitions whose tokenizing needs to
   take place while `@` is of category-code 11(letter)
   but whose processing/carrying-out/running/execution
   is to take place while the category-code of `@` is
   what it was before encountering \makeatletter.⟩
}%

The argument of \@firstofone will be read and tokenized within a local scope where @ is of category-code 11(letter) and where therefore @ either can be part of the name of control-word-tokens or will be tokenized as an explicit character-token of category-code 11(letter).
When processing/carrying out/running/executing those tokens that came into being due to reading and tokenizing the argument of \@firstofone takes place, the first \endgroup will terminate that local scope, resetting the category code of @ to whatever it was before calling \makeatletter.
Thus processing/carrying out/running/executing the remaining tokens of \@firstofone's argument does not take place within that local scope where the category-code of @ is changed but with the category-code-régime that was in effect before processing the \makeatletter-token.

Ulrich Diez
  • 28,770
4

You could define a new macro to store the current catcode of @ and restore it using this macro later on:

\documentclass{article}

\def\safemakeatletter#1{%
    \edef#1{\catcode`@=\the\catcode`@\relax}%
    \makeatletter
}

\def\testat{catcode of \texttt{@} is: \the\catcode`@\relax\par}

\begin{document}

\catcode`@=10\relax
\testat
\makeatletter
\testat
\makeatother
\testat

\medskip

\catcode`@=10\relax
\testat
\safemakeatletter\safemakeatother
\testat
\safemakeatother
\testat

\end{document}

As you can see in the output, \makeatother incorrectly restores the catcode in the first part, while \safemakeatother doesn't:

enter image description here

siracusa
  • 13,411