1

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 at 16:15
2

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.