Save and restore the makeatletter/makeatother state
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.
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: