TeX - LaTeX Asked on January 25, 2021
In trying to get a better understanding of kvoptions
I have the following minimal example package and document. In the package, I construct a new macro command based on the logical states of three Boolean options. The presence of the Boolean false settings seems not to be a problem, but my question is whether or not this is an acceptable way to manipulate the options given that I get no errors or warnings.
The three options in this case are mutually exclusive and this seems to be the only way to ensure that. This is but a minimal example taken from a larger context in which there is already an existing command for changing the setup randomly within a document. No one could know that from this MWE (lack of context is a danger of using MWEs IHMO as exemplified here). I had already planned to document the requirement to specify only one option (again, not knowable from this MWE). I used a setup
command because I have repeatedly been told that that is "the best way." So in the presence of correct functioning and absence of errors or warnings, how am I to know that something shouldn’t be done? If there is a better way to handle the Booleans (and I agree it looks somewhat awkward here), I’m open to learning it. That’s why I asked the question.
MWE package:
ProvidesPackage{tiny}[2021-01-05 v1.0 A tiny demo package]
RequirePackage{kvoptions}
SetupKeyvalOptions{%
family=tiny,%
prefix=tiny@,%
setkeys=kvsetkeys%
}%
DeclareBoolOption[false]{optionone}
DeclareBoolOption[false]{optiontwo}
DeclareBoolOption[true]{optionthree}
ProcessKeyvalOptions{tiny}
newcommand*{tinysetup}{%
kvsetkeys{tiny}%
}%
% This block doesn't quite work as expected with tinysetup{}
%AtBeginDocument{%
% iftiny@optionone
% newcommand*{momentum}[1]{#1,mathrm{kg}cdotmathrm{m}cdotmathrm{s}^{-1}}
% optionone is activepar
% tiny@optiontwofalse
% optiontwo is not activepar
% tiny@optionthreefalse
% optionthree is not activepar
% fi
% iftiny@optiontwo
% newcommand*{momentum}[1]{#1,mathrm{kg}cdotmathrm{m}/mathrm{s}}
% tiny@optiononefalse
% optionone is not activepar
% optiontwo is activepar
% tiny@optionthreefalse
% optionthree is not activepar
% fi
% iftiny@optionthree
% newcommand*{momentum}[1]{#1,mathrm{N}cdotmathrm{s}}
% tiny@optiononefalse
% optionone is not activepar
% tiny@optiontwofalse
% optiontwo is not activepar
% optionthree is activepar
% fi
%}%
% This block seems to work as expected with tinysetup{}
AtBeginDocument{%
newcommand*{momentum}[1]{#1,%
iftiny@optionone
tiny@optiontwofalse
tiny@optionthreefalse
mathrm{kg}cdotmathrm{m}cdotmathrm{s}^{-1}%
fi
iftiny@optiontwo
tiny@optiononefalse
tiny@optionthreefalse
mathrm{kg}cdotmathrm{m}/mathrm{s}%
fi
iftiny@optionthree
tiny@optiononefalse
tiny@optiontwofalse
mathrm{N}cdotmathrm{s}%
fi
}% % end of newcommand
}%
And here’s the MWE document that uses the package:
% !TEX TS-program = lualatexmk
% !TEX encoding = UTF-8 Unicode
documentclass{article}
usepackage{tiny}
begin{document}
Hello.
% should invoke optionthree
( momentum{3} )
% should invoke optionone
tinysetup{optionthree=false,optionone}
( momentum{3} )
% should invoke optiontwo
tinysetup{optionone=false,optiontwo}
( momentum{3} )
% should again invoke optionthree
tinysetup{optiontwo=false,optionthree}
( momentum{3} )
end{document}
Edit: I've changed this code example to also use the chardef
variant (see below for a short explanation).
I'd drop kvoptions
and use another key=value implementation which offers choice like keys for this. Viable options would be l3keys
(with l3keys2e
for package options support), pgfkeys
(with pgfopts
for package options support), expkv
(with expkv-opt
for package options support, and expkv-def
for predefined key types), or options
.
Since I'm the author of expkv
, what follows is an example implementation using it.
ProvidesPackage{tiny}[2021-01-05 v1.0 A tiny demo package]
RequirePackage{expkv-opt,expkv-def}
ekvdefinekeys{tiny}
{
protected choice option =
{
one = {chardeftiny@option0 }
,two = {chardeftiny@option1 }
,three = {chardeftiny@option2 }
}
,initial option = three
}
ekvoProcessGlobalOptions{tiny}
ekvoProcessLocalOptions{tiny}
ekvsetdeftinysetup{tiny}
% This block seems to work as expected with tinysetup{}
AtBeginDocument{%
newcommand*{momentum}[1]{#1,%
ifcasetiny@option
mathrm{kg}cdotmathrm{m}cdotmathrm{s}^{-1}%
or
mathrm{kg}cdotmathrm{m}/mathrm{s}%
or
mathrm{N}cdotmathrm{s}%
fi
}% % end of newcommand
}
documentclass{article}
usepackage{tiny}
begin{document}
Hello.
% should invoke optionthree
( momentum{3} )
% should invoke optionone
tinysetup{option=one}
( momentum{3} )
% should invoke optiontwo
tinysetup{option=two}
( momentum{3} )
% should again invoke optionthree
tinysetup{option=three}
( momentum{3} )
end{document}
kvoptions
If you want to stick to kvoptions
I'd drop the Bool
options and use three Void
options which directly set the Booleans (similar to the three choices in the code above). This way those keys don't take any values, but you get the correct behaviour of mutually exclusive options.
ProvidesPackage{tiny}[2021-01-05 v1.0 A tiny demo package]
RequirePackage{kvoptions}
SetupKeyvalOptions{%
family=tiny,%
prefix=tiny@,%
setkeys=kvsetkeys
}
newififtiny@one
newififtiny@two
newififtiny@three
tiny@threetrue
DeclareVoidOption{optionone} {tiny@onetruetiny@twofalsetiny@threefalse}
DeclareVoidOption{optiontwo} {tiny@onefalsetiny@twotruetiny@threefalse}
DeclareVoidOption{optionthree}{tiny@onefalsetiny@twofalsetiny@threetrue}
ProcessKeyvalOptions{tiny}
newcommand*tinysetup{kvsetkeys{tiny}}
% This block seems to work as expected with tinysetup{}
AtBeginDocument{%
newcommand*{momentum}[1]{#1,%
iftiny@one
mathrm{kg}cdotmathrm{m}cdotmathrm{s}^{-1}%
fi
iftiny@two
mathrm{kg}cdotmathrm{m}/mathrm{s}%
fi
iftiny@three
mathrm{N}cdotmathrm{s}%
fi
}% % end of newcommand
}%
documentclass{article}
usepackage{tiny}
begin{document}
Hello.
% should invoke optionthree
( momentum{3} )
% should invoke optionone
tinysetup{optionone}
( momentum{3} )
% should invoke optiontwo
tinysetup{optiontwo}
( momentum{3} )
% should again invoke optionthree
tinysetup{optionthree}
( momentum{3} )
end{document}
Instead of using three newif
s, a perhaps more elegant implementation could be to use one macro that stores the respective choice as a number (the code uses kvoptions
, the principle is applicable to the expkv
example or any other key=value package as well):
ProvidesPackage{tiny}[2021-01-05 v1.0 A tiny demo package]
RequirePackage{kvoptions}
SetupKeyvalOptions{%
family=tiny,%
prefix=tiny@,%
setkeys=kvsetkeys
}
% initial value
chardeftiny@option2
DeclareVoidOption{optionone} {chardeftiny@option0 }
DeclareVoidOption{optiontwo} {chardeftiny@option1 }
DeclareVoidOption{optionthree}{chardeftiny@option2 }
ProcessKeyvalOptions{tiny}
newcommand*tinysetup{kvsetkeys{tiny}}
% This block seems to work as expected with tinysetup{}
AtBeginDocument{%
newcommand*{momentum}[1]{#1,%
ifcasetiny@option
mathrm{kg}cdotmathrm{m}cdotmathrm{s}^{-1}%
or
mathrm{kg}cdotmathrm{m}/mathrm{s}%
or
mathrm{N}cdotmathrm{s}%
fi
}% % end of newcommand
}
documentclass{article}
usepackage{tiny}
begin{document}
Hello.
% should invoke optionthree
( momentum{3} )
% should invoke optionone
tinysetup{optionone}
( momentum{3} )
% should invoke optiontwo
tinysetup{optiontwo}
( momentum{3} )
% should again invoke optionthree
tinysetup{optionthree}
( momentum{3} )
end{document}
Correct answer by Skillmon on January 25, 2021
This answer is just for fun (and because Skillmon didn't put something using expl3
:D). As you put in the description of your question, sometimes, it's hard to understand the context without having a long MWE, anyway, since expl3
came to stay, it's better to try to get used to it :), here is my best attempt.
documentclass{article}
begin{filecontents*}[overwrite]{tiny.sty}
RequirePackage{l3keys2e}
ProvidesExplPackage
{tiny}
{2021-01-05}
{1.0}
{A tiny demo package}
% Define a bool vars
bool_new:N l_tiny_option_one_bool
bool_new:N l_tiny_option_two_bool
bool_new:N l_tiny_option_three_bool
% Define a keys
keys_define:nn { tiny }
{
option .choice:,
option / one .code:n = bool_set_true:N l_tiny_option_one_bool
bool_set_false:N l_tiny_option_two_bool
bool_set_false:N l_tiny_option_three_bool,
option / two .code:n = bool_set_true:N l_tiny_option_two_bool
bool_set_false:N l_tiny_option_one_bool
bool_set_false:N l_tiny_option_three_bool,
option / three .code:n = bool_set_true:N l_tiny_option_three_bool
bool_set_false:N l_tiny_option_one_bool
bool_set_false:N l_tiny_option_two_bool,
option .initial:n = three,
option .value_required:n = true,
}
% Process
ProcessKeysOptions { tiny }
% Setup
NewDocumentCommandtinysetup{ m }
{
keys_set:nn { tiny } { #1 }
}
% Command
AtBeginDocument{
NewDocumentCommandmomentum{ m }
{
group_begin:
bool_if:NT l_tiny_option_one_bool
{
#1,mathrm{kg}cdotmathrm{m}cdotmathrm{s}^{-1}
}
bool_if:NT l_tiny_option_two_bool
{
#1,mathrm{kg}cdotmathrm{m}/mathrm{s}
}
bool_if:NT l_tiny_option_three_bool
{
#1,mathrm{N}cdotmathrm{s}
}
group_end:
}
}
end{filecontents*}
usepackage{tiny} % default option=three
%usepackage[option=one]{tiny} % load option=one
begin{document}
Hello.
% should invoke option=three
( momentum{3} )
% should invoke option=one
tinysetup{option=one}
( momentum{3} )
% should invoke option=two
tinysetup{option=two}
( momentum{3} )
% should again invoke option=three
tinysetup{option=three}
( momentum{3} )
end{document}
Answered by Pablo González L on January 25, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP