Надежная команда не расширяется как аргумент \input

Надежная команда не расширяется как аргумент \input

Мне действительно нужна помощь — я уже несколько лет пользуюсь LaTeX время от времени, но мне редко приходилось погружаться в детали или решать проблемы, и я не могу в этом разобраться.

Проблема

Я работаю с .clsфайлом, который мне дала моя организация, в котором есть (возможно) проблемная строка. Он определяет макрос, который сохраняет вторую цифру введенного размера точки, а затем использует ее для ввода файла .clo. Спаренный MWE выглядит следующим образом:

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{mwe}

\RequirePackage{etoolbox}
\newrobustcmd\@ptsize{}

\DeclareOption{10pt}{\renewrobustcmd\@ptsize{0}}
\DeclareOption{11pt}{\renewrobustcmd\@ptsize{1}}
\DeclareOption{12pt}{\renewrobustcmd\@ptsize{2}}

\ExecuteOptions{11pt}
\ProcessOptions
\input{size1\@ptsize.clo}

Я также создал .texфайл MWE для компиляции:

\documentclass[11pt]{mwe}

\begin{document}
    
\end{document}

Однако, когда я компилирую это с помощью latexmk -synctex=1 -interaction=nonstopmode -file-line-error -pdf -outdir=%OUTDIR% %DOC%(внутри VSCode с использованием LaTeX Workshop), я получаю этот файл журнала с сообщением:

`File `[email protected]' not found`

В журнале записано:

This is pdfTeX, Version 3.14159265-2.6-1.40.21 (MiKTeX 20.11) (preloaded format=pdflatex 2020.11.3)  3 NOV 2020 08:57
entering extended mode
**"path/to/mwe/mwe.tex"
("path/to/mwe/mwe.tex"
LaTeX2e <2020-10-01> patch level 2
L3 programming layer <2020-10-27> xparse <2020-03-03> ("path/to\mwe\mwe.cls" <-- In case it matters, these slashes
Document Class: mwe                                                              really do switch directions
(path\to\tex/latex/etoolbox\etoolbox.sty                                     <-- Here as well
Package: etoolbox 2020/10/05 v2.5k e-TeX tools for LaTeX (JAW)
\etb@tempcnta=\count175
)

! LaTeX Error: File `[email protected]' not found.

Type X to quit or <RETURN> to proceed,
or enter new name. (Default extension: clo)

Enter file name: 

Потенциальное решение

Вот в чем дело: если я изменю файл .clsтак, чтобы он @ptsizeбыл определен через \newcommandи \renewcommandвместо через \newrobustcmdи \renewrobustcmd, то указанный выше файл MWE .texбудет работать нормально, генерируя пустой (двухстраничный) PDF-файл.

Может ли кто-нибудь а) объяснить, почему это происходит (насколько мне известно, этот .clsфайл отлично работает у других людей в моей организации), и б) какой подход к решению этой проблемы будет наилучшим? Заранее спасибо!

решение1

Разницу в поведении можно увидеть в этом примере кода:

\makeatletter
\protected\def\@ptsize{1}
\input{size1\@ptsize.clo}
\stop

который раньше вводил size11.clo, но с момента выхода LaTeX 01.10.2020 больше не делает этого, вместо этого выдает ошибку:

! LaTeX Error: File `[email protected]' not found.

Type X to quit or <RETURN> to proceed,
or enter new name. (Default extension: clo)

Enter file name:

Это было преднамеренное изменение в механизме разбора имени файла LaTeX. Он был заменен более надежным парсером, который не расширяет защищенные макросы (вместе с несколькими другими полезными функциями). Поведение нового парсера является правильным относительно надежных макросов: этине могуработают только в контекстах расширения, потому что они так или иначе сломаются (ты может находить тонн из Примеры из что вокруг), поэтому не расширять их — разумное решение.

Файл класса в вашей организации неправильно использует надежные команды для хранения некоторых данных (то, что мы обычно называем «списком токенов»). Такие данные в основном всегда должны быть доступны другим макросам, поэтому они должны расширяться, поэтому они не могут быть надежными/защищенными. Правильным решением, если вы можете редактировать файл класса, будет заменить \(re)newrobustcmdна \(re)newcommand:

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{mwe}

\RequirePackage{etoolbox}
\newcommand\@ptsize{}

\DeclareOption{10pt}{\renewcommand\@ptsize{0}}
\DeclareOption{11pt}{\renewcommand\@ptsize{1}}
\DeclareOption{12pt}{\renewcommand\@ptsize{2}}

\ExecuteOptions{11pt}
\ProcessOptions
\input{size1\@ptsize.clo}

Для справки, сообщение об ошибке

! LaTeX Error: File `[email protected]' not found.

выглядит так, потому что анализатор имени файла работает с \escapechar=-1, затем \@ptsizeсталкивается с \stringи становится @ptsize.

решение2

Если по какой-то непонятной причине необходимо сохранять данные, которые должны быть доступны для извлечения в надежных макросах, то вы можете самостоятельно запустить расширение и размещение токенов в правильном порядке, используя некоторые трюки \expandafterс \romannumeralобменом аргументами:

файл mwe.cls:

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{mwe}

\RequirePackage{etoolbox}
\newrobustcmd\@ptsize{}

\DeclareOption{10pt}{\renewrobustcmd\@ptsize{0}}
\DeclareOption{11pt}{\renewrobustcmd\@ptsize{1}}
\DeclareOption{12pt}{\renewrobustcmd\@ptsize{2}}

\newcommand\exchangeargs[2]{#2#1}

\ExecuteOptions{11pt}
\ProcessOptions
% It is not relied on \input expanding and putting into correct order
% tokens of its argument.
% Instead \romannumeral-expansion brings all tokens into
% correct order before \input and \input's filename-parsing 
% come into action.
% (This way things might sustain more changes to \input's 
% filename-parsing.)
\expandafter\input\expandafter{%
  \romannumeral
  \expandafter\exchangeargs\expandafter{\@ptsize}{\z@ size1}.clo%
}

В моей системе компилируется файл test.tex:

\documentclass{mwe}
\stop

выдает следующий вывод консоли:

$ pdflatex test.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2020-10-01> patch level 2
L3 programming layer <2020-10-27> xparse <2020-03-03> (./mwe.cls
Document Class: mwe 
(/usr/local/texlive/2020/texmf-dist/tex/latex/etoolbox/etoolbox.sty)
(/usr/local/texlive/2020/texmf-dist/tex/latex/base/size11.clo)) )
No pages of output.
Transcript written on test.log.

и этот файл test.log:

This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex 2020.11.23)  4 DEC 2020 19:47
entering extended mode
 restricted \write18 enabled.
 %&-line parsing enabled.
**test.tex
(./test.tex
LaTeX2e <2020-10-01> patch level 2
L3 programming layer <2020-10-27> xparse <2020-03-03> (./mwe.cls
Document Class: mwe 
(/usr/local/texlive/2020/texmf-dist/tex/latex/etoolbox/etoolbox.sty
Package: etoolbox 2020/10/05 v2.5k e-TeX tools for LaTeX (JAW)
\etb@tempcnta=\count175
)
(/usr/local/texlive/2020/texmf-dist/tex/latex/base/size11.clo
File: size11.clo 2020/04/10 v1.4m Standard LaTeX file (size option)
)) ) 
Here is how much of TeX's memory you used:
 364 strings out of 479485
 4393 string characters out of 5871962
 273140 words of memory out of 5000000
 17462 multiletter control sequences out of 15000+600000
 535388 words of font info for 30 fonts, out of 8000000 for 9000
 1141 hyphenation exceptions out of 8191
 52i,1n,59p,208b,36s stack positions out of 5000i,500n,10000p,200000b,80000s

No pages of output.
PDF statistics:
 0 PDF objects out of 1000 (max. 8388607)
 0 named destinations out of 1000 (max. 500000)
 1 words of extra memory for PDF output out of 10000 (max. 10000000)

На ваши вопросы:

а) объясните, почему это происходит (насколько мне известно, этот файл .cls прекрасно работает у других людей в моей организации)

Значительные изменения были внесены в LaTeX 2ε-ядро. Одно из последних изменений заключается в том, что в аргументе \input{...}robust макросы не расширяются.

Эти изменения хороши и раздражают одновременно: Они хороши, потому что во многих вещах то, как они теперь работают, кажется мне более строгим. Они раздражают, потому что мне приходится снова смотреть на код и привыкать к нему. ;-)

Вероятно, другие люди в вашей организации не используют ни одну из последних версий ядра LaTeX 2ε, и поэтому изменения, внесенные в более поздние версии ядра LaTeX 2ε, не влияют на работу их машин.

б) какой подход является наилучшим для решения этой проблемы?

Я не знаю, являются ли это «лучшими практиками», но поскольку за последние два года произошло так много изменений, я перестал полагаться на макросы LaTeX 2ε-kernel для работы так, как я привык на протяжении двух десятилетий. ;-)

В частности, я больше не полагаюсь на код, поддерживаемый другими, выполняющими расширение так, как это было два дня назад, и поэтому я часто использую свой собственный код, выполняющий всю работу по расширению, прежде чем данные передаются в качестве аргументов макросам других людей.

Я этого не делаю, потому что меня бы не огорчили изменения и нововведения.

Я делаю это для того, чтобы сделать свой код совместимым с как можно большим количеством различных версий LaTeX 2ε.

Связанный контент