2

As stated in a previous question, I wish to switch my class written with LaTeX2e to the expl3 syntax. I will try to do this by asking a series of questions, collecting code examples, making the transition gradually (quoting @PhelypeOleinik). In this way, I'll be able to reduce the risk of violating expl3 conventions.

I begin with the initialization part, for example processing options and loading classes. What is the proper way to achieve the effect of the following code with expl3 syntax?

% (Package initialization)
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{packagename}
    [2021/03/09 ...]
\RequirePackage{kvoptions}
\SetupKeyvalOptions{%
family = packagename,
prefix = packagename@
}
\DeclareBoolOption[false]{off}

\ProcessKeyvalOptions*\relax
% (Class initialization)
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{classname}
    [2021/03/09 ...]

\RequirePackage{kvoptions}
\RequirePackage{etoolbox}

\SetupKeyvalOptions{
    family = classname,
    prefix = classname@,
    setkeys= \kvsetkeys,
}
\DeclareStringOption[some-string]{option-name}
\DeclareStringOption{option-with-no-default-value}
\DeclareVoidOption{other-string}{\kvsetkeys{classname}{option-name = other-string}}

\DeclareDefaultOption{\PassOptionsToClass{\CurrentOption}{\classname@baseclass}}
\ProcessKeyvalOptions*\relax

\LoadClass{\classname@baseclass}

Maybe there're better ways with expl3, I'm just including these LaTeX2e code as a comparison.

1
  • 3
    you are starting at the wrong place. Most of these commands have either no expl3 version or are (like the keys) not trivial to replace. Mar 9, 2021 at 16:15

1 Answer 1

4

Here's something to give you a start. That part of the code is mostly option handling, so I suggest you take a look at the l3keys module documentation which covers the available key handlers. I'll explain very briefly, because there are a lot of small concepts here in this piece of code.

First we start by declaring the format needed, and loading l3keys2e for the class option parsing:

\NeedsTeXFormat{LaTeX2e}
\RequirePackage{l3keys2e}

then we declare the class (or package):

\ProvidesExplClass{classname}
  {2021/03/09} {1.0} {My class to learn expl3}

The command \ProvidesExpl<thing> already does \ExplSyntaxOn for you, and takes care of switching it off at the end of the class/package, so this is the proper way to start an expl3-based class.

Now define a constant string (constant means it shouldn't be changed with \str_set:Nn or something like that) to hold the base class name. The prefix \c_... indicates it is a constant (l is local and g is global), and the suffix ..._str means it's a string variable:

\str_const:Nn \c__classname_base_class_str { book }

Now we'll define some options for your class called classname:

\keys_define:nn { classname }
  {

First what kvoptions calls a string option (one which takes a value):

    % \DeclareStringOption
    , option-name .tl_set:N = \l__classname_option_name_tl
    , option-name .initial:n = { some-string }

you use it as option-name and the value given to it will be stored in \l__classname_option_name_tl (a local tl variable). The initial value is some-string. One peculiarity of \keys_define:nn is that it already creates the variables for you, so you don't need to do \tl_new:N \l__classname_option_name_tl beforehand (but it doesn't hurt either).

Now what kvoptions calls a void option, one which just executes a piece of code:

    % \DeclareVoidOption
    , other-string .code:n =
        { \keys_set:nn { classname } { option-name = other-string } }

When used, the other-string option will do the same as option-name=other-string.

Now a boolean option, whose initial value is false:

    % \DeclareBoolOption
    , off .bool_set:N = \l__classname_off_bool
    , off .initial:n = { false }

And finally, you tell l3str how to handle unknown options:

    % \DeclareDefaultOption
    , unknown .code:n =
        {
          \iow_term:x
            {
              Passing~option~\CurrentOption \c_space_tl to~
              \str_use:N \c__classname_base_class_str
            }
          \PassOptionsToClass { \CurrentOption } { \c__classname_base_class_str }
        }
  }

It will print "Passing option to " in the terminal. \iow_term:n writes to the terminal, and \iow_term:x does an "exhaustive expansion" of the argument before passing it to the underlying n variant.

Now process the options and load the base class:

\ProcessKeysOptions { classname }
\LoadClass { \c__classname_base_class_str }

As an example code, you can use \bool_if:NTF to test a boolean variable:

\bool_if:NTF \l__classname_off_bool
  { \iow_term:n { off~true } }
  { \iow_term:n { off~false } }

and again \iow_term:x to write to the terminal:

\iow_term:x { Value~of~option-name:~\tl_use:N \l__classname_option_name_tl }

Putting it all together:

\begin{filecontents}{classname.cls}
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{l3keys2e}
\ProvidesExplClass{classname}
  {2021/03/09} {1.0} {My class to learn expl3}

\str_const:Nn \c__classname_base_class_str { book }

\keys_define:nn { classname }
  {
    % \DeclareStringOption
    , option-name .tl_set:N = \l__classname_option_name_tl
    , option-name .initial:n = { some-string }
    % \DeclareVoidOption
    , other-string .code:n =
        { \keys_set:nn { classname } { option-name = other-string } }
    % \DeclareBoolOption
    , off .bool_set:N = \l__classname_off_bool
    , off .initial:n = { false }
    % \DeclareDefaultOption
    , unknown .code:n =
        {
          \iow_term:x
            {
              Passing~option~\CurrentOption \c_space_tl to~
              \str_use:N \c__classname_base_class_str
            }
          \PassOptionsToClass { \CurrentOption } { \c__classname_base_class_str }
        }
  }
\ProcessKeysOptions { classname }
\LoadClass { \c__classname_base_class_str }

% example code:
\bool_if:NTF \l__classname_off_bool
  { \iow_term:n { off~true } }
  { \iow_term:n { off~false } }

\iow_term:x { Value~of~option-name:~\tl_use:N \l__classname_option_name_tl }
\end{filecontents}

\documentclass[10pt,a4paper,option-name=VALUE]{classname}
\begin{document}
\end{document}
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .