计算机编程的第三大祸害是向后兼容性。你必须修复不可避免会发现的错误。有些语法并不是一个好主意,必须修改才能不那么糟糕。这些修改是邪恶的。要么你修复错误和命令的行为变化,或者您保留错误以确保向后兼容性。第一种可能性可能会产生严重的后果,例如,当您使用 latex 管理酒店时,它\removeClient
被修改为不从数据库中删除患者,而是派出机器人将患者从房间中移除,然后您发送一个旧文件。第二种情况会让您得到一个\removeClient
宏和一个\deleteClient
宏,您永远不知道哪个宏可以执行您想要的操作。这个问题在 latex 中尤其严重,因为您总是绕过可疑行为来获得您想要的 \expandafter\noexpand\noexpand\noexpand :错误更正可能会在最糟糕的时候破坏您的文档。
我注意到,有几个软件包通过提供兼容性命令来处理此类问题。例如,pgfplots
has \pgfplotsset{compat=1.7}
、siunitx
has a version-1-compatibility
option 和mhchem
is called with \usepackage[version=3]{mhchem}
。其他策略包括从一开始就正确设计您的软件包,并且在需要进行重大更改的极少数情况下,您可以同时使用\pgfdeclarearrow
和\pgfarrowsdeclare
。或者,完全不关心这个问题也是一种选择。
从用户的角度来看,第一种解决方案非常方便,因为你不必寻找旧版本的软件包并努力让它发挥作用。我的问题是,从 latex 包作者的角度来看,这是如何工作的?
是主代码中包含条件语句更好,还是在寻求兼容性时加载一些兼容性文件更好,这些文件包含相关宏的兼容定义,这些宏将取代常规宏?还是还有其他解决方案?维护想要采取此类措施的乳胶代码库有多困难?
总而言之,处理 Latex 中的向后兼容性时的最佳做法是什么?
答案1
就我的包而言stackengine
(每个开发人员的情况都不同),在 1.0 版中,我将默认堆叠间隙存储在长度中,而不是宏中。由于长度在定义时以机器单位固定,因此这会产生意想不到的行为:如果我\Large
在设置堆叠间隙后更改字体大小(例如,变为),间隙仍以以前的字体大小单位表示(如果间隙是可缩放的长度,例如,这很重要1ex
)。
我改为用宏来定义它,这样就可以在构建堆栈之前从存储的宏中设置实际长度,从而设置当前字体大小。
但是定义这些间隙的语法不能再保留,例如,\Sstackgap=1ex
这是一个长度分配,而是改为\setstackgap{S}{1ex}
,调用宏分配。
为了向后兼容,我提供了一个[oldsyntax]
包选项。就我而言,代码的更改很小,因此我将两个代码块都包含在主代码中,并使用了条件,如下所示:
% PROCESS PACKAGE OPTIONS
\newif\ifstackengine@oldsyntax
\newif\ifstackengine@usestackEOL
\DeclareOption{oldsyntax}{\stackengine@oldsyntaxtrue}
\DeclareOption{usestackEOL}{\stackengine@usestackEOLtrue}
\ProcessOptions\relax
\ifstackengine@oldsyntax%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% THIS VERSION 1.0 SYNTAX WAS PHASED OUT %%%%%%%%%%%%%%%%%%%
%%%%% (but can invoke with \usepackage[oldsyntax]{stackengine} )
\newlength\Sstackgap\newlength\Lstackgap
\newcommand\stackgap{\if S\stacktype\the\Sstackgap\else\the\Lstackgap\fi}
\global\setlength{\Sstackgap}{3pt}
\global\setlength{\Lstackgap}{\baselineskip}
\else%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% AND REPLACED WITH THE FOLLOWING %%%%%%%%%%%%%%%%%%%%%%%%%%
% Again, thanks to Prof. Gregorio for his lucid explanation that helped
% improve this package. See:
% http://tex.stackexchange.com/questions/123443/
% defining-a-length-that-scales-with-fontsize-changes/123470#123470
\newcommand{\setstackgap}[2]{\@namedef{#1stackgap}{#2}}
\newcommand\stackgap{\@nameuse{\if S\stacktype S\else L\fi stackgap}\relax}
\setstackgap{S}{3pt}% SHORTSTACKING GAP BETWEEN ITEMS (SAME AS \shortstack)
\setstackgap{L}{\baselineskip}% LONGSTACKING GAP BETWEEN ITEMS (def. \baselineskip)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\fi
答案2
自提出这个问题以来,情况已经发生了变化,LaTeX 内核现在提供了一种提供“冻结”版本软件包的机制。这在进行重大更改时特别有用。例如,对于siunitx
v3,我有
\providecommand\DeclareRelease[3]{}
\providecommand\DeclareCurrentRelease[2]{}
\DeclareRelease{2}{2010-05-23}{siunitx-v2.sty}
\DeclareRelease{v2}{2010-05-23}{siunitx-v2.sty}
\DeclareCurrentRelease{}{2021-05-17}
这意味着如果用户请求 v2
\usepackage{siunitx}[=v2]
然后他们得到的正是:一个名为的文件siunitx-v2.sty
被加载,这是 v2 系列的最后一个冻结版本。
然后由开发人员决定添加多少粒度。再次查看siunitx
,我对 v3 使用语义版本控制,这意味着没有主要版本步骤就不会发生重大更改。因此,我认为我不需要提供回滚到 v3.1、v3.2 等,因为更改始终是严格附加的。