xkeyval and the 9 arguments

LaTeX_logo.svg
Recently I’ve been playing D&D with some friends. Tired of writing my small homebrew adventures in OpenOffice I turned into my old friend \LaTeX.

Seemed to me that a D&D adventure was specific enough to have its own document class, like article, book etc. And could not find it online, so right now I am writing one.html select jquery set selected

In this article I will explai how to write \LaTeX commands accepting more than the 9 default parameters.

\LaTeX as a user is easy and powerful, lets you use different classes without problem and produces clean documents. I love it and it is beautiful.

As a class developer, as I just discovered, is a pain in the ass.

I got a lot of problems while writing my class, and I had to throw away some ideas that just didn’t work. Now, we’ll see how to create commands with named parameters.

Objective

\newcommand \newenvironment tags are nice and all, but are limited to 9 non-named arguments.

Being a D&D adventure document there are going to be monster tables, so I want to produce something like:

\begin{ddmonster}
[name=Hobgoblin,level=3,hp=50,xp=150,
role=Soldier,
keywords=natural humanoid,
initiative=7,Speed=5,
AC=20,Fort=18,Refl=16,Will=16]


%a lot more of stuff here. Powers, descriptions, stats

\end{monster}

I just want a basic definition as a set of named arguments, the rest of the monster will be defined with commands.

Easy as pie

Creating a LaTeX command is quite easy, also the Wikibooks guide helps a lot with it. We just have to use the \newcommand macro and populate it

\newcommand{name}[num]{definition}

So for example,

\newcommand{\myprintname}[1]{
 my name is #1
}
\myprintname{Jordi}

If I want my name and my surname,

\newcommand{\myprintname}[2]{
 my name is #2 , #1 #2
}
\myprintname{James}{Bond}

And I could keep going and going with parameters.


NOTE from the future:
There was a Typo in this post. Discovered (and commented) by Hikari Boulders on the number of parameters.
This is now fixed 😀


The bad news

  • The bar closes at 9. No more arguments for you.
  • More arguments would be ugly, so I agree with the limit.
  • \LaTeX errors will make you cry

The good news

keyval, xkeyval and others come to the rescue. I will work with xkeyval. Why? there is no specific reason, I picked that up and worked with it.

While they are easy to use, the documentation does not include a nice example, just very detailed definitions of what each macro does. For examples google and stackoverflow are your friends.

Using xkeyval

First we have to define all the keys that we want to use. There different types of keys: key, cmdkey, choicekey, booleankey and its variations. I am just working with basic keys, and won’t bother to use other types right now.

The xkeyval documentation states:

\define@key[prefix]{family}{key}          [default]{function}
\define@cmdkey[prefix]{family}[mp]{key}   [default]{function}

The prefix, family, key parameters are only an identifier for the key. The define key will create a macro of the form \prefix@family@key, the define cmkdkey something like \cmdprefix@family@key to be used later.

\define@cmdkey[DMK]{ddm}{name}{}
(...)
%I can use it when initialized as:
\cmdDMK@ddm@name

In this D&D monster example I want to define a lot of keys for the different possible attributes of the monster.

  • name
  • keywords
  • role
  • HP
  • level
  • HP
  • etc

\define@cmdkey      [DMK] {ddm}     {name}      {}
\define@cmdkey      [DMK] {ddm}     {keywords}  {}
\define@cmdkey      [DMK] {ddm}     {role}      {}
\define@cmdkey      [DMK] {ddm}     {HP}        {}
\define@cmdkey      [DMK] {ddm}     {lvl}       {}
\define@cmdkey      [DMK] {ddm}     {xp}        {}
\define@cmdkey      [DMK] {ddm}     {initiative}{}
\define@cmdkey      [DMK] {ddm}     {perception}{}
\define@cmdkey      [DMK] {ddm}     {AC}        {}
\define@cmdkey      [DMK] {ddm}     {Fort}      {}
\define@cmdkey      [DMK] {ddm}     {Refl}      {}
\define@cmdkey      [DMK] {ddm}     {Will}      {}
\define@cmdkey      [DMK] {ddm}     {speed}     {}
\define@cmdkey      [DMK] {ddm}     {AP}        {}
\define@cmdkey      [DMK] {ddm}     {save}      {}
\define@cmdkey      [DMK] {ddm}     {vision}    {}
\define@cmdkey      [DMK] {ddm}     {immune}    {}
\define@cmdkey      [DMK] {ddm}     {resist}    {}
\define@cmdkey      [DMK] {ddm}     {vulnerable}{}

The purpose of this big list is to be used as parameters of a \newcommand or \newenvironment. What xkeyval will do is parse a “string” of parameters. So for a newcommand there will be only one parameter to be parsed.

\newenvironment{ddmonster}[1][]{
    \setkeys[DMK]{ddm}{#1}
}{}

A simple full example

Finally, I post here what I missed when reading the xkeyval documentation, a full simple working example with basic keys.

\documentclass{article}

\usepackage{xkeyval}

%Two parameters command
\newcommand{\myprintname}[2]
{
    The name is #2, #1 #2    
}


%Maximum parameters command
\newcommand{\mymaxparams}[9]
{
    I can't hold nothing more than #1 #2 #3 #4 #5 #6 #7 #8 #9
}

%This is needed for encapsulating. 
%changes the catcode of @ so it
%can be used freely (not needed
%when writing a package)
\makeatletter

\define@cmdkey      [PRE] {fam}     {d}  {}
\define@cmdkey      [PRE] {fam}     {u}  {}
\define@cmdkey      [PRE] {fam}     {p}  {}
\define@cmdkey      [PRE] {fam}     {l}  {}
\define@cmdkey      [PRE] {fam}     {i}  {}
\define@cmdkey      [PRE] {fam}     {c}  {}
\define@cmdkey      [PRE] {fam}     {a}  {}
\define@cmdkey      [PRE] {fam}     {t}  {}
\define@cmdkey      [PRE] {fam}     {e}  {}
\define@cmdkey      [PRE] {fam}     {s} {}


\presetkeys         [PRE] {fam} {d = 1,
                                 u = 2,
                                 p = 3,
                                 l = 4,
                                 i = 5,
                                 c = 6,
                                 a = 7,
                                 t = 8,
                                 e = 9,
                                 s = 10,}{}

\newcommand{\myxkeyval}[1]{
    %First thing to do, Set the keys 
    %from the single parameter input
    \setkeys[PRE]{fam}{#1}
    \textbf{
    \cmdPRE@fam@d
    \cmdPRE@fam@u
    \cmdPRE@fam@p
    \cmdPRE@fam@l
    \cmdPRE@fam@i
    \cmdPRE@fam@c
    \cmdPRE@fam@a
    \cmdPRE@fam@t
    \cmdPRE@fam@e
    \cmdPRE@fam@s
    }
    here you have your 10 parameters \newline
}

%Change again the @ catcode
%to normal
\makeatother

\begin{document}
    %An example using newcommand with two parameters
    \myprintname{James}{Bond}

    %an example using a newcommand with maximum number of parameters
    \mymaxparams{un}{dos}{tres}{quatre}{cinc}{sis}{set}{vuit}{nou}
    \myxkeyval{}
    \myxkeyval{d=D,u=u,p=p,l=l,i=i,c=c,a=a,t=t,e=e,s=s has 10 different letters}

\end{document}

Isograms are words that have no duplicate letter. I find it funny that “duplicates” is an Isogram 🙂

References

xkeyval documentation ⇒GO
xkeyval package at CTAN ⇒GO
keyval package at CTAN⇒GO

Advertisement

3 Comments

Filed under code, tools

3 responses to “xkeyval and the 9 arguments

  1. Pingback: The monster environment | Castells

  2. Hikari Boulders

    I think there is an error in

    \newcommand{\myprintname}[1]{
    my name is #2 , #1 #2
    }
    \myprintname{James}{Bond}

    The number of arguments should be 2, not 1.

    • kxtells

      Hello! You think and you are right! There are two arguments instead of 1.

      I’ll fix it in the post.

      Thanks for the tip

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.