将剩余选项传递给另一个类的好方法是什么?

\begin{filecontents}{main.cls}
\ExplSyntaxOn

\newcommand{ \selectedoptions } { color }

\keys_define:nn { class / options } {
  theme .choices:nn = {
    bw,
    color,
    dark,
    draft
  } {
    \renewcommand{ \selectedoptions } {
      \tl_use:N \l_keys_choice_tl
    }
  }
}

\ProcessKeyOptions[class / options]

\LoadClassWithOptions{ article } % How to pass all options 
                                 % except the theme one?

\ExplSyntaxOff
\end{filecontents}


\documentclass[12pt, a4paper, theme=bw]{main}
%\documentclass[12pt, a4paper]{article}

\usepackage{blindtext}

\begin{document}

\blinddocument

\end{document}

笔记

选择的选项永远不会与其他类同名,因为其他类所做的设置永远不会被主类使用。

6

  • 1
    我希望你不要仅仅因为收到Unused global option(s): [theme]警告就使用这个。如果是这样的话,还有更简单(或其他?)的解决方案(虽然我现在找不到相关问题……)。


    – 

  • 我正在学习使用课程选项;所以即使它或多或少起作用,我也在做坏事。


    – 

  • 事实上, Joseph Write给出的解决方案与cfr给出的设置可以在没有任何警告的情况下完成工作。


    – 

  • 1
    它可能会起作用,但它会吞噬您在类中定义的任何与 中同名的选项article。因此,如果您12pt向类中添加了一个选项,article12pt选项将不再触发。


    – 

  • 我已经更新了我的问题。我会始终避免名称冲突。


    – 


最佳答案
4

我的意思是

\begin{filecontents}[overwrite]{main.cls}
  \ExplSyntaxOn

  \tl_new:N \l_cookbook_cls_option_tl

  \newcommand{ \selectedoptions } { color }

  \keys_define:nn { class / options } {
    theme .choices:nn = {
      bw,
      color,
      dark,
      draft
    } {
      \renewcommand{ \selectedoptions } {
        \tl_use:N \l_keys_choice_tl
      }
    }
  }


  \DeclareUnknownKeyHandler[class/options]{%
      \PassOptionsToClass{\CurrentOption}{article}%
    }
    \ProcessKeyOptions[class/options]

  \LoadClass{ article } % How to pass all options
  % except the theme one?

  \ExplSyntaxOff
\end{filecontents}


\documentclass[12pt, a4paper, theme=bw]{main}
%\documentclass[12pt, a4paper]{article}

\usepackage{blindtext}

\begin{document}
\selectedoptions

\blinddocument

\end{document}

运行良好。但是

  \DeclareUnknownKeyHandler{%
      \PassOptionsToClass{\CurrentOption}{article}%
    }
    \ProcessKeyOptions

  \LoadClass{ article } % How to pass all options
  % except the theme one?

不处理main类选项,而

  \DeclareUnknownKeyHandler{%
      \PassOptionsToClass{\CurrentOption}{article}%
    }
    \ProcessKeyOptions[class/options]

  \LoadClass{ article } % How to pass all options
  % except the theme one?

不会将未知选项传递article

  \DeclareUnknownKeyHandler[class/options]{%
      \PassOptionsToClass{\CurrentOption}{article}%
    }
    \ProcessKeyOptions

  \LoadClass{ article } % How to pass all options
  % except the theme one?

不处理其中任何一个。

3

  • [稍后删除]


    – 

  • 这是预期的行为:所有 keyval 命令都采用可选参数来指定在选项树中查找的位置 – 因此,如果您要添加未知的处理程序,它必须与您要处理的键位于同一位置。


    – 

  • @JosephWright 我只是想说,在 OP 的例子上下文中,仅仅发布你发布的内容并不是最具解释性的答案。


    – 

如果您的目标是加载article所有选项,并且您只是设置这个奇怪的过滤来避免Unused global option(s): [theme]警告,那么您可以使用以下简单的解决方案。否则,请查看此答案(或其他答案)中的任何其他块。

expkv-opt具有不同的宏来解析选项,对于未使用的全局选项列表具有不同的行为。特别是在类文件中,使用\ekvoProcessLocalOptions会将任何未知选项添加到所述列表并从该列表中删除任何已知选项。但是,如果您使用,\ekvoProcessGlobalOptions您基本上会获得相同的选项(因为全局选项是传递给的选项\documentclass,并且您的类是主类),但行为不同。\ekvoProcessGlobalOptions 从未使用的选项列表中删除,它永远不会添加到其中。

通过此行为,您可以简单地将所有选项转发给article(这将填充未使用的选项列表),然后解析它们,\ekvoProcessGlobalOptions这将从未使用的选项列表中删除您的类已知的所有选项。无需过滤。

\begin{filecontents}[overwrite]{main.cls}
\ProvidesExplClass{main}{2024-11-01}{1}{adhoc class for an MWE}

\LoadClassWithOptions { article }

\RequirePackage{expkv-def,expkv-opt}

\newcommand*\selectedfontsize{10}

\ekvdefinekeys { class / options }
  {
     choice-store~     theme = \selectedoptions {bw, color, dark, draft }
    ,initial~          theme = color
    ,protect~ noval~   12pt  = \renewcommand*\selectedfontsize{12}
  }

\ekvoProcessGlobalOptions { class / options }
\end{filecontents}


\documentclass[12pt, a4paper, theme=bw, foo]{main} % added foo so we get an unused global options warning
%\documentclass[12pt, a4paper]{article}

\usepackage{blindtext}

\begin{document}

\selectedfontsize

\blinddocument

\end{document}

不幸的是,据我所知,这种方法是不可行的ltkeys,因为如果您没有指定未知处理程序,它会从未使用的选项列表中删除并向其中添加未知选项,或者如果您提供未知处理程序来忽略未知选项,它不会从未使用的选项列表中删除已知选项。

实现此功能的唯一方法ltkeys是,如果您的类中没有任何与article-options 同名的选项,或者您使用 定义所有选项.code:n = <whatever you want to do>\PassOptionsToClass{\CurrentOption}{article}。最后一种情况是可能的,但您会丢失所有不支持任意用户代码的键处理程序(例如不支持.int_set:N)。所以它看起来像这样:

\begin{filecontents}[overwrite]{main.cls}
\ProvidesExplClass{main}{2024-11-01}{1}{adhoc class for an MWE}

\newcommand \selectedoptions { color }
\newcommand \selectedfontsize { 10 }

\keys_define:nn { class / options } {
  theme .choices:nn = {
    bw,
    color,
    dark,
    draft
  } {
    \exp_args:NNV \renewcommand \selectedoptions \l_keys_choice_tl
  }
  ,12pt .code:n =
    \renewcommand\selectedfontsize{12} \PassOptionsToClass{12pt}{article}
}
\DeclareUnknownKeyHandler[class/options]{%
  \PassOptionsToClass{\CurrentOption}{article}%
}

\ProcessKeyOptions[class / options]

\LoadClass{ article }
\end{filecontents}


\documentclass[12pt, a4paper, theme=bw, foo]{main}
%\documentclass[12pt, a4paper]{article}

\usepackage{blindtext}

\begin{document}
\selectedoptions\selectedfontsize

\blinddocument

\end{document}

由于您只定义了一个theme密钥,最简单的解决方案是使用unknown .code:n来转发所有未知选项。

不幸的l3keys是没有提供一些接口来区分opt={}opt所以这里的代码假设每个空值都是没有给出值的选项。

\begin{filecontents}[overwrite]{main.cls}
\ProvidesExplClass{main}{2024-11-01}{1}{adhoc class for an MWE}

\newcommand \selectedoptions { color }

\keys_define:nn { class / options } {
  theme .choices:nn = {
    bw,
    color,
    dark,
    draft
  } {
    \exp_args:NNV \renewcommand \selectedoptions \l_keys_choice_tl
  }
  % as noted in Joseph's answer you can (and should) use
  % `\PassOptionsToClass{\CurrentOption}{article}` here if you're only using
  % this key set in \ProcessKeyOptions.
  ,unknown .code:n = \exp_args:Ne \PassOptionsToClass
    { \l_keys_key_str \tl_if_empty:nF {#1} { = { \exp_not:n {#1} } } }
    {article}
}

\ProcessKeyOptions[class / options]

\LoadClass{ article }
\end{filecontents}


\documentclass[12pt, a4paper, theme=bw]{main}
%\documentclass[12pt, a4paper]{article}

\usepackage{blindtext}

\begin{document}

\blinddocument

\end{document}

如果您有多个密钥,并且其中一部分应该转发,那么您必须以某种方式过滤掉不应转发的密钥。为此,下面定义了一个小辅助宏,它对从输入 key=value 列表中删除的单个密钥名称进行基于字符串的比较。

\begin{filecontents}[overwrite]{main.cls}
\ProvidesExplClass{main}{2024-11-01}{1}{adhoc class for an MWE}

\newcommand \selectedoptions { color }
\newcommand*\selectedfontsize{10}

\keys_define:nn { class / options } {
  12pt .code:n = \tl_set:cn { selectedfontsize } { 12 } % just to get a second key
  ,theme .choices:nn = {
    bw,
    color,
    dark,
    draft
  } {
    \exp_args:NNV \renewcommand \selectedoptions \l_keys_choice_tl
  }
}

\ProcessKeyOptions[class / options]

\cs_generate_variant:Nn \__projetmbc_filter_kv:nn { nv }
\cs_new:Npn \__projetmbc_filter_kv:nn #1#2
  {
    % \keyval_parse:nnn will return each kv-pair in \exp_not:n, we need a step
    % more
    \use:e
      {
        \keyval_parse:nnn
          { \__projetmbc_filter_kv_aux:nn {#1} }
          { \__projetmbc_filter_kv_aux:nnn {#1} }
          {#2}
      }
  }
\cs_new:Npn \__projetmbc_filter_kv_aux:nn #1#2
  { \str_if_eq:nnF {#1} {#2} { , \exp_not:n {#2} } }
\cs_new:Npn \__projetmbc_filter_kv_aux:nnn #1#2#3
  { \str_if_eq:nnF {#1} {#2} { , \exp_not:n {#2={#3}} } }

\exp_args:Ne \PassOptionsToClass
  { \__projetmbc_filter_kv:nv { theme } { @raw@opt@\@currname.\@currext } }
  { article }

\LoadClass{ article }
\end{filecontents}


\documentclass[12pt, a4paper, theme=bw]{main}
%\documentclass[12pt, a4paper]{article}

\usepackage{blindtext}

\begin{document}

\selectedfontsize

\blinddocument

\end{document}

expkv-def这是一个使用和expkv-opt代替 的示例ltkeys

由于expkv-def具有非常方便的also机制,允许将任意代码添加到任何预定义代码处理程序,因此将密钥转发添加到密钥列表中相当简单。下面使用\clist_map_inline:nn密钥循环执行此操作12pt

\begin{filecontents}[overwrite]{main.cls}
\ProvidesExplClass{main}{2024-11-01}{1}{adhoc class for an MWE}

\RequirePackage{expkv-def,expkv-opt}

\newcommand*\selectedfontsize{10}

\ekvdefinekeys { class / options }
  {
     choice-store~     theme = \selectedoptions {bw, color, dark, draft }
    ,initial~          theme = color
    ,protect~ noval~   12pt  = \renewcommand*\selectedfontsize{12}
    ,protect~ unknown~ code  = \PassOptionsToClass{#1={#2}}{article}
    ,protect~ unknown~ noval = \PassOptionsToClass{#1}{article}
  }

% specify all defined keys that should be forwarded
\clist_map_inline:nn { 12pt }
  {
    \ekvifdefined { class / options } { #1 }
      {
        \ekvdefinekeys { class / options }
          { also~ protect~ code~ #1 = \PassOptionsToClass { #1 = {##1} } { article } }
      }
      {}
    \ekvifdefinedNoVal { class / options } { #1 }
      {
        \ekvdefinekeys { class / options }
          { also~ protect~ noval~ #1 = \PassOptionsToClass { #1 } { article } }
      }
      {}
  }

\ekvoUseUnknownHandlers*
\ekvoProcessLocalOptions { class / options }

\LoadClass{ article }
\end{filecontents}


\documentclass[12pt, a4paper, theme=bw]{main}
%\documentclass[12pt, a4paper]{article}

\usepackage{blindtext}

\begin{document}

\selectedfontsize

\blinddocument

\end{document}

另一种expkv方法是定义两个键集,一个是经过过滤的,另一个是未经过滤的。这种方法的优点是,您可以在选项解析之外使用键集(例如在普通\mainsetup宏或类似宏中)来更改文档中的后续选项。

\begin{filecontents}[overwrite]{main.cls}
\ProvidesExplClass{main}{2024-11-01}{1}{adhoc class for an MWE}

\RequirePackage{expkv-def,expkv-opt}

\newcommand*\selectedfontsize{10}

% if you also want to have a normal set for the options for a `\setup`-macro
% this allows that transparently to the user. If you don't need this the
% following four lines can be removed.
\ekvredirectunknown { class/options }
  { class/options/filtered, class/options/unfiltered }
\ekvredirectunknownNoVal { class/options }
  { class/options/filtered, class/options/unfiltered }

\ekvdefinekeys { class/options/filtered }
  {
     choice-store~     theme = \selectedoptions {bw, color, dark, draft }
    ,initial~          theme = color
  }
\ekvdefinekeys { class/options/unfiltered }
  {
    ,protect~ noval~   12pt  = \renewcommand*\selectedfontsize{12}
  }

% rules to forward an unknown key. Always forward to article, and if the key is
% known in the unfiltered set also use that (via the *documented* low level
% interface of building the key-macros directly, this is just a bit faster than
% using \ekvset{class/options/unfiltered}{#1} -- and since expkv supports
% expansion notation this could in theory make a difference in strange edge
% cases of key names)
\cs_new_protected:Npn \__projetmbc_forward_key:n #1
  {
    \PassOptionsToClass {#1} { article }
    \ekvifdefinedNoVal { class/options/unfiltered } {#1}
      { \use:c { \ekv@name { class/options/unfiltered } {#1} N } }
      {}
  }
\cs_new_protected:Npn \__projetmbc_forward_key:nn #1#2
  {
    \PassOptionsToClass {#1={#2}} { article }
    \ekvifdefined { class/options/unfiltered } {#1}
      { \use:c { \ekv@name { class/options/unfiltered } {#1} } {#2} }
      {}
  }
\ekvoUseUnknownHandlers \__projetmbc_forward_key:n \__projetmbc_forward_key:nn
\ekvoProcessLocalOptions { class/options/filtered }

\LoadClass{ article }
\end{filecontents}


\documentclass[12pt, a4paper, theme=bw]{main}
%\documentclass[12pt, a4paper]{article}

\usepackage{blindtext}

\begin{document}

\selectedfontsize

\blinddocument

\end{document}

5

  • 我提供了一个基本示例。感谢您的解决方案,但是有没有一种通用方法,可以处理更多选项l3


    – 

  • @projetmbc 也使用非 L3 包(所以要远离l3keys)?


    – 


  • 如果这不是一个太TeX明智的解决方案,我愿意接受它。:-)


    – 


  • 1
    @projetmbc 我会尝试同时提供两者。请注意,您的theme定义很差,因为 的值\l_keys_choice_tl可能会在整个文档中发生变化,您需要先将其展开,然后再将新值分配给\selectedoptions,请参阅我的编辑。


    – 

  • 扩展的观点很好。我明天会看看你的建议。感谢分享你的知识。


    – 

正如在中提到的clsguide,您可以使用内置命令传递未知选项:

\DeclareUnknownKeyHandler{%
  \PassOptionsToClass{\CurrentOption}{article}%
}

因此,任何您未申报的东西都可能被传递下去。

1

  • 评论已移至;请不要在此继续讨论。在此评论下发表评论之前,请先查看。不要求澄清或建议改进的评论通常属于答案 TeX 。继续讨论的评论可能会被删除。


    – 

原来使用的选项处理程序article是为更简单的情况而设计的,您只需在这里给它一点帮助即可。

如您所知,您的类已定义一个选项,您可以简单地将其从归类为未使用的theme选项列表中删除。每当包处理选项时,LateX 都会使用宏来执行此操作,并且只有在仍有项目时才会发出未使用的全局选项警告。 不幸的是,它不接受要删除哪个选项的参数,它只是使用,因此您需要在本地设置它:article\@use@ption\begin{document}\CurrentOption

\begin{filecontents}{main.cls}
\ExplSyntaxOn

\newcommand{ \selectedoptions } { color }

\keys_define:nn { class / options } {
  theme .choices:nn = {
    bw,
    color,
    dark,
    draft
  } {
    \renewcommand{ \selectedoptions } {
      \tl_use:N \l_keys_choice_tl
    }
  }
}



\ProcessKeyOptions[class / options]

\LoadClassWithOptions{ article } % How to pass all options 
                                 % except the theme one?



% if theme is in unknown option list remove it
\def\CurrentOption{theme}\@use@ption\let\CurrentOption\@empty



\ExplSyntaxOff
\end{filecontents}


\documentclass[12pt, a4paper, theme=bw]{main}
\begin{document}

\end{document}