如何将定义传递给 \AtBeginDocument?

如何将定义传递给 \AtBeginDocument?

我需要将一段代码的执行延迟到其他包加载时,因此我将其包装到\AtBeginDocument

\AtBeginDocument{%
  \def\reserved@a#1#2{%
    \edef\reserved@b{#1}%
    \edef\reserved@c{#2}%
    \ifx\reserved@b\reserved@c
      \let\cyrillicencoding\reserved@c
    \fi}
  \def\cdp@elt#1#2#3#4{%
    \reserved@a{#1}{OT2}%
    \reserved@a{#1}{LCY}%
    \reserved@a{#1}{X2}%
    \reserved@a{#1}{T2C}%
    \reserved@a{#1}{T2B}%
    \reserved@a{#1}{T2A}%
    \if@uni@code
      \reserved@a{#1}{EU1}%
      \reserved@a{#1}{EU2}%
    \fi}
  \cdp@list
}

此代码查看已加载的字体编码列表(保存在 中\cdp@list),将其与预定义的所需西里尔编码列表(从最不优选到最优选排序)进行比较(OT2、LCY... EU2),然后将选定编码的名称保存到宏\cyrillicencoding。如果 LuaTeX 或 XeTeX 正在运行,则布尔宏的\if@uni@code计算结果为 true:

\newif\if@uni@code
\ifdefined\luatexversion \@uni@codetrue \else
\ifdefined\XeTeXrevision \@uni@codetrue \fi\fi

它工作正常。但是,如果我将部分预备定义移出框外,它就会失败。例如,

\def\reserved@a#1#2{%
  \edef\reserved@b{#1}%
  . . .
\fi}
\AtBeginDocument{\cdp@list}

我猜测移动参数内的宏\AtBeginDocument应该以某种方式被protect编辑或expendafter有人能解释一下我的烦恼从何而来吗? 第二个问题:将大块代码延迟到文档开始时间是一种好的做法吗?

更新:这是一个最简单的例子:

\documentclass{minimal}
\usepackage{mytest}
\usepackage[T2B]{fontenc}
\begin{document}
\verb|cyrillicencoding|={\csname cyrillicencoding\endcsname}
\end{document}

mytest.sty

\def\reserved@a#1#2{%
    \edef\reserved@b{#1}%
    \edef\reserved@c{#2}%
    \ifx\reserved@b\reserved@c
      \let\cyrillicencoding\reserved@c
    \fi}
\def\cdp@elt#1#2#3#4{%
    \reserved@a{#1}{T2C}%
    \reserved@a{#1}{T2B}%
    \reserved@a{#1}{T2A}%
}
\AtBeginDocument{\cdp@list}

如果将 的整个内容mytest.sty包装到 中\AtBeginDocument,则输出将是\cyrillicencoding=T2B

答案1

你应该绝不依赖于\reserved@a内核经常用作“临时宏”的类似控制序列名称的含义。因此删除\reserved@afrom的定义\AtBeginDocument是错误的,这就是为什么带有

\AtBeginDocument{\cdp@list}

例如,对于一个最小文档,执行\reserved@a时我得到的意思是\@begindocumenthook

> \reserved@a=macro:
->\def \@currenvir {document}\edef \@currenvline {\on@line }\csname document\endcsname .

在 中包含大段代码并没有错\AtBeginDocument;但是你可以通过以下方式缩短标记列表:

\def\@setcyrillicencoding{%
  \def\sce@a##1##2{%
    \edef\sce@b{##1}%
    \edef\sce@c{##2}%
    \ifx\sce@b\sce@c
      \let\cyrillicencoding\sce@c
    \fi}%
  \def\cdp@elt##1##2##3##4{%
    \sce@a{##1}{OT2}%
    \sce@a{##1}{LCY}%
    \sce@a{##1}{X2}%
    \sce@a{##1}{T2C}%
    \sce@a{##1}{T2B}%
    \sce@a{##1}{T2A}%
    \if@uni@code
      \sce@a{##1}{EU1}%
      \sce@a{##1}{EU2}%
    \fi}%
  \cdp@list
}
\AtBeginDocument{\@setcyrillicencoding}
\@onlypreamble\@setcyrillicencoding
\@onlypreamble\sce@a
\@onlypreamble\sce@b
\@onlypreamble\sce@c

最后几行用于释放文档中无用的宏\@setcyrillicencoding和所占用的内存。的内容将自动清除。\sce@x\@begindocumenthook

由于\reserved@a在那里使用,因此不建议重新定义它。

答案2

有人能解释一下我的烦恼从何而来吗?

您正在使用预订的*临时*宏,然后不要直接使用它们。一旦执行任何其他 LaTeX 内核宏,这些宏可能会被覆盖。换句话说,您的定义将不会保留,直到\begin{document}您的\cdp@list宏使用临时宏的一些任意定义。

您应该使用其他临时宏。如果babel您调用的代码确实需要\reserved@a使用,您需要确保在使用宏之前直接进行定义\cdp@list

而且,您的定义也\cdp@elt被改变了fontenc,所以当然您需要在使用它之前直接进行这个定义,而不是在您的包内。

将大块代码延迟到文档开始时间是一种好的做法吗?

我会将所有内容定义到一个宏中,并仅将此宏添加到钩子中。每次钩子获得更多代码时,所有旧代码都会被复制,如果许多软件包安装了大量代码,则成本会很高。请注意,#如果将宏定义放入宏中,则需要将所有内容翻倍。

相关内容