The options package
Convenient key-value options for $\mbox{\LaTeX}$ package writers
Daan Leijen
2015-12-05

1. Introduction

The options package provides easy to use key-value options for $\mbox{\LaTeX}$ package writers. It has a similar interface as pgfkeys with path options but comes with more built-in data types and more convenient support for families and searching.

The main features of options are:

2. Overview

Defining options is very easy. As an example, we will make an \mybox command that takes named options. Our goal is that the user can invoke \mybox like:

\mybox{padding=1ex, border/width=0.4pt, font/style=italic}{text}

2.1. An example

We can define the options for our \mybox command as:

\options{
  /mybox/.new family,
  /mybox/padding/.new length      = \fboxsep,
  /mybox/border/width/.new length = \fboxrule * 2,
  /mybox/border/color/.new color, 
  /mybox/font/style/.new choice   = {normal,italic,small-caps},
}

The options are all specified as a path where names that start with a dot, like .new length are called handlers. In this case, the handlers create the options for our box command. To parse the options passed by the user, we use the same \options command:

\newcommand\mybox[2]{%
  {\options{/mybox,#1}%
   \myboxdisplay{#2}%
  }%
}

Here we start the option list with /mybox which makes that the default path (because it was declared with .new family) so the user can give relative option names instead of prefixing all options with /mybox/. Options are always set local to the current group.

Finally, we can use \option{option} to get the value of an option. This command expands directly to the command name (i.e. \optk@option) and is very efficient. So, our implementation for displaying our box may look like:

\newcommand\myboxdisplay[1]{%
  \setlength\fboxsep{\option{/mybox/padding}}%
  \setlength\fboxrule{\option{/mybox/border/width}}%
  \colorlet{currentcolor}{.}%
  \color{\option{/mybox/border/color}}%
  \fbox{%
    \color{currentcolor}%
    \ifcase\option{/mybox/font/style/@ord}\relax
    \or\itshape
    \or\scshape
    \fi
    #1%
  }%
}

Here, instead of using the font/style directly, we use the automatically generated font/style/@ord that gives the ordinal of the choice on which we can match more efficiently. Here is our new command in action:

Here is a \mybox{padding=1ex, border/color=blue!70, font/style=italic}{boxed} text.
Here is a \mybox{padding=1ex, border/color=blue!70, font/style=italic}{boxed} text.

There are many ways to refine our implementation, for example, we can use styles to make it easier to set multiple options at once:

\options{ /mybox/border/normal/.new style* = 
            {/mybox/border/width=0.4pt, /mybox/border/color=black }}

The * after the style signifies that this option does not expect an argument, and we can use it as:

A \mybox{border/normal}{normal} border.
A \mybox{border/normal}{normal} border.

Another improvement is in our choice definition for the font. Instead of using a case over the choice ordinal, we can set the required font command directly as the value of the choice options, we show this in Section 3.1.

2.2. Paths and searches

The option paths are mainly used to prevent name clashes between different packages but they can also be used for searches. In particular, we can specify for any path that if a sub-path cannot be found, we should look under another path. Suppose we define a new \fancymybox command that takes some extra options. In that case, we would like to re-use the /mybox options and look for any undefined options under /mybox instead:

\options{ 
  /fancymybox/.new family = {/mybox}, % search also /mybox
  /fancymybox/border/radius/.new length,
  /fancymybox/rounded/.new style = [5pt]{/fancymybox/border/radius = #1},
}

Note how the rounded style takes an argument which is defaulted to 5pt. In the .new family declaration, we can provide a list of search paths: here we just gave /mybox such that any options not found under /fancymybox will be looked up under /mybox:

\options{/fancymybox, rounded=10pt, border/normal} 

In the previous sample, /mybox/border/normal is invoked. In general, we can add comma separated search paths either in a .new family declaration or using the .search also handler. Search paths can be added to any path and will apply recursively. For example, if we set:

\options{
  /a/foo/x/.new cmd* = x,
  /b/bar/y/.new cmd* = y,
  /c/a/.search also = {/a/foo},
  /c/b/.search also = {/b},
  /c/.new family    = {/a,/b},
}

Then all of the following options are resolved:

\options{ /c/foo/x, /c/a/x, /c/b/bar/y, /c/bar/y}
\options{ /c/foo/x, /c/a/x, /c/b/bar/y, /c/bar/y}

Note that the options package will even search if an absolute path is given, and always searches with the relative sub-path.

This is important for modularity since it allows us for example to combine options of different sub packages. For example, if I want to handle options under /package A and /package B together, I can just define a new family:

\options{ /packageAB/.new family = {/package A,/package B}}

and start processing options using \options{/packageAB,..}. Even when the user now uses absolute paths like /packageAB/name the option search will route it automatically to the sub packages.

Also note that for efficiency, the basic \option command does not search and always expands directly to the command name. Therefore, in implementation code we still need to use \option{/a/foo/x} and cannot use \option{/c/foo/x} for example.

2.3. Handling unknown options

It is possible to handle only options under some path and ignore any unknown options. For example, give our previous options, we can only process the options under /c/a as:

\options{/options/collectunknown, /c/a/.cd, x, bar/y }\\
\options{/options/remaining/.show}
\options{/options/collectunknown, /c/a/.cd, x, bar/y }\\
\options{/options/remaining/.show}

Here we used the /options/collectunknown style to signify that we want to collect unknown options into the /options/remaining list. We used the .cd handler to change the default path to /c/a such that only x is found (as /a/foo/x) but the bar/y option is put in the remaining list.

Any remaining options can be processed eventually using the \optionswithremaining command:

\optionswithremaining{/c/b/.cd}
\optionswithremaining{/c/b/.cd}

The command takes a list of options that are processed before the options in the remaining list. It is allowed to pass in /options/collectunknown right away again to collect any new remaining options.

The /options/remaining is list is only cleared when using the /options/collectunknown style. This can be useful to collect unknown options using multiple passes of \optionsalso.

Besides using the remaining list, you can also define general @unknown handlers on any path. When an option not found, the library looks bottom-up for @unknown handlers and invokes it with the path and its argument if found. The general handler /@unknown will raise a package error.

You can customize this behavior by installing an @unknown handler yourself:

\options{ 
  /mybox/@unknown/.new cmd 2 = 
    {I don't know option ``#1'' (=#2).},
  /mybox/silly = hi 
}
\options{ 
  /mybox/@unknown/.new cmd 2 = 
    {I don't know option ``#1'' (=#2).},
  /mybox/silly = hi 
}

As an aside, note that we needed to put braces around @unknown handler since it uses the = character.

3. Defining options

3.1. Basic data types

option/.new value = [default]initial value

Defines a new option that with an initial value. A value option just contains the value that was provided by the user. The default value is optional. If it is given, it is used when the user does not provide an argument when using this option.

option/.new toggle [= bool]

Define a new toggle. These are boolean values and have the advantage over the standard \newif (see Section 3.3) that they only require one macro instead of three. The initial value (if not given) is false and the default value is always true. A toggle can be tested using \iftoggle{toggle}.

\options{/test/condition/.new toggle}
\options{/test/condition}% default sets to true
Toggle is \iftoggle{/test/condition}{true}{false}
\options{/test/condition/.new toggle}
\options{/test/condition}% default sets to true
Toggle is \iftoggle{/test/condition}{true}{false}

Besides assigning a new value of true or false, you can also flip a condition as:

\options{/test/condition/.flip}
option/.new choice = [default]{choice1[=value1],,choicen[=valuen]}

Defines a new choice option where the user can provide choice1 to choicen as arguments. The initial value is always choice1. We always need to enclose the choice list with braces to distinguish the comma used to separate the choices from the comma used to separate the options. For convenience, the new choice handler also defines option/@ord that contain the ordinal of the current choice (starting at 0), and the option/@name that contains the current choice name. The ordinal is a number and can be tested efficiently using ifcase and ifnum. For example:

\options{/test/program/.new choice={latex,xelatex,luatex,pdftex}}
\options{/test/program = xelatex}
\noindent\options{/test/program/.show}

% case on ordinal
\ifcase\option{/test/program/@ord}%
  latex
\or
  xelatex
\else
  other
\fi

% ifnum on ordinal
\ifnum\option{/test/program/@ord}<2\relax
  latex or xelatex
\else
  luatex or pdftex
\fi
\options{/test/program/.new choice={latex,xelatex,luatex,pdftex}}
\options{/test/program = xelatex}
\noindent\options{/test/program/.show}

% case on ordinal
\ifcase\option{/test/program/@ord}%
  latex
\or
  xelatex
\else
  other
\fi

% ifnum on ordinal
\ifnum\option{/test/program/@ord}<2\relax
  latex or xelatex
\else
  luatex or pdftex
\fi

Another powerful feature is to define the values that each choice implies. By default this is the name of the choice but we can assign anything else. For example, for our \mybox command, we could have specified the font style as:

\options{/test/font/style/.new choice=
            {normal={}, italic=\itshape, small-caps=\scshape}}

The value of \option{/test/font/style} is not the choice name now, but the command that we assigned to it. We can now use the option directly instead of doing a case on the @ord value:

\options{/test/font/style=italic}
This is {\option{/test/font/style}in italics}.
\options{/test/font/style=italic}
This is {\option{/test/font/style}in italics}.
option/.new list [=[default]{elem1,,elemn}]

Define a new comma separated list. The initial value if not given is the empty list. There are various operations to work with lists:

\options{/test/list/.new list = {banana,milk,eggs}}
\optionlistdo{/test/list}{%
  Element ``\textsf{#1}''.
  \ifstrequal{#1}{milk}{\listbreak}{}%
}
\options{/test/list/.new list = {banana,milk,eggs}}
\optionlistdo{/test/list}{%
  Element ``\textsf{#1}''.
  \ifstrequal{#1}{milk}{\listbreak}{}%
}

There are also two special handlers for manipulating lists in the options,

For example,

\options{/test/list 2/.new list = {banana}}
\options{/test/list 2/.push = milk, /test/list 2/.show}
\options{/test/list 2/.new list = {banana}}
\options{/test/list 2/.push = milk, /test/list 2/.show}
option/.new length [= [default]dimexpr]

Defines a new length option option. This option stores its value in a new length register, and its value be used directly where a length is expected, e.g., \hspace{\option{option}}. If no initial value is given, the initial length is 0pt. The user can assign any length expressions (dimexpr), e.g., \options{option = 1pt + \fboxsep}.

option/.new dim [= [default]dimexpr]

Defines a new dimension option option. This option stores its value as an unevaluated dimexpr, and its value be used directly where a dimension is expected, e.g., \hspace{\option{option}}. The main difference with a length option is that a dimension option is not evaluated at the time the key is assigned, but rather when it is used. This may be important when relying on the contents of other registers. For example, if we declare:

\setlength\fboxrule{1pt}
\options{/test/width/.new dim=\fboxrule,/test/length/.new length=\fboxrule}
\setlength\fboxrule{10pt}

This will show 10pt for the width, but 1pt for the length:

\options{/test/width/.show}\\\options{/test/length/.show}
\options{/test/width/.show}\\\options{/test/length/.show}
option/.new num [= [default]numexpr]

Defines a new number option option. The assigned value is evaluated as a numexpr. Can be used directly in any context that expects a numexpr. For example:

\options{/test/num/.new num = 2+4}
Is it 6? \ifnum\option{/test/num}=6\relax Yes.\else No!\fi
\options{/test/num/.new num = 2+4}
Is it 6? \ifnum\option{/test/num}=6\relax Yes.\else No!\fi
option/.new glue [=[default]glueexpr]

Defines a new glue option. Can be assigned any valid glue expression and used in any context that expects a glue expression. If no initial value is given, 0pt is used.

3.2. Command options

The options in this section are not values by themselves but are only invoked for their side effect.

option/.new family [= search list]

Defines a new family, this is a shorthand for

option/.search also = search list,
option/.new style*  = {option/.cd},
option/.type        = family
option/.new style = {options}
option/.new style* = {options}

Defines a new style; when invoked it will also set the specified options using \optionsalso. The .new style* variant does not take an argument itself. The plain .new style can use #1 for the argument value, e.g.

/border/width/.new style = {/border/top/width=#1,/border/bottom/width=#1}
option/.new cmd = [default]code
option/.new cmd* = code

Define a new command that is invoked when the options is given. .new cmd* takes no argument while .new cmd takes a single argument.

Use of these options is generally discouraged and if you can you should try to use a data type directly together with \option{option} commands.

\options{ /test/cmd/.new cmd = You said ``#1'', 
          /test/cmd = hi,
}
\options{ /test/cmd/.new cmd = You said ``#1'', 
          /test/cmd = hi,
}
option/.new cmd 0 = code
option/.new cmd 1 = [default]code
option/.new cmd 2 = [default]code
option/.new cmd 3 = [default]code
option/.new cmd 4 = [default]code

Define commands with multiple arguments, where .new cmd 0 is equal to .new cmd* and .new cmd 1 to .new cmd. Each argument needs to be enclosed in braces and if not all arguments are given, they will be empty.

\options{
  /test/cmd2/.new cmd 2 = [{x}{y}]{I got (#1,#2)\\},
  /test/.cd,
  cmd2,
  cmd2 = hi,
  cmd2 = {hi}, 
  cmd2 = {{hi}}, 
  cmd2 = {hi}{there},
}
\options{
  /test/cmd2/.new cmd 2 = [{x}{y}]{I got (#1,#2)\\},
  /test/.cd,
  cmd2,
  cmd2 = hi,
  cmd2 = {hi}, 
  cmd2 = {{hi}}, 
  cmd2 = {hi}{there},
}

Note how {hi} and hi had the same effect since the options processing peels of a single layer of braces. If we want to preserve braces we need to double up.

option/.new cmd tuple = code
option/.new cmd triple = code

These are variants of .new cmd 2 and .new cmd 3 that take exactly 2 or 3 arguments separated by commas.

option/.new cmdx = {pattern}{postfix}{code}

This defines a plain $\mbox{\TeX}$ command with the specified pattern. Also, when invoking the command, it will append postfix to the argument which is often necessary to ensure the command pattern will always match. For example, here is how we defined .new cmd tuple which matches with exactly 2 arguments:

/handlers/new cmd tuple/.new handler/.defaults = \optionsalso{
  #1/.new cmdx = {##1,##2,##3}{,,}% match comma separated
    {\ifstrequal{##3}{,}%
       {#2}%
       {\optionerror{#1}{expecting a 2 argument tuple}}%
    },
  #1/.type=cmd tuple,
}

3.3. Using existing definitions

Use these declarations if you need to work with existing definitions and are not able to declare your own using the data types in Section 3.1.

option/.is if = if name

Declare a new option that directly sets a defined $\mbox{\LaTeX}$ if declared with \newif. The name should not start with the if part, e.g.

\options{ /twocolumn/.newif = @twocolumn }
option/.is counter = counter name

Declare an option that directly sets or gets a $\mbox{\LaTeX}$ counter declared with \newcounter. You can use .inc, .step, and .refstep operations on counters.

option/.is def = [default]macro name

Declare an option that directly sets or gets a defined definition. This is basically equivalent to the \define@key operation of the keyval package.

\def\mytest{}
\options{ 
  /mytest/.is def = \mytest,
  /mytest = ``hi'',
}
\mytest{} there.
\def\mytest{}
\options{ 
  /mytest/.is def = \mytest,
  /mytest = ``hi'',
}
\mytest{} there.
option/.is edef = [default]macro name

Same as the .is def but will use \edef to redefine the macro definition.

4. Option transformers

4.1. Operations on option paths

path/.cd

Change the directory to make path the default option prefix.

option/.show

Show the current option (in the document)

option/.typeout

Show the current option in the console.

4.2. Operations on arguments

option/.expand once = value

Expand the argument once (using \expandafter)

option/.expand twice = value

Expand the argument twice.

option/.expanded = value

Fully expand the argument. Defined as:

/handlers/expanded/.new transformer =
  \protected@edef\optionvalue{\optionvalue}
list option/.push = element

Push an element on the end a list option.

list option/.concat = list

Concatenate a list to the end of a list option.

option/.inc [= numexpr]

Increment a counter or number (.new num) with the given amount (or 1 if no argument is given)

counter option/.step

Step a counter option by 1.

counter option/.refstep

‘Ref step’ a counter option by 1.

4.3. Predefined options

/options/exec = code

Directly execute the provided code

\options{ /options/exec=hi there}
\options{ /options/exec=hi there}
/options/ignoreunknown [= bool]

If set to true, this will ignore any subsequent unknown options. Such ignored options will be added to the /options/remaining list.

/options/allowsearch [= bool]

If set to false, it will no longer search along search paths provided by .search also or .new family.

/options/unknownwarnonly [= bool]

If set to true, this only issues a warning when finding an unknown option (instead of raising an error).

/options/remaining [= options]

A list of remaining options to be processed. This list should not be used directly but mostly in conjunction with /options/collectunknown and \optionswithremaining.

/options/collectunknown

A style. If given, it will clear the /options/remaining list and set /options/ignoreunknown to true.

5. Option commands

This section defines the command macros available on option values.

5.1. Processing options

\options{options}

Process the options list. This is a comma separated list of option [=value] elements. Both the option and value are trimmed of whitespace (at the end and start). The list may have empty elements and also end or start with commas. When extracting the value a single layer of braces is removed – this is done such that the user can specify values that contain commas or equal signs themselves. Any option defines are always local to the current group.

When invoking \options the initial path is always the root (/) and the flag /option/ignoreunknown is false, and /option/allowsearch is true.

\optionsalso{options}

Just like \options except it will use the current default path and option flags.

\optionswithremaining{options}

Like \options but also processes any options in the list /options/remaining after processing options. Will start by making a copy of the /options/remaining list so you can call /options/collectunknown in options to immediately start collecting further remaining options.

\options@ProcessOptions{family}

Call this in a package (.sty) or class (.cls) file to handle the options passed to it. In a package file it will also consider the known options that are passed to the class besides the options passed to it directly. The family should be the root path for your options. For example,

\NeedsTeXFormat{LaTeX2e}[1995/12/01]

\ProvidesPackage{mypkg}[2015/01/01,By Me]
\RequirePackage{options}

\options{
  /mypkg/.new family,
  /mypkg/columns/.new num = [1]{2},
  /mypkg/10pt/.new cmd* = \typeout{use 10pt font size},
}
\options@ProcessOptions{/mypkg}

Others can now pass options to your package as:

\usepackage[10pt,columns=2]{mypkg}

5.2. Using options

\option{option}

This uses the current value of option. It directly expands to the command \optk@option and is very efficient. Usually that command contains the value of the option, but sometimes it expands to something different depending on the type of the options. For example, it may be a length register.

\letoption{option}\macro

If you need to use an option in loop or need careful expansion control, it is sometimes more efficient to \let bind the option value into a macro, e.g.

\letoption{/my/option}\myoptionvalue
\edefoption{option}\macro

Same as \letoption but fully expands the current value.

5.3. Testing options

All these test take true and false branch as final arguments.

\ifoptiondefined{option}{true}{false}

Is the option defined?

\ifoptionvoid{option}{true}{false}

Is the option undefined or is its value blank (empty or spaces)?

\ifoptionblank{option}{true}{false}

Is the option value blank (empty or spaces)?

\ifoptionequal{option}{value}{true}{false}

Is the option value equal to the provided value?

\ifoptionanyof{option}{list}{true}{false}

Does the option value occur in the comma separated value list list?

\ifoptiontype{option}{type}{true}{false}

Does the option have type type?

\ifoptionnil{list option}{true}{false}

Is list option value empty?

\ifoptioniscode{option}{true}{false}

Is this option just an action? i.e. it has no associated value.

5.4. List options

These are utility functions for list options.

\optionlistdo{list option}{action}

Perform action on every element of the current value of list option. The element is available as #1 in action and the iteration can be stopped by ending with \listbreak.

\letoptionlist{list option}\macro

\let bind the current list value as a list of comma separated values in \macro. This is sometimes needed since the internal representation of lists uses another representation internally (in order to contain commas itself).

\ifoptioncontains{list option}{true}{false}

Check if a list option contains a certain element.

5.5. Showing options

\optionshow{option}

Show the value of option.

Ignore unknown = \optionshow{/options/ignoreunknown}
Ignore unknown = \optionshow{/options/ignoreunknown}
\optionshowall[bool]

Show all the options that are defined. If you pass true in the optional argument it also shows all the internal values under the /handlers/ which can be a big list.

\optionshowpath{option}

Show all the options defined under a certain path. Useful for debugging:

\optionshowpath{/mybox}
\optionshowpath{/mybox}

6. Advanced topics

This section gives an overview of more advanced mechanisms, like defining handlers and new data types.

6.1. Handlers

Names that start with a dot are handlers. These are commands that are called with the current option path and argument, and are used for example to declare new options (e.g. .new choice), to change the environment (e.g. .cd), or to transform the argument (e.g. .expanded).

You can define your own handler .name by adding a command option under /handlers/name. For example, let's make a handler that transforms a color argument from a HTML format #XXXXXX to a named color:

\options{%
  /handlers/htmlcolor/.new transformer = {%
    % we need to change the \optionvalue 
    % #1 = current path, #2 = current \optionvalue
    \definecolor{#1/@color}{HTML}{#2}%
    \def\optionvalue{#1/@color}%
  }
}

We can use our new handler directly with our previous box command:

A \mybox{border/color/.htmlcolor = 800080}{purple} box.
A \mybox{border/color/.htmlcolor = 800080}{purple} box.

There are various kinds of handlers:

6.2. Defining new data types

Handlers are also used to define the standard data types and can be used to define new data types yourself. Here is for example how the value data type is defined:

\option{
  /handlers/new value/.new handler = []\optionsalso{%
    #1/.new cmd = \option@set{#1}{##1},
    #1/.type    = value,
    #1/.initial = {#2},
  },
}

The .new handler receives the current path (#1) and the initial value (#2). The default value provided at definition time is empty ([]). When a user defines a new value, we simply set more options based on the path. When the defined option is set, the .new cmd is called with the argument (##1) and we use that set the actual option value directly: \option@set{#1}{##1}. Usually, we do some additional processing here, for example, for choice values we would check if the choice is valid.

6.3. Single option values

Finally, we can also specify handlers that are invoked when the option name starts with special character. This allows you to handle, say, a quoted value like "Georgia" as a shorthand for /font/family=Georgia. As an example, we will handle options that start with a bang (!) as an HTML color. Handling a special character char can be done simply by installing a handler under /handlers/special/char:

\options{%
  /handlers/special/!/.new handler = {%
    \optionsalso{ /mybox/border/color/.expanded/.htmlcolor = \@gobble#2 }%
  },
}
A \mybox{!008080}{teal} box.
\options{%
  /handlers/special/!/.new handler = {%
    \optionsalso{ /mybox/border/color/.expanded/.htmlcolor = \@gobble#2 }%
  },
}
A \mybox{!008080}{teal} box.

Here we used chaining to first expand the argument (.expanded) and then invoking the .htmlcolor handler. We needed to use \@gobble to remove the exclamation mark from the provided argument (i.e. #2 will be equal !008080).

6.4. Search algorithm

When \options parses the options and finds an option and argument, it will assign argument to \optionvalue and search for the option:

  1. If the option name is not absolute (i.e. does not start with /) then prepend the default path (\opt@defaultpath).
  2. If the option exist we are done; invoke option/@code.
  3. Otherwise, if the option has handlers .name look for the first handler and invoke /handlers/name/@code if it exists with \option@handlerpath set to the option path.
  4. If there is no handler, perform a search bottom-up through the sub-paths of the option path. If path/@searchalso exists where option = path/subpath, then invoke the search also handler with subpath as its argument, and keep searching recursively.
  5. If we still don't find the option, search bottom-up through the sub-paths of the option path for /@unknown handlers and invoke it the first one that is found. If none are defined, this will invoke /@unknown with option as its path which will raise a package error.

This may seem like quite a bit of work but there has been much attention to efficiency. For example, we can match in a single $\mbox{\TeX}$ definition for handlers since we evaluate chained handlers from left to right. Also, we never search for handlers but just match directly etc.

7. Advanced options and commands

This section introduces option handlers and commands that are useful when extending the options package itself.

7.1. Transforming an option

These operations transform an option itself. Usually only used when defining new handlers but should not be necessary in general.

option/.expands

When applied to an option it ensures the option argument is always fully expanded (as if using .expanded at every invocation).

option/.default = default value

Set the default argument if the user does not provide one. The default argument is stored in option/@def.

option/.initial = [default]initial

Set the default argument (if provided) and the initial value of an option. The initial value is stored in option/@ini and used when invoking .reset.

option/.reset

Sets the option back to its original value.

option/.undef

Undefine an option. This can be used to redefine existing options of other packages.

option/.search also = search paths

Specify additional search paths for the given option as explained in Section 2.2.

option/.type = type

Specify the type of an option. This is used for example by the default .show function to display values correctly. The type can be queried using \letoptiontype, \ifoptiontype, and \ifoptioniscode.

7.2. Compatibility

Some compatibility functions for pgfkeys users.

option/.is family

Equivalent to .new family (but without a search argument)

option/.code = code

Equivalent to .new cmd.

option/.style = style

Equivalent to .new style.

7.3. Special paths

/handlers/name = code

Define such path to install a new handler name. The code takes two arguments (the current path and argument) and should be defined using .new handler, .new operation, or .new transformer.

/handlers/show/type = code

Provide custom show functions for options of type type. The code takes a single argument, namely the option name. For example:

/handlers/show/length/.new cmd = \option@showvalue{\the\option{#1}}

Using \option@showvalue ensures option values are shown consistently.

/handlers/special/char = code

Provide handlers for options that start with a special character, see Section 6.3.

7.4. Miscellaneous commands

\optionerror{option}{message}

Raise a package error for option with a certain message.

\optionwarning{option}{message}

Output a package warning for option with a certain message.

\optionerror@expect{option}{expecting}

Raise a package error for option with an expecting message.

\letoptiontype{option}\type

Bind the type of an option to \type. Properly takes care of command options.

7.5. Setting options directly

\optionname{option}

Expand to the internal name of an options, i.e. optk@option.

\option@set{option}{value}

Set the \optk@option macro to value.

\option@eset{option}{value}

Set the \optk@option macro to a single expansion of value (using \expandafter).

\option@def{option}pattern{code}

Define the \optk@option as a macro that takes parameters defined in pattern.

\option@let{option}\macro

\let the \optk@option macro to \macro. This can be more efficient than \options@set if the value happens to be a macro itself.

7.6. Invoking an option directly

Internally, every option option comes with option/@code that is invoked when an option is set with the \optionvalue containing the current argument. The \optionvalue will equal \optionnovalue when no argument is given. A single expansion of \optionvalue will yield the exact value provided by the user (minus one layer of outer braces).

The option@code usually starts with an argument check. If no argument is expected (the * options on commands) and error is raised if there was an argument. If an argument is expected, and there is no argument, the \optionvalue is set to the default argument (in option/@def). If there is no default, an error is raised.

\option@invoke{option}{value}
\option@xinvoke{option}{value}
\option@einvoke{option}{value}
\option@invokedefault{option}

Various ways to invoke a option handler directly. The \option@invoke macros directly call option/@code. The einvoke does a single expansion of its argument, and xinvoke does a full expansion. The \option@invokedefault call the option/@code with \optionvalue set to \optionnovalue.

7.7. Defining a new code value

option/.new code = [default]code

Define a new option/@code handler. This is invoked when the option option is set with the macro \optionvalue set to the argument. This will equal \optionnovalue if no argument was provided. This is the most primitive command handler and it is recommended to use .new cmd instead.

option/.new code* = code

Just like .new code but checks that option did not get an argument, i.e. that \optionvalue equals \optionnovalue.

7.8. Defining new handlers

To define handlers, we cannot use .new code since the handler needs access to the option path it is handling. The .new handler gets also passed this path as an argument.

/handlers/name/.new handler = [default]code

Define a new handler name. If default is provided, this will be the default argument for the newly defined handler. The code takes two arguments, the option path (#1) when .name is invoked, and the provided argument (#2). For example:

/handlers/new style*/.new handler = \optionsalso{
  #1/.new cmd* = \optionsalso{#2},
  #1/.type     = style,
},
/handlers/name/.new handler* = code

Similar to .new handler but for handlers that take on argument.

option/.new transformer = [default]code

A form of handler that transforms a provided argument. Should redefine \optionvalue which will be passed to the next handler. Takes the option path and current option argument as arguments. For example:

/handlers/expanded/.new transformer=\protected@edef\optionvalue{\optionvalue}
option/.new operation = [default]code

A form of handler that is invoked for its side effect. Takes the option path and current option argument as arguments. In contrast with a transformer after this handler, the original option will not be set and it only chains with other handlers. For example:

/handlers/concat/.new operation = \option@concat{#1}{#2}
option/.new operation* = code

Same as .new operation but for operations that take no argument.

option/.new cmd transformer = [default]code

A transformer that changes the option value of a command. For example:

/handlers/expands/.new cmd transformer={\protected@edef\optionvalue{\optionvalue}}

7.9. Defining options directly

\optionprependcode{option}{code}

Add some code to option/@code right after the argument check. This is used for example by the .expands handler to always expand the \optionvalue.

\optionnewcode{option}{code}

Define a new option/@code handler that expects an argument.

\optionnewcode*{option}{code}

Define a new option/@code handler that expects no argument.

\optionnewhandler{option}{code}

Define a new option/@code handler for defining a handler that will take an argument. This will insert some code that calls code with the option path that is handled in #1 and the argument in #2.

\optionnewhandler*{option}{code}

Define a new option/@code handler for defining a handler that takes no argument. This will insert some code that calls code with the option path that is handled in #1 and the argument in #2.

8. Performance

There are some performance numbers of the options, pgfkeys and xkeyval packages. To test the performance, we performed 100.000 invocations of \options, pgfkeys, and setkeys respectively. For each library, we defined two options in the family bar and foo, as:

\options{
  /bar/bar-test/.new value = hi,
  /foo/foo-test/.new value = world,
  /foo/.new family={/bar},
}

We then tested two queries. The first one is simple and set an option that can be directly found:

\options{/foo,foo-test=a test}

and a complex one that needs a one-level search:

\options{/foo,bar-test=a test}

We measured the time of a run without any testing (the baseline) and then ran each benchmark 100.000 times and picked the best time out of three runs, subtracting the baseline time. The benchmarks were run on an Intel Core2 Quad CPU @ 3ghz with 4Gb memory running XeLaTeX 3.1415926 from TeX Live 2013/W32TeX. The results were:

simple
package relative total 100.000 reps.
options 1.0x 3.41s 25ms per 1000
pgfkeys 1.3x slower 4.34s 34ms per 1000
xkeyval  8.1x slower  27.77s  268ms per 1000
complex (one level search)
package relative total 100.000 reps.
options 1.0x 5.32s 44ms per 1000
pgfkeys 2.1x slower 11.10s 101ms per 1000
xkeyval  5.7x slower  30.34s  294ms per 1000

So both options and pgfkeys are quite a bit faster than xkeyval, and options performs quite well when searches are involved. We also tested against the basic keyval package but this is a bit tricky since keyval does not support features like searching in the first place. It was about 1.5 times faster on simple queries though.

Created with Madoko.net.