keyval - function for keyB should act dependent on value of keyA - how to do this?Test if token is a control sequenceThe definition of key default value by keyval packageUse alternate cite keys depending on document versionA list of pairs in pgfkeysExpansion problems with pgfkeysXparse macro with optional parameter breaks value for PGF foreachHow to define a function with optional key-value argumentsGenerating a table from key-value pairsHow to create and manage hierarchically dependent PGF keys?LaTeX Utility/Template for Generic Reusable Code
Count rotary dial pulses in a phone number (including letters)
My employer faked my resume to acquire projects
Simple function that simulates survey results based on sample size and probability
What are these arcade games in Ghostbusters 1984?
Looking for a soft substance that doesn't dissolve underwater
Where's this lookout in Nova Scotia?
Is it rude to call a professor by their last name with no prefix in a non-academic setting?
Are there any well known academic philosophy forums?
Where is the logic in castrating fighters?
Should breaking down something like a door be adjudicated as an attempt to beat its AC and HP, or as an ability check against a set DC?
I unknowingly submitted plagarised work
How to use " shadow " in pstricks?
Compactness of finite sets
Were pens caps holes designed to prevent death by suffocation if swallowed?
Crossing US border with music files I'm legally allowed to possess
When and what was the first 3D acceleration device ever released?
Make 24 using exactly three 3s
Simple fuzz pedal using breadboard
Defining the standard model of PA so that a space alien could understand
Why do airplanes use an axial flow jet engine instead of a more compact centrifugal jet engine?
How to remove the trailing ` in StringForm["Mean `1`", 2.2]?
Who will lead the country until there is a new Tory leader?
Pirate democracy at its finest
Would jet fuel for an F-16 or F-35 be producible during WW2?
keyval - function for keyB should act dependent on value of keyA - how to do this?
Test if token is a control sequenceThe definition of key default value by keyval packageUse alternate cite keys depending on document versionA list of pairs in pgfkeysExpansion problems with pgfkeysXparse macro with optional parameter breaks value for PGF foreachHow to define a function with optional key-value argumentsGenerating a table from key-value pairsHow to create and manage hierarchically dependent PGF keys?LaTeX Utility/Template for Generic Reusable Code
I'm new to LaTeX. I learn and I use article class. I struggle learning working with keyval package:
I try to write macro macro which only uses one mandatory argument where user can provide key value list.
Possible keys should be keyA and keyB.
keyAmay take values true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).keyAdefault should be yes branch.
I'd like keyB to take an arbitrary value and to append this value between parentheses to macro foo if keyA denotes yes-branch and to append it between square brackets to the macro bar if keyA denotes no-branch.
Besides this I'd like that keyA and keyB at every call to macro must be in the key value list and that they can be in the key value list only once - setting one of the keys more often or not at all → error-message.
I don't know how to do this because when setkeys processes the key and value pairs one by one, it is not known what other key value pair follow and with key value pairs order is not firmly prescribed.
I tried this without success:
documentclassarticle
usepackagekeyval
deffoo
defbar
newififkeyAtruekeyAtruefalse
makeatletter
define@keyMyFamilykeyA[true]keyAtruetrue
define@keyMyFamilykeyB
ifkeyAtrue
deffoofoo(#1)
else
defbarbar[#1]
fi
makeatother
newcommandmacro[1]
setkeysMyFamily#1
keyAtruefalse
begindocument
% This almost works but there is 'foo in foo'-recursion.
macrokeyA=true,keyB=first value
showfooshowbar
% This almost works but there is 'foo in foo'-recursion and
% 'bar in bar'-recursion
macrokeyB=second value
showfooshowbar
% This dos not work at all. There is recursions and added to foo, not to bar.
macrokeyA=false,keyB=third value
showfooshowbar
enddocument
Thank you for help.
macros conditionals programming key-value
New contributor
Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment |
I'm new to LaTeX. I learn and I use article class. I struggle learning working with keyval package:
I try to write macro macro which only uses one mandatory argument where user can provide key value list.
Possible keys should be keyA and keyB.
keyAmay take values true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).keyAdefault should be yes branch.
I'd like keyB to take an arbitrary value and to append this value between parentheses to macro foo if keyA denotes yes-branch and to append it between square brackets to the macro bar if keyA denotes no-branch.
Besides this I'd like that keyA and keyB at every call to macro must be in the key value list and that they can be in the key value list only once - setting one of the keys more often or not at all → error-message.
I don't know how to do this because when setkeys processes the key and value pairs one by one, it is not known what other key value pair follow and with key value pairs order is not firmly prescribed.
I tried this without success:
documentclassarticle
usepackagekeyval
deffoo
defbar
newififkeyAtruekeyAtruefalse
makeatletter
define@keyMyFamilykeyA[true]keyAtruetrue
define@keyMyFamilykeyB
ifkeyAtrue
deffoofoo(#1)
else
defbarbar[#1]
fi
makeatother
newcommandmacro[1]
setkeysMyFamily#1
keyAtruefalse
begindocument
% This almost works but there is 'foo in foo'-recursion.
macrokeyA=true,keyB=first value
showfooshowbar
% This almost works but there is 'foo in foo'-recursion and
% 'bar in bar'-recursion
macrokeyB=second value
showfooshowbar
% This dos not work at all. There is recursions and added to foo, not to bar.
macrokeyA=false,keyB=third value
showfooshowbar
enddocument
Thank you for help.
macros conditionals programming key-value
New contributor
Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment |
I'm new to LaTeX. I learn and I use article class. I struggle learning working with keyval package:
I try to write macro macro which only uses one mandatory argument where user can provide key value list.
Possible keys should be keyA and keyB.
keyAmay take values true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).keyAdefault should be yes branch.
I'd like keyB to take an arbitrary value and to append this value between parentheses to macro foo if keyA denotes yes-branch and to append it between square brackets to the macro bar if keyA denotes no-branch.
Besides this I'd like that keyA and keyB at every call to macro must be in the key value list and that they can be in the key value list only once - setting one of the keys more often or not at all → error-message.
I don't know how to do this because when setkeys processes the key and value pairs one by one, it is not known what other key value pair follow and with key value pairs order is not firmly prescribed.
I tried this without success:
documentclassarticle
usepackagekeyval
deffoo
defbar
newififkeyAtruekeyAtruefalse
makeatletter
define@keyMyFamilykeyA[true]keyAtruetrue
define@keyMyFamilykeyB
ifkeyAtrue
deffoofoo(#1)
else
defbarbar[#1]
fi
makeatother
newcommandmacro[1]
setkeysMyFamily#1
keyAtruefalse
begindocument
% This almost works but there is 'foo in foo'-recursion.
macrokeyA=true,keyB=first value
showfooshowbar
% This almost works but there is 'foo in foo'-recursion and
% 'bar in bar'-recursion
macrokeyB=second value
showfooshowbar
% This dos not work at all. There is recursions and added to foo, not to bar.
macrokeyA=false,keyB=third value
showfooshowbar
enddocument
Thank you for help.
macros conditionals programming key-value
New contributor
Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
I'm new to LaTeX. I learn and I use article class. I struggle learning working with keyval package:
I try to write macro macro which only uses one mandatory argument where user can provide key value list.
Possible keys should be keyA and keyB.
keyAmay take values true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).keyAdefault should be yes branch.
I'd like keyB to take an arbitrary value and to append this value between parentheses to macro foo if keyA denotes yes-branch and to append it between square brackets to the macro bar if keyA denotes no-branch.
Besides this I'd like that keyA and keyB at every call to macro must be in the key value list and that they can be in the key value list only once - setting one of the keys more often or not at all → error-message.
I don't know how to do this because when setkeys processes the key and value pairs one by one, it is not known what other key value pair follow and with key value pairs order is not firmly prescribed.
I tried this without success:
documentclassarticle
usepackagekeyval
deffoo
defbar
newififkeyAtruekeyAtruefalse
makeatletter
define@keyMyFamilykeyA[true]keyAtruetrue
define@keyMyFamilykeyB
ifkeyAtrue
deffoofoo(#1)
else
defbarbar[#1]
fi
makeatother
newcommandmacro[1]
setkeysMyFamily#1
keyAtruefalse
begindocument
% This almost works but there is 'foo in foo'-recursion.
macrokeyA=true,keyB=first value
showfooshowbar
% This almost works but there is 'foo in foo'-recursion and
% 'bar in bar'-recursion
macrokeyB=second value
showfooshowbar
% This dos not work at all. There is recursions and added to foo, not to bar.
macrokeyA=false,keyB=third value
showfooshowbar
enddocument
Thank you for help.
macros conditionals programming key-value
macros conditionals programming key-value
New contributor
Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
edited 8 hours ago
Phelype Oleinik
28.3k64894
28.3k64894
New contributor
Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
asked 9 hours ago
JevdokijaJevdokija
211
211
New contributor
Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
The foo in foo recursion you mentioned happens because when you do deffoofoo(something else), the inner foo is not expanded. If you were yo use that foo TeX would soon explode: TeX capacity exceeded, sorry [input stack size=5000].
To have foo contain whatever foo was, plus the new contents, you can use edef (expand def) and unexpanded, like this:
edeffoounexpandedexpandafterfoo(more stuff)
then foo will be (something else)(more stuff).
Now, the keyA=false wasn't working because you defined keyA with:
define@keyMyFamilykeyA[true]keyAtruetrue
so it wouldn't matter which value you passed to keyA, at the end it would execute keyAtruetrue, which is not what you wanted.
I defined a simple teststring<str a><str b><true><false> macro for you which compares the two strings <str a> and <str b> then returns <true> if they are equal or <false> if they are not. Then I changed the definition of keyA to cope with all the possibilities true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).
I also added a couple of % at the end of the lines to avoid spurious spaces.
And, as Ulrich Diez mentions in his answer (and I completely overlooked) you should be careful when using def because you can override commands without knowing. I replaced bar by rab (I'm not creative :)
When running the code I got this in the terminal:
> foo=macro:
->(first value).
l.51 showfoo
showrab
?
> rab=macro:
->.
l.51 showfooshowrab
?
> foo=macro:
->(first value).
l.55 showfoo
showrab
?
> rab=macro:
->[second value].
l.55 showfooshowrab
?
> foo=macro:
->(first value).
l.59 showfoo
showrab
?
> rab=macro:
->[second value][third value].
l.59 showfooshowrab
?
Full code:
documentclassarticle
usepackagekeyval
deffoo
defrab
newififkeyAtruekeyAtruefalse
makeatletter
defteststring#1#2%
edef@tempadetokenize#1%
edef@tempbdetokenize#2%
ifx@tempa@tempb
@valid@keytrue
expandafter@firstoftwo
else
expandafter@secondoftwo
fi
newifif@valid@key
define@keyMyFamilykeyA[true]%
@valid@keyfalse
teststring#1truekeyAtruetrue
teststring#1yeskeyAtruetrue
teststring#1onkeyAtruetrue
teststring#11keyAtruetrue
%
unlessif@valid@key
teststring#1falsekeyAtruefalse
teststring#1nokeyAtruefalse
teststring#1offkeyAtruefalse
teststring#10keyAtruefalse
%
fi
unlessif@valid@key
PackageErrorInvalid option `#1' for keyA
fi
define@keyMyFamilykeyB% <--
ifkeyAtrue
edeffoounexpandedexpandafterfoo(#1)% <--
else
edefrabunexpandedexpandafterrab[#1]% <--
fi
makeatother
newcommandmacro[1]% <--
setkeysMyFamily#1% <--
keyAtruefalse
begindocument
% This almost works but there is 'foo in foo'-recursion.
macrokeyA=true,keyB=first value
showfooshowrab
% This almost works but there is 'foo in foo'-recursion and
% 'rab in rab'-recursion
macrokeyB=second value
showfooshowrab
% This dos not work at all. There is recursions and added to foo, not to rab.
% tracingall
macrokeyA=false,keyB=third value
showfooshowrab
enddocument
add a comment |
Some remarks not related to the problem which nonetheless may be useful:
If lines end with curly braces (
or), LaTeX usually will insert space characters at the ends of these lines. This is due to theendlinechar-thingie. These space characters will get tokenized as space-tokens (character tokens of category code 10(space) and character code 32. In (restricted) horizontal mode these space-tokens may yield visible horizontal glue. Make sure that lines end with a comment-character (%) in cases where such glue is undesired.baris already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the macro-names I use the uppercase-variantsFOOandBAR.
Some remarks related to the problem which probably may be useful:
With the keyval package you cannot have the function underlying keyB act depending on the value of keyA/depending on the result of carrying out the function underlying keyA—as you stated yourself:
The order in which these functions get carried out is not predictable.
Also it is not predictable whether these functions get carried out at all or get carried out more than once.
Instead you can define a family of keys with functions for setting some flags (which is a method of storing true/false-values or 0/1-values) and storing as macros other values that are to be used while/after evaluating the flags.
(In the example below as flags I use switches created via newif. I do so because I don't want the example to load a large amount packages. For setting flags, the packages flags and bitset, both of Heiko Oberdiek, may be of interest to you.
I assume that the "arbitrary values" for keyB are sequences of tokens which are brace-balanced. ;-) When storing such arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing as during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.
That's why I used the MyScratchtoks..-edefmacrotheMyScratchtoks-thingie: When the is carried out during edef for delivering the content of a token-register, e.g., theMyScratchtoks, the content of the token-register MyScratchtoks will be delivered and hereby hashes will be doubled.)
Then you can define a macro which does setkeys for setting flags and storing values and then does evaluate the flags for printing error-messages and in case of not having printed any error message processing the stored values accordingly.
You also need forking depending on the phrase/token-sequence provided as value to keyA:
In the example below you find DetectYesNo which does the forking by means of delimited macro arguments.
You wish to have (almost) arbitrary things added to the macro foo.
When doing this, you need to make sure that the new definition text of foo also contains the tokens that form the old definition text of foo by top-level-expanding foo.
There are various methods for this. The answer of Phelype Oleinik exhibits the usage of edef and unexpanded.
I tend to use expandafter.
You could be tempted to do:
expandafterdefexpandafterfooexpandafterfoo<tokens to append>
But you need to be careful with such approaches as the amount of hashes will be halved during expansion of foo.
If, e.g., foo is defined via
deffoo%
defbas##1argument of bas: ##1.%
and you do:
expandafterdefexpandafterfooexpandafter%
foo
defbat##1argument of bat: ##1.%
, then this will not yield
deffoo%
defbas##1argument of bas: ##1.%
defbat##1argument of bat: ##1.%
, but it will yield:
deffoo%
defbas#1argument of bas: #1.%
defbat##1argument of bat: ##1.%
This will be a problem and therefore will yield an error message as foo does not process parameters while foo's definition text does contain #1.
Therefore in such situations I also tend to go the MyScratchtoks..-edef...theMyScratchtoks-route:
newtoksMyScratchtoks
deffoo%
defbas##1argument of bas: ##1.%
%
% Now let's append something to `foo`:
MyScratchtoksexpandafterfoo%
MyScratchtoksexpandafter%
theMyScratchtoks
defbat#1argument of bat: #1.%
%
edeffootheMyScratchtoks%
showfoo
I used this technique in the example below.
If eTeX-extensions are available you can go the edef-unexpanded-route as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.
documentclassarticle
usepackagekeyval
makeatletter
%%----------------------------------------------------------------------
newcommandUD@firstofone[1]#1%
newcommandUD@firstoftwo[2]#1%
newcommandUD@secondoftwo[2]#2%
newcommandUD@Exchange[2]#2#1%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% UD@CheckWhetherNull<Argument which is to be checked>%
%% <Tokens to be delivered in case that argument
%% which is to be checked is empty>%
%% <Tokens to be delivered in case that argument
%% which is to be checked is not empty>%
%%
%% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
newcommandUD@CheckWhetherNull[1]%
romannumeral0expandafterUD@secondoftwostringexpandafter
UD@secondoftwoexpandafterexpandafterstring#1expandafter
UD@secondoftwostringexpandafterUD@firstoftwoexpandafter%
expandafterUD@secondoftwostringexpandafterexpandafter
UD@firstoftwo UD@secondoftwoexpandafterexpandafter
UD@firstoftwo UD@firstoftwo%
%
%%----------------------------------------------------------------------
%% Macros FOO and BAR
%%----------------------------------------------------------------------
newcommand*FOO%
newcommand*BAR%
%%----------------------------------------------------------------------
%% Flags / if-switches
%%----------------------------------------------------------------------
%% Flag: Is there a need to print an error-message about keyA not
%% being set?
newififDeliverErrMsgKeyANotProvided
DeliverErrMsgKeyANotProvidedtrue
%% Flag: Is there a need to print an error-message about keyB not
%% being set?
newififDeliverErrMsgKeyBNotProvided
DeliverErrMsgKeyBNotProvidedtrue
%% Flag: Is there a need to print an error-message about keyA
%% being provided multiple times?
newififDeliverErrMsgKeyAProvidedSeveralTimes
DeliverErrMsgKeyAProvidedSeveralTimesfalse
%% Flag: Is there a need to print an error-message about keyB
%% being provided multiple times?
newififDeliverErrMsgKeyBProvidedSeveralTimes
DeliverErrMsgKeyBProvidedSeveralTimesfalse
%% Flag: Is there a need to print an error-message about keyA
%% having a value which is not in the true/false-spectrum?
newififDeliverErrMsgKeyANeitherTrueNorFalse
DeliverErrMsgKeyANeitherTrueNorFalsefalse
%% Flag: Has keyA the value true?
newififKeyAsValueIsTrue
KeyAsValueIsTruetrue
%%----------------------------------------------------------------------
%% Place-holders for values that are to be used after flag-evaluation:
%%----------------------------------------------------------------------
newtoksMyScratchtoks
newcommandMyKeyBvalue%
%%----------------------------------------------------------------------
%% Error-messages:
%%----------------------------------------------------------------------
%% PreambleMacroError
%%......................................................................
%% This macro takes three arguments:
%% A macro name. An error message. The help information.
%% It displays the error message, and sets the error help (the result of
%% typing h to the prompt).
%%----------------------------------------------------------------------
newcommand*PreambleMacroError[3]%
GenericError%
spacespacespace@spaces@spaces@spaces
%
LaTeX Error: Inapproriate usage of macro string#1on@line.MessageBreak
(string#1 is defined in the document's preamble.)MessageBreak
Problem: #2%
%
Have a look at the comments in the preamble of this document.%
#3%
%
%%----------------------------------------------------------------------
%% Error-message in case a flag-key does not have a value of the
%% true/false-spectrum:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyNeitherTrueNorFalse[1]%
PreambleMacroErrormacroInvalid value for #1%
%PackageErrorMyPackagestringmacro: Invalid value for #1on@line%
%@latex@errorstringmacro: Invalid value for #1on@line%
%
#1 must have one of the following values:%
MessageBreak true/yes/on/1 or or false/no/off/0.%
%
%
%%----------------------------------------------------------------------
%% Error-message in case a mandatory key is not set at all:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyNotProvided[1]%
PreambleMacroErrormacroSetting for #1 is missing%
%PackageErrorMyPackagestringmacro: Setting for #1 is missingon@line%
%@latex@errorstringmacro: Setting for #1 is missingon@line%
%
Setting #1 cannot be omitted.%
%
%
%%----------------------------------------------------------------------
%% Error-message in case a keys is set more times than once:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyProvidedSeveralTimes[1]%
PreambleMacroErrormacroMore than one value for #1%
%PackageErrorMyPackagestringmacro: More than one value for #1on@line%
%@latex@errorstringmacro: More than one value for #1on@line%
%
For the sake of unambiguity provide a value for #1 exactly once.%
%
%
%%----------------------------------------------------------------------
%% DetectYesNo detects whether value is either one of
%% true/yes/on/1 or one of false/no/off/0
%% DetectYesNo<value>%
%% <tokens if value neither is "yes" nor is "no">%
%% <tokens if value is "yes">%
%% <tokens if value is "no">
%%----------------------------------------------------------------------
%% Check whether argument contains no exclamation mark which is not
%% nested in braces:
%%......................................................................
%% UD@CheckWhetherNoExclam<Argument which is to be checked>%
%% <Tokens to be delivered in case that
%% argument contains no exclamation mark>%
%% <Tokens to be delivered in case that
%% argument contains exclamation mark>%
%%
newcommandUD@GobbleToExclamlongdefUD@GobbleToExclam#1!%
newcommandUD@CheckWhetherNoExclam[1]%
expandafterUD@CheckWhetherNullexpandafterUD@GobbleToExclam#1!%
%
newcommandTrueFalseFork
longdefTrueFalseFork#1!!true!yes!on!1!false!no!off!0!#2#3!!!!#2%
newcommandDetectYesNo[1]lowercaseInnerDetectYesNo#1%
newcommandInnerDetectYesNo[4]%
romannumeral0UD@CheckWhetherNoExclam#1%
TrueFalseFork!#1!true!yes!on!1!false!no!off!0! #2%<-case #1 is empty/has no tokens
!!#1!yes!on!1!false!no!off!0! #3%<-case #1 = true
!!true!#1!on!1!false!no!off!0! #3%<-case #1 = yes
!!true!yes!#1!1!false!no!off!0! #3%<-case #1 = on
!!true!yes!on!#1!false!no!off!0! #3%<-case #1 = 1
!!true!yes!on!1!#1!no!off!0! #4%<-case #1 = false
!!true!yes!on!1!false!#1!off!0! #4%<-case #1 = no
!!true!yes!on!1!false!no!#1!0! #4%<-case #1 = off
!!true!yes!on!1!false!no!off!#1! #4%<-case #1 = 0
!!true!yes!on!1!false!no!off!0! #2%<-case #1 something else without exclamation mark
!!!!%
#2%<-case #1 = something else with exclamation-mark.
%
%%----------------------------------------------------------------------
%% Use the keys of the family MyFamily to change flags and to
%% save values that are to be used later
%%----------------------------------------------------------------------
define@keyMyFamilykeyA[true]%
DeliverErrMsgKeyANotProvidedfalse
define@keyMyFamilykeyA[true]%
DeliverErrMsgKeyAProvidedSeveralTimestrue
%
DetectYesNo#1%
DeliverErrMsgKeyANeitherTrueNorFalsetrue%
KeyAsValueIsTruetrue%
KeyAsValueIsTruefalse%
%
define@keyMyFamilykeyB%
DeliverErrMsgKeyBNotProvidedfalse
define@keyMyFamilykeyB%
DeliverErrMsgKeyBProvidedSeveralTimestrue
%
expandafterUD@Exchange
expandafter%
expandafterMyScratchtoks
expandaftertheMyScratchtoks%
MyScratchtoks#1%
edefMyKeyBvaluetheMyScratchtoks%
%
%
%%----------------------------------------------------------------------
%% Now the macro with evaluation and error-messages ad nauseam ;-) :
%%----------------------------------------------------------------------
newcommandmacro[1]%
begingroup
setkeysMyFamily#1%
%
% Print Error-Messages if necessary:
%
ifDeliverErrMsgKeyANeitherTrueNorFalseDeliverErrMsgKeyNeitherTrueNorFalsekeyAfi
ifDeliverErrMsgKeyANotProvidedDeliverErrMsgKeyNotProvidedkeyAfi
ifDeliverErrMsgKeyBNotProvidedDeliverErrMsgKeyNotProvidedkeyBfi
ifDeliverErrMsgKeyAProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyAfi
ifDeliverErrMsgKeyBProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyBfi
%
% Perform the adding to FOO or BAR in case no error-messages were
% printed:
%
ifDeliverErrMsgKeyANotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyBNotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyAProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyBProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyANeitherTrueNorFalseexpandafterUD@secondoftwoelseexpandafterUD@firstoftwofi%
ifKeyAsValueIsTrueKeyAsValueIsTruetrueexpandafterUD@firstoftwoelseexpandafterUD@secondoftwofi%
expandafterUD@Exchangeexpandafter%
expandafterMyScratchtoksexpandaftertheMyScratchtoks%
%
MyScratchtoksexpandafterFOO%
expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter(MyKeyBvalue)%
edefFOOtheMyScratchtoks%
%
%
expandafterUD@Exchangeexpandafter%
expandafterMyScratchtoksexpandaftertheMyScratchtoks%
%
MyScratchtoksexpandafterBAR%
expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter[MyKeyBvalue]%
edefBARtheMyScratchtoks%
%
%
%
%
%
%
%
endgroup
%
makeatother
begindocument
noindent
verb|macrokeyA=true, keyB=Value in first call| - now you have:\
macrokeyA=true, keyB=Value in first call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=yes, keyB=Value in second call| - now you have:\
macrokeyA=yes, keyB=Value in second call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=0, keyB=Value in third call| - now you have:\
macrokeyA=0, keyB=Value in third call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=Off, keyB=Value in fourth call| - now you have:\
macrokeyA=Off, keyB=Value in fourth call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA, keyB=Value in fifth call| - now you have:\
macrokeyA, keyB=Value in fifth call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
% Let's trigger some error-messages:
%
%nullhrulefillnull\
%verb|macrokeyA=woozle, | - now you have:\
%macrokeyA=woozle, %
%textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
%nullhrulefillnull\
%verb|macrokeyA=1, keyA=false, keyB=Value in sixth call| - now you have:\
%macrokeyA=1, keyA=false, keyB=Value in sixth call%
%textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
enddocument

add a comment |
Your Answer
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "85"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Jevdokija is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f492597%2fkeyval-function-for-keyb-should-act-dependent-on-value-of-keya-how-to-do-thi%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
The foo in foo recursion you mentioned happens because when you do deffoofoo(something else), the inner foo is not expanded. If you were yo use that foo TeX would soon explode: TeX capacity exceeded, sorry [input stack size=5000].
To have foo contain whatever foo was, plus the new contents, you can use edef (expand def) and unexpanded, like this:
edeffoounexpandedexpandafterfoo(more stuff)
then foo will be (something else)(more stuff).
Now, the keyA=false wasn't working because you defined keyA with:
define@keyMyFamilykeyA[true]keyAtruetrue
so it wouldn't matter which value you passed to keyA, at the end it would execute keyAtruetrue, which is not what you wanted.
I defined a simple teststring<str a><str b><true><false> macro for you which compares the two strings <str a> and <str b> then returns <true> if they are equal or <false> if they are not. Then I changed the definition of keyA to cope with all the possibilities true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).
I also added a couple of % at the end of the lines to avoid spurious spaces.
And, as Ulrich Diez mentions in his answer (and I completely overlooked) you should be careful when using def because you can override commands without knowing. I replaced bar by rab (I'm not creative :)
When running the code I got this in the terminal:
> foo=macro:
->(first value).
l.51 showfoo
showrab
?
> rab=macro:
->.
l.51 showfooshowrab
?
> foo=macro:
->(first value).
l.55 showfoo
showrab
?
> rab=macro:
->[second value].
l.55 showfooshowrab
?
> foo=macro:
->(first value).
l.59 showfoo
showrab
?
> rab=macro:
->[second value][third value].
l.59 showfooshowrab
?
Full code:
documentclassarticle
usepackagekeyval
deffoo
defrab
newififkeyAtruekeyAtruefalse
makeatletter
defteststring#1#2%
edef@tempadetokenize#1%
edef@tempbdetokenize#2%
ifx@tempa@tempb
@valid@keytrue
expandafter@firstoftwo
else
expandafter@secondoftwo
fi
newifif@valid@key
define@keyMyFamilykeyA[true]%
@valid@keyfalse
teststring#1truekeyAtruetrue
teststring#1yeskeyAtruetrue
teststring#1onkeyAtruetrue
teststring#11keyAtruetrue
%
unlessif@valid@key
teststring#1falsekeyAtruefalse
teststring#1nokeyAtruefalse
teststring#1offkeyAtruefalse
teststring#10keyAtruefalse
%
fi
unlessif@valid@key
PackageErrorInvalid option `#1' for keyA
fi
define@keyMyFamilykeyB% <--
ifkeyAtrue
edeffoounexpandedexpandafterfoo(#1)% <--
else
edefrabunexpandedexpandafterrab[#1]% <--
fi
makeatother
newcommandmacro[1]% <--
setkeysMyFamily#1% <--
keyAtruefalse
begindocument
% This almost works but there is 'foo in foo'-recursion.
macrokeyA=true,keyB=first value
showfooshowrab
% This almost works but there is 'foo in foo'-recursion and
% 'rab in rab'-recursion
macrokeyB=second value
showfooshowrab
% This dos not work at all. There is recursions and added to foo, not to rab.
% tracingall
macrokeyA=false,keyB=third value
showfooshowrab
enddocument
add a comment |
The foo in foo recursion you mentioned happens because when you do deffoofoo(something else), the inner foo is not expanded. If you were yo use that foo TeX would soon explode: TeX capacity exceeded, sorry [input stack size=5000].
To have foo contain whatever foo was, plus the new contents, you can use edef (expand def) and unexpanded, like this:
edeffoounexpandedexpandafterfoo(more stuff)
then foo will be (something else)(more stuff).
Now, the keyA=false wasn't working because you defined keyA with:
define@keyMyFamilykeyA[true]keyAtruetrue
so it wouldn't matter which value you passed to keyA, at the end it would execute keyAtruetrue, which is not what you wanted.
I defined a simple teststring<str a><str b><true><false> macro for you which compares the two strings <str a> and <str b> then returns <true> if they are equal or <false> if they are not. Then I changed the definition of keyA to cope with all the possibilities true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).
I also added a couple of % at the end of the lines to avoid spurious spaces.
And, as Ulrich Diez mentions in his answer (and I completely overlooked) you should be careful when using def because you can override commands without knowing. I replaced bar by rab (I'm not creative :)
When running the code I got this in the terminal:
> foo=macro:
->(first value).
l.51 showfoo
showrab
?
> rab=macro:
->.
l.51 showfooshowrab
?
> foo=macro:
->(first value).
l.55 showfoo
showrab
?
> rab=macro:
->[second value].
l.55 showfooshowrab
?
> foo=macro:
->(first value).
l.59 showfoo
showrab
?
> rab=macro:
->[second value][third value].
l.59 showfooshowrab
?
Full code:
documentclassarticle
usepackagekeyval
deffoo
defrab
newififkeyAtruekeyAtruefalse
makeatletter
defteststring#1#2%
edef@tempadetokenize#1%
edef@tempbdetokenize#2%
ifx@tempa@tempb
@valid@keytrue
expandafter@firstoftwo
else
expandafter@secondoftwo
fi
newifif@valid@key
define@keyMyFamilykeyA[true]%
@valid@keyfalse
teststring#1truekeyAtruetrue
teststring#1yeskeyAtruetrue
teststring#1onkeyAtruetrue
teststring#11keyAtruetrue
%
unlessif@valid@key
teststring#1falsekeyAtruefalse
teststring#1nokeyAtruefalse
teststring#1offkeyAtruefalse
teststring#10keyAtruefalse
%
fi
unlessif@valid@key
PackageErrorInvalid option `#1' for keyA
fi
define@keyMyFamilykeyB% <--
ifkeyAtrue
edeffoounexpandedexpandafterfoo(#1)% <--
else
edefrabunexpandedexpandafterrab[#1]% <--
fi
makeatother
newcommandmacro[1]% <--
setkeysMyFamily#1% <--
keyAtruefalse
begindocument
% This almost works but there is 'foo in foo'-recursion.
macrokeyA=true,keyB=first value
showfooshowrab
% This almost works but there is 'foo in foo'-recursion and
% 'rab in rab'-recursion
macrokeyB=second value
showfooshowrab
% This dos not work at all. There is recursions and added to foo, not to rab.
% tracingall
macrokeyA=false,keyB=third value
showfooshowrab
enddocument
add a comment |
The foo in foo recursion you mentioned happens because when you do deffoofoo(something else), the inner foo is not expanded. If you were yo use that foo TeX would soon explode: TeX capacity exceeded, sorry [input stack size=5000].
To have foo contain whatever foo was, plus the new contents, you can use edef (expand def) and unexpanded, like this:
edeffoounexpandedexpandafterfoo(more stuff)
then foo will be (something else)(more stuff).
Now, the keyA=false wasn't working because you defined keyA with:
define@keyMyFamilykeyA[true]keyAtruetrue
so it wouldn't matter which value you passed to keyA, at the end it would execute keyAtruetrue, which is not what you wanted.
I defined a simple teststring<str a><str b><true><false> macro for you which compares the two strings <str a> and <str b> then returns <true> if they are equal or <false> if they are not. Then I changed the definition of keyA to cope with all the possibilities true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).
I also added a couple of % at the end of the lines to avoid spurious spaces.
And, as Ulrich Diez mentions in his answer (and I completely overlooked) you should be careful when using def because you can override commands without knowing. I replaced bar by rab (I'm not creative :)
When running the code I got this in the terminal:
> foo=macro:
->(first value).
l.51 showfoo
showrab
?
> rab=macro:
->.
l.51 showfooshowrab
?
> foo=macro:
->(first value).
l.55 showfoo
showrab
?
> rab=macro:
->[second value].
l.55 showfooshowrab
?
> foo=macro:
->(first value).
l.59 showfoo
showrab
?
> rab=macro:
->[second value][third value].
l.59 showfooshowrab
?
Full code:
documentclassarticle
usepackagekeyval
deffoo
defrab
newififkeyAtruekeyAtruefalse
makeatletter
defteststring#1#2%
edef@tempadetokenize#1%
edef@tempbdetokenize#2%
ifx@tempa@tempb
@valid@keytrue
expandafter@firstoftwo
else
expandafter@secondoftwo
fi
newifif@valid@key
define@keyMyFamilykeyA[true]%
@valid@keyfalse
teststring#1truekeyAtruetrue
teststring#1yeskeyAtruetrue
teststring#1onkeyAtruetrue
teststring#11keyAtruetrue
%
unlessif@valid@key
teststring#1falsekeyAtruefalse
teststring#1nokeyAtruefalse
teststring#1offkeyAtruefalse
teststring#10keyAtruefalse
%
fi
unlessif@valid@key
PackageErrorInvalid option `#1' for keyA
fi
define@keyMyFamilykeyB% <--
ifkeyAtrue
edeffoounexpandedexpandafterfoo(#1)% <--
else
edefrabunexpandedexpandafterrab[#1]% <--
fi
makeatother
newcommandmacro[1]% <--
setkeysMyFamily#1% <--
keyAtruefalse
begindocument
% This almost works but there is 'foo in foo'-recursion.
macrokeyA=true,keyB=first value
showfooshowrab
% This almost works but there is 'foo in foo'-recursion and
% 'rab in rab'-recursion
macrokeyB=second value
showfooshowrab
% This dos not work at all. There is recursions and added to foo, not to rab.
% tracingall
macrokeyA=false,keyB=third value
showfooshowrab
enddocument
The foo in foo recursion you mentioned happens because when you do deffoofoo(something else), the inner foo is not expanded. If you were yo use that foo TeX would soon explode: TeX capacity exceeded, sorry [input stack size=5000].
To have foo contain whatever foo was, plus the new contents, you can use edef (expand def) and unexpanded, like this:
edeffoounexpandedexpandafterfoo(more stuff)
then foo will be (something else)(more stuff).
Now, the keyA=false wasn't working because you defined keyA with:
define@keyMyFamilykeyA[true]keyAtruetrue
so it wouldn't matter which value you passed to keyA, at the end it would execute keyAtruetrue, which is not what you wanted.
I defined a simple teststring<str a><str b><true><false> macro for you which compares the two strings <str a> and <str b> then returns <true> if they are equal or <false> if they are not. Then I changed the definition of keyA to cope with all the possibilities true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).
I also added a couple of % at the end of the lines to avoid spurious spaces.
And, as Ulrich Diez mentions in his answer (and I completely overlooked) you should be careful when using def because you can override commands without knowing. I replaced bar by rab (I'm not creative :)
When running the code I got this in the terminal:
> foo=macro:
->(first value).
l.51 showfoo
showrab
?
> rab=macro:
->.
l.51 showfooshowrab
?
> foo=macro:
->(first value).
l.55 showfoo
showrab
?
> rab=macro:
->[second value].
l.55 showfooshowrab
?
> foo=macro:
->(first value).
l.59 showfoo
showrab
?
> rab=macro:
->[second value][third value].
l.59 showfooshowrab
?
Full code:
documentclassarticle
usepackagekeyval
deffoo
defrab
newififkeyAtruekeyAtruefalse
makeatletter
defteststring#1#2%
edef@tempadetokenize#1%
edef@tempbdetokenize#2%
ifx@tempa@tempb
@valid@keytrue
expandafter@firstoftwo
else
expandafter@secondoftwo
fi
newifif@valid@key
define@keyMyFamilykeyA[true]%
@valid@keyfalse
teststring#1truekeyAtruetrue
teststring#1yeskeyAtruetrue
teststring#1onkeyAtruetrue
teststring#11keyAtruetrue
%
unlessif@valid@key
teststring#1falsekeyAtruefalse
teststring#1nokeyAtruefalse
teststring#1offkeyAtruefalse
teststring#10keyAtruefalse
%
fi
unlessif@valid@key
PackageErrorInvalid option `#1' for keyA
fi
define@keyMyFamilykeyB% <--
ifkeyAtrue
edeffoounexpandedexpandafterfoo(#1)% <--
else
edefrabunexpandedexpandafterrab[#1]% <--
fi
makeatother
newcommandmacro[1]% <--
setkeysMyFamily#1% <--
keyAtruefalse
begindocument
% This almost works but there is 'foo in foo'-recursion.
macrokeyA=true,keyB=first value
showfooshowrab
% This almost works but there is 'foo in foo'-recursion and
% 'rab in rab'-recursion
macrokeyB=second value
showfooshowrab
% This dos not work at all. There is recursions and added to foo, not to rab.
% tracingall
macrokeyA=false,keyB=third value
showfooshowrab
enddocument
edited 8 hours ago
answered 8 hours ago
Phelype OleinikPhelype Oleinik
28.3k64894
28.3k64894
add a comment |
add a comment |
Some remarks not related to the problem which nonetheless may be useful:
If lines end with curly braces (
or), LaTeX usually will insert space characters at the ends of these lines. This is due to theendlinechar-thingie. These space characters will get tokenized as space-tokens (character tokens of category code 10(space) and character code 32. In (restricted) horizontal mode these space-tokens may yield visible horizontal glue. Make sure that lines end with a comment-character (%) in cases where such glue is undesired.baris already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the macro-names I use the uppercase-variantsFOOandBAR.
Some remarks related to the problem which probably may be useful:
With the keyval package you cannot have the function underlying keyB act depending on the value of keyA/depending on the result of carrying out the function underlying keyA—as you stated yourself:
The order in which these functions get carried out is not predictable.
Also it is not predictable whether these functions get carried out at all or get carried out more than once.
Instead you can define a family of keys with functions for setting some flags (which is a method of storing true/false-values or 0/1-values) and storing as macros other values that are to be used while/after evaluating the flags.
(In the example below as flags I use switches created via newif. I do so because I don't want the example to load a large amount packages. For setting flags, the packages flags and bitset, both of Heiko Oberdiek, may be of interest to you.
I assume that the "arbitrary values" for keyB are sequences of tokens which are brace-balanced. ;-) When storing such arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing as during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.
That's why I used the MyScratchtoks..-edefmacrotheMyScratchtoks-thingie: When the is carried out during edef for delivering the content of a token-register, e.g., theMyScratchtoks, the content of the token-register MyScratchtoks will be delivered and hereby hashes will be doubled.)
Then you can define a macro which does setkeys for setting flags and storing values and then does evaluate the flags for printing error-messages and in case of not having printed any error message processing the stored values accordingly.
You also need forking depending on the phrase/token-sequence provided as value to keyA:
In the example below you find DetectYesNo which does the forking by means of delimited macro arguments.
You wish to have (almost) arbitrary things added to the macro foo.
When doing this, you need to make sure that the new definition text of foo also contains the tokens that form the old definition text of foo by top-level-expanding foo.
There are various methods for this. The answer of Phelype Oleinik exhibits the usage of edef and unexpanded.
I tend to use expandafter.
You could be tempted to do:
expandafterdefexpandafterfooexpandafterfoo<tokens to append>
But you need to be careful with such approaches as the amount of hashes will be halved during expansion of foo.
If, e.g., foo is defined via
deffoo%
defbas##1argument of bas: ##1.%
and you do:
expandafterdefexpandafterfooexpandafter%
foo
defbat##1argument of bat: ##1.%
, then this will not yield
deffoo%
defbas##1argument of bas: ##1.%
defbat##1argument of bat: ##1.%
, but it will yield:
deffoo%
defbas#1argument of bas: #1.%
defbat##1argument of bat: ##1.%
This will be a problem and therefore will yield an error message as foo does not process parameters while foo's definition text does contain #1.
Therefore in such situations I also tend to go the MyScratchtoks..-edef...theMyScratchtoks-route:
newtoksMyScratchtoks
deffoo%
defbas##1argument of bas: ##1.%
%
% Now let's append something to `foo`:
MyScratchtoksexpandafterfoo%
MyScratchtoksexpandafter%
theMyScratchtoks
defbat#1argument of bat: #1.%
%
edeffootheMyScratchtoks%
showfoo
I used this technique in the example below.
If eTeX-extensions are available you can go the edef-unexpanded-route as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.
documentclassarticle
usepackagekeyval
makeatletter
%%----------------------------------------------------------------------
newcommandUD@firstofone[1]#1%
newcommandUD@firstoftwo[2]#1%
newcommandUD@secondoftwo[2]#2%
newcommandUD@Exchange[2]#2#1%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% UD@CheckWhetherNull<Argument which is to be checked>%
%% <Tokens to be delivered in case that argument
%% which is to be checked is empty>%
%% <Tokens to be delivered in case that argument
%% which is to be checked is not empty>%
%%
%% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
newcommandUD@CheckWhetherNull[1]%
romannumeral0expandafterUD@secondoftwostringexpandafter
UD@secondoftwoexpandafterexpandafterstring#1expandafter
UD@secondoftwostringexpandafterUD@firstoftwoexpandafter%
expandafterUD@secondoftwostringexpandafterexpandafter
UD@firstoftwo UD@secondoftwoexpandafterexpandafter
UD@firstoftwo UD@firstoftwo%
%
%%----------------------------------------------------------------------
%% Macros FOO and BAR
%%----------------------------------------------------------------------
newcommand*FOO%
newcommand*BAR%
%%----------------------------------------------------------------------
%% Flags / if-switches
%%----------------------------------------------------------------------
%% Flag: Is there a need to print an error-message about keyA not
%% being set?
newififDeliverErrMsgKeyANotProvided
DeliverErrMsgKeyANotProvidedtrue
%% Flag: Is there a need to print an error-message about keyB not
%% being set?
newififDeliverErrMsgKeyBNotProvided
DeliverErrMsgKeyBNotProvidedtrue
%% Flag: Is there a need to print an error-message about keyA
%% being provided multiple times?
newififDeliverErrMsgKeyAProvidedSeveralTimes
DeliverErrMsgKeyAProvidedSeveralTimesfalse
%% Flag: Is there a need to print an error-message about keyB
%% being provided multiple times?
newififDeliverErrMsgKeyBProvidedSeveralTimes
DeliverErrMsgKeyBProvidedSeveralTimesfalse
%% Flag: Is there a need to print an error-message about keyA
%% having a value which is not in the true/false-spectrum?
newififDeliverErrMsgKeyANeitherTrueNorFalse
DeliverErrMsgKeyANeitherTrueNorFalsefalse
%% Flag: Has keyA the value true?
newififKeyAsValueIsTrue
KeyAsValueIsTruetrue
%%----------------------------------------------------------------------
%% Place-holders for values that are to be used after flag-evaluation:
%%----------------------------------------------------------------------
newtoksMyScratchtoks
newcommandMyKeyBvalue%
%%----------------------------------------------------------------------
%% Error-messages:
%%----------------------------------------------------------------------
%% PreambleMacroError
%%......................................................................
%% This macro takes three arguments:
%% A macro name. An error message. The help information.
%% It displays the error message, and sets the error help (the result of
%% typing h to the prompt).
%%----------------------------------------------------------------------
newcommand*PreambleMacroError[3]%
GenericError%
spacespacespace@spaces@spaces@spaces
%
LaTeX Error: Inapproriate usage of macro string#1on@line.MessageBreak
(string#1 is defined in the document's preamble.)MessageBreak
Problem: #2%
%
Have a look at the comments in the preamble of this document.%
#3%
%
%%----------------------------------------------------------------------
%% Error-message in case a flag-key does not have a value of the
%% true/false-spectrum:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyNeitherTrueNorFalse[1]%
PreambleMacroErrormacroInvalid value for #1%
%PackageErrorMyPackagestringmacro: Invalid value for #1on@line%
%@latex@errorstringmacro: Invalid value for #1on@line%
%
#1 must have one of the following values:%
MessageBreak true/yes/on/1 or or false/no/off/0.%
%
%
%%----------------------------------------------------------------------
%% Error-message in case a mandatory key is not set at all:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyNotProvided[1]%
PreambleMacroErrormacroSetting for #1 is missing%
%PackageErrorMyPackagestringmacro: Setting for #1 is missingon@line%
%@latex@errorstringmacro: Setting for #1 is missingon@line%
%
Setting #1 cannot be omitted.%
%
%
%%----------------------------------------------------------------------
%% Error-message in case a keys is set more times than once:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyProvidedSeveralTimes[1]%
PreambleMacroErrormacroMore than one value for #1%
%PackageErrorMyPackagestringmacro: More than one value for #1on@line%
%@latex@errorstringmacro: More than one value for #1on@line%
%
For the sake of unambiguity provide a value for #1 exactly once.%
%
%
%%----------------------------------------------------------------------
%% DetectYesNo detects whether value is either one of
%% true/yes/on/1 or one of false/no/off/0
%% DetectYesNo<value>%
%% <tokens if value neither is "yes" nor is "no">%
%% <tokens if value is "yes">%
%% <tokens if value is "no">
%%----------------------------------------------------------------------
%% Check whether argument contains no exclamation mark which is not
%% nested in braces:
%%......................................................................
%% UD@CheckWhetherNoExclam<Argument which is to be checked>%
%% <Tokens to be delivered in case that
%% argument contains no exclamation mark>%
%% <Tokens to be delivered in case that
%% argument contains exclamation mark>%
%%
newcommandUD@GobbleToExclamlongdefUD@GobbleToExclam#1!%
newcommandUD@CheckWhetherNoExclam[1]%
expandafterUD@CheckWhetherNullexpandafterUD@GobbleToExclam#1!%
%
newcommandTrueFalseFork
longdefTrueFalseFork#1!!true!yes!on!1!false!no!off!0!#2#3!!!!#2%
newcommandDetectYesNo[1]lowercaseInnerDetectYesNo#1%
newcommandInnerDetectYesNo[4]%
romannumeral0UD@CheckWhetherNoExclam#1%
TrueFalseFork!#1!true!yes!on!1!false!no!off!0! #2%<-case #1 is empty/has no tokens
!!#1!yes!on!1!false!no!off!0! #3%<-case #1 = true
!!true!#1!on!1!false!no!off!0! #3%<-case #1 = yes
!!true!yes!#1!1!false!no!off!0! #3%<-case #1 = on
!!true!yes!on!#1!false!no!off!0! #3%<-case #1 = 1
!!true!yes!on!1!#1!no!off!0! #4%<-case #1 = false
!!true!yes!on!1!false!#1!off!0! #4%<-case #1 = no
!!true!yes!on!1!false!no!#1!0! #4%<-case #1 = off
!!true!yes!on!1!false!no!off!#1! #4%<-case #1 = 0
!!true!yes!on!1!false!no!off!0! #2%<-case #1 something else without exclamation mark
!!!!%
#2%<-case #1 = something else with exclamation-mark.
%
%%----------------------------------------------------------------------
%% Use the keys of the family MyFamily to change flags and to
%% save values that are to be used later
%%----------------------------------------------------------------------
define@keyMyFamilykeyA[true]%
DeliverErrMsgKeyANotProvidedfalse
define@keyMyFamilykeyA[true]%
DeliverErrMsgKeyAProvidedSeveralTimestrue
%
DetectYesNo#1%
DeliverErrMsgKeyANeitherTrueNorFalsetrue%
KeyAsValueIsTruetrue%
KeyAsValueIsTruefalse%
%
define@keyMyFamilykeyB%
DeliverErrMsgKeyBNotProvidedfalse
define@keyMyFamilykeyB%
DeliverErrMsgKeyBProvidedSeveralTimestrue
%
expandafterUD@Exchange
expandafter%
expandafterMyScratchtoks
expandaftertheMyScratchtoks%
MyScratchtoks#1%
edefMyKeyBvaluetheMyScratchtoks%
%
%
%%----------------------------------------------------------------------
%% Now the macro with evaluation and error-messages ad nauseam ;-) :
%%----------------------------------------------------------------------
newcommandmacro[1]%
begingroup
setkeysMyFamily#1%
%
% Print Error-Messages if necessary:
%
ifDeliverErrMsgKeyANeitherTrueNorFalseDeliverErrMsgKeyNeitherTrueNorFalsekeyAfi
ifDeliverErrMsgKeyANotProvidedDeliverErrMsgKeyNotProvidedkeyAfi
ifDeliverErrMsgKeyBNotProvidedDeliverErrMsgKeyNotProvidedkeyBfi
ifDeliverErrMsgKeyAProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyAfi
ifDeliverErrMsgKeyBProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyBfi
%
% Perform the adding to FOO or BAR in case no error-messages were
% printed:
%
ifDeliverErrMsgKeyANotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyBNotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyAProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyBProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyANeitherTrueNorFalseexpandafterUD@secondoftwoelseexpandafterUD@firstoftwofi%
ifKeyAsValueIsTrueKeyAsValueIsTruetrueexpandafterUD@firstoftwoelseexpandafterUD@secondoftwofi%
expandafterUD@Exchangeexpandafter%
expandafterMyScratchtoksexpandaftertheMyScratchtoks%
%
MyScratchtoksexpandafterFOO%
expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter(MyKeyBvalue)%
edefFOOtheMyScratchtoks%
%
%
expandafterUD@Exchangeexpandafter%
expandafterMyScratchtoksexpandaftertheMyScratchtoks%
%
MyScratchtoksexpandafterBAR%
expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter[MyKeyBvalue]%
edefBARtheMyScratchtoks%
%
%
%
%
%
%
%
endgroup
%
makeatother
begindocument
noindent
verb|macrokeyA=true, keyB=Value in first call| - now you have:\
macrokeyA=true, keyB=Value in first call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=yes, keyB=Value in second call| - now you have:\
macrokeyA=yes, keyB=Value in second call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=0, keyB=Value in third call| - now you have:\
macrokeyA=0, keyB=Value in third call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=Off, keyB=Value in fourth call| - now you have:\
macrokeyA=Off, keyB=Value in fourth call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA, keyB=Value in fifth call| - now you have:\
macrokeyA, keyB=Value in fifth call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
% Let's trigger some error-messages:
%
%nullhrulefillnull\
%verb|macrokeyA=woozle, | - now you have:\
%macrokeyA=woozle, %
%textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
%nullhrulefillnull\
%verb|macrokeyA=1, keyA=false, keyB=Value in sixth call| - now you have:\
%macrokeyA=1, keyA=false, keyB=Value in sixth call%
%textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
enddocument

add a comment |
Some remarks not related to the problem which nonetheless may be useful:
If lines end with curly braces (
or), LaTeX usually will insert space characters at the ends of these lines. This is due to theendlinechar-thingie. These space characters will get tokenized as space-tokens (character tokens of category code 10(space) and character code 32. In (restricted) horizontal mode these space-tokens may yield visible horizontal glue. Make sure that lines end with a comment-character (%) in cases where such glue is undesired.baris already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the macro-names I use the uppercase-variantsFOOandBAR.
Some remarks related to the problem which probably may be useful:
With the keyval package you cannot have the function underlying keyB act depending on the value of keyA/depending on the result of carrying out the function underlying keyA—as you stated yourself:
The order in which these functions get carried out is not predictable.
Also it is not predictable whether these functions get carried out at all or get carried out more than once.
Instead you can define a family of keys with functions for setting some flags (which is a method of storing true/false-values or 0/1-values) and storing as macros other values that are to be used while/after evaluating the flags.
(In the example below as flags I use switches created via newif. I do so because I don't want the example to load a large amount packages. For setting flags, the packages flags and bitset, both of Heiko Oberdiek, may be of interest to you.
I assume that the "arbitrary values" for keyB are sequences of tokens which are brace-balanced. ;-) When storing such arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing as during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.
That's why I used the MyScratchtoks..-edefmacrotheMyScratchtoks-thingie: When the is carried out during edef for delivering the content of a token-register, e.g., theMyScratchtoks, the content of the token-register MyScratchtoks will be delivered and hereby hashes will be doubled.)
Then you can define a macro which does setkeys for setting flags and storing values and then does evaluate the flags for printing error-messages and in case of not having printed any error message processing the stored values accordingly.
You also need forking depending on the phrase/token-sequence provided as value to keyA:
In the example below you find DetectYesNo which does the forking by means of delimited macro arguments.
You wish to have (almost) arbitrary things added to the macro foo.
When doing this, you need to make sure that the new definition text of foo also contains the tokens that form the old definition text of foo by top-level-expanding foo.
There are various methods for this. The answer of Phelype Oleinik exhibits the usage of edef and unexpanded.
I tend to use expandafter.
You could be tempted to do:
expandafterdefexpandafterfooexpandafterfoo<tokens to append>
But you need to be careful with such approaches as the amount of hashes will be halved during expansion of foo.
If, e.g., foo is defined via
deffoo%
defbas##1argument of bas: ##1.%
and you do:
expandafterdefexpandafterfooexpandafter%
foo
defbat##1argument of bat: ##1.%
, then this will not yield
deffoo%
defbas##1argument of bas: ##1.%
defbat##1argument of bat: ##1.%
, but it will yield:
deffoo%
defbas#1argument of bas: #1.%
defbat##1argument of bat: ##1.%
This will be a problem and therefore will yield an error message as foo does not process parameters while foo's definition text does contain #1.
Therefore in such situations I also tend to go the MyScratchtoks..-edef...theMyScratchtoks-route:
newtoksMyScratchtoks
deffoo%
defbas##1argument of bas: ##1.%
%
% Now let's append something to `foo`:
MyScratchtoksexpandafterfoo%
MyScratchtoksexpandafter%
theMyScratchtoks
defbat#1argument of bat: #1.%
%
edeffootheMyScratchtoks%
showfoo
I used this technique in the example below.
If eTeX-extensions are available you can go the edef-unexpanded-route as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.
documentclassarticle
usepackagekeyval
makeatletter
%%----------------------------------------------------------------------
newcommandUD@firstofone[1]#1%
newcommandUD@firstoftwo[2]#1%
newcommandUD@secondoftwo[2]#2%
newcommandUD@Exchange[2]#2#1%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% UD@CheckWhetherNull<Argument which is to be checked>%
%% <Tokens to be delivered in case that argument
%% which is to be checked is empty>%
%% <Tokens to be delivered in case that argument
%% which is to be checked is not empty>%
%%
%% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
newcommandUD@CheckWhetherNull[1]%
romannumeral0expandafterUD@secondoftwostringexpandafter
UD@secondoftwoexpandafterexpandafterstring#1expandafter
UD@secondoftwostringexpandafterUD@firstoftwoexpandafter%
expandafterUD@secondoftwostringexpandafterexpandafter
UD@firstoftwo UD@secondoftwoexpandafterexpandafter
UD@firstoftwo UD@firstoftwo%
%
%%----------------------------------------------------------------------
%% Macros FOO and BAR
%%----------------------------------------------------------------------
newcommand*FOO%
newcommand*BAR%
%%----------------------------------------------------------------------
%% Flags / if-switches
%%----------------------------------------------------------------------
%% Flag: Is there a need to print an error-message about keyA not
%% being set?
newififDeliverErrMsgKeyANotProvided
DeliverErrMsgKeyANotProvidedtrue
%% Flag: Is there a need to print an error-message about keyB not
%% being set?
newififDeliverErrMsgKeyBNotProvided
DeliverErrMsgKeyBNotProvidedtrue
%% Flag: Is there a need to print an error-message about keyA
%% being provided multiple times?
newififDeliverErrMsgKeyAProvidedSeveralTimes
DeliverErrMsgKeyAProvidedSeveralTimesfalse
%% Flag: Is there a need to print an error-message about keyB
%% being provided multiple times?
newififDeliverErrMsgKeyBProvidedSeveralTimes
DeliverErrMsgKeyBProvidedSeveralTimesfalse
%% Flag: Is there a need to print an error-message about keyA
%% having a value which is not in the true/false-spectrum?
newififDeliverErrMsgKeyANeitherTrueNorFalse
DeliverErrMsgKeyANeitherTrueNorFalsefalse
%% Flag: Has keyA the value true?
newififKeyAsValueIsTrue
KeyAsValueIsTruetrue
%%----------------------------------------------------------------------
%% Place-holders for values that are to be used after flag-evaluation:
%%----------------------------------------------------------------------
newtoksMyScratchtoks
newcommandMyKeyBvalue%
%%----------------------------------------------------------------------
%% Error-messages:
%%----------------------------------------------------------------------
%% PreambleMacroError
%%......................................................................
%% This macro takes three arguments:
%% A macro name. An error message. The help information.
%% It displays the error message, and sets the error help (the result of
%% typing h to the prompt).
%%----------------------------------------------------------------------
newcommand*PreambleMacroError[3]%
GenericError%
spacespacespace@spaces@spaces@spaces
%
LaTeX Error: Inapproriate usage of macro string#1on@line.MessageBreak
(string#1 is defined in the document's preamble.)MessageBreak
Problem: #2%
%
Have a look at the comments in the preamble of this document.%
#3%
%
%%----------------------------------------------------------------------
%% Error-message in case a flag-key does not have a value of the
%% true/false-spectrum:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyNeitherTrueNorFalse[1]%
PreambleMacroErrormacroInvalid value for #1%
%PackageErrorMyPackagestringmacro: Invalid value for #1on@line%
%@latex@errorstringmacro: Invalid value for #1on@line%
%
#1 must have one of the following values:%
MessageBreak true/yes/on/1 or or false/no/off/0.%
%
%
%%----------------------------------------------------------------------
%% Error-message in case a mandatory key is not set at all:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyNotProvided[1]%
PreambleMacroErrormacroSetting for #1 is missing%
%PackageErrorMyPackagestringmacro: Setting for #1 is missingon@line%
%@latex@errorstringmacro: Setting for #1 is missingon@line%
%
Setting #1 cannot be omitted.%
%
%
%%----------------------------------------------------------------------
%% Error-message in case a keys is set more times than once:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyProvidedSeveralTimes[1]%
PreambleMacroErrormacroMore than one value for #1%
%PackageErrorMyPackagestringmacro: More than one value for #1on@line%
%@latex@errorstringmacro: More than one value for #1on@line%
%
For the sake of unambiguity provide a value for #1 exactly once.%
%
%
%%----------------------------------------------------------------------
%% DetectYesNo detects whether value is either one of
%% true/yes/on/1 or one of false/no/off/0
%% DetectYesNo<value>%
%% <tokens if value neither is "yes" nor is "no">%
%% <tokens if value is "yes">%
%% <tokens if value is "no">
%%----------------------------------------------------------------------
%% Check whether argument contains no exclamation mark which is not
%% nested in braces:
%%......................................................................
%% UD@CheckWhetherNoExclam<Argument which is to be checked>%
%% <Tokens to be delivered in case that
%% argument contains no exclamation mark>%
%% <Tokens to be delivered in case that
%% argument contains exclamation mark>%
%%
newcommandUD@GobbleToExclamlongdefUD@GobbleToExclam#1!%
newcommandUD@CheckWhetherNoExclam[1]%
expandafterUD@CheckWhetherNullexpandafterUD@GobbleToExclam#1!%
%
newcommandTrueFalseFork
longdefTrueFalseFork#1!!true!yes!on!1!false!no!off!0!#2#3!!!!#2%
newcommandDetectYesNo[1]lowercaseInnerDetectYesNo#1%
newcommandInnerDetectYesNo[4]%
romannumeral0UD@CheckWhetherNoExclam#1%
TrueFalseFork!#1!true!yes!on!1!false!no!off!0! #2%<-case #1 is empty/has no tokens
!!#1!yes!on!1!false!no!off!0! #3%<-case #1 = true
!!true!#1!on!1!false!no!off!0! #3%<-case #1 = yes
!!true!yes!#1!1!false!no!off!0! #3%<-case #1 = on
!!true!yes!on!#1!false!no!off!0! #3%<-case #1 = 1
!!true!yes!on!1!#1!no!off!0! #4%<-case #1 = false
!!true!yes!on!1!false!#1!off!0! #4%<-case #1 = no
!!true!yes!on!1!false!no!#1!0! #4%<-case #1 = off
!!true!yes!on!1!false!no!off!#1! #4%<-case #1 = 0
!!true!yes!on!1!false!no!off!0! #2%<-case #1 something else without exclamation mark
!!!!%
#2%<-case #1 = something else with exclamation-mark.
%
%%----------------------------------------------------------------------
%% Use the keys of the family MyFamily to change flags and to
%% save values that are to be used later
%%----------------------------------------------------------------------
define@keyMyFamilykeyA[true]%
DeliverErrMsgKeyANotProvidedfalse
define@keyMyFamilykeyA[true]%
DeliverErrMsgKeyAProvidedSeveralTimestrue
%
DetectYesNo#1%
DeliverErrMsgKeyANeitherTrueNorFalsetrue%
KeyAsValueIsTruetrue%
KeyAsValueIsTruefalse%
%
define@keyMyFamilykeyB%
DeliverErrMsgKeyBNotProvidedfalse
define@keyMyFamilykeyB%
DeliverErrMsgKeyBProvidedSeveralTimestrue
%
expandafterUD@Exchange
expandafter%
expandafterMyScratchtoks
expandaftertheMyScratchtoks%
MyScratchtoks#1%
edefMyKeyBvaluetheMyScratchtoks%
%
%
%%----------------------------------------------------------------------
%% Now the macro with evaluation and error-messages ad nauseam ;-) :
%%----------------------------------------------------------------------
newcommandmacro[1]%
begingroup
setkeysMyFamily#1%
%
% Print Error-Messages if necessary:
%
ifDeliverErrMsgKeyANeitherTrueNorFalseDeliverErrMsgKeyNeitherTrueNorFalsekeyAfi
ifDeliverErrMsgKeyANotProvidedDeliverErrMsgKeyNotProvidedkeyAfi
ifDeliverErrMsgKeyBNotProvidedDeliverErrMsgKeyNotProvidedkeyBfi
ifDeliverErrMsgKeyAProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyAfi
ifDeliverErrMsgKeyBProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyBfi
%
% Perform the adding to FOO or BAR in case no error-messages were
% printed:
%
ifDeliverErrMsgKeyANotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyBNotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyAProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyBProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyANeitherTrueNorFalseexpandafterUD@secondoftwoelseexpandafterUD@firstoftwofi%
ifKeyAsValueIsTrueKeyAsValueIsTruetrueexpandafterUD@firstoftwoelseexpandafterUD@secondoftwofi%
expandafterUD@Exchangeexpandafter%
expandafterMyScratchtoksexpandaftertheMyScratchtoks%
%
MyScratchtoksexpandafterFOO%
expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter(MyKeyBvalue)%
edefFOOtheMyScratchtoks%
%
%
expandafterUD@Exchangeexpandafter%
expandafterMyScratchtoksexpandaftertheMyScratchtoks%
%
MyScratchtoksexpandafterBAR%
expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter[MyKeyBvalue]%
edefBARtheMyScratchtoks%
%
%
%
%
%
%
%
endgroup
%
makeatother
begindocument
noindent
verb|macrokeyA=true, keyB=Value in first call| - now you have:\
macrokeyA=true, keyB=Value in first call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=yes, keyB=Value in second call| - now you have:\
macrokeyA=yes, keyB=Value in second call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=0, keyB=Value in third call| - now you have:\
macrokeyA=0, keyB=Value in third call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=Off, keyB=Value in fourth call| - now you have:\
macrokeyA=Off, keyB=Value in fourth call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA, keyB=Value in fifth call| - now you have:\
macrokeyA, keyB=Value in fifth call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
% Let's trigger some error-messages:
%
%nullhrulefillnull\
%verb|macrokeyA=woozle, | - now you have:\
%macrokeyA=woozle, %
%textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
%nullhrulefillnull\
%verb|macrokeyA=1, keyA=false, keyB=Value in sixth call| - now you have:\
%macrokeyA=1, keyA=false, keyB=Value in sixth call%
%textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
enddocument

add a comment |
Some remarks not related to the problem which nonetheless may be useful:
If lines end with curly braces (
or), LaTeX usually will insert space characters at the ends of these lines. This is due to theendlinechar-thingie. These space characters will get tokenized as space-tokens (character tokens of category code 10(space) and character code 32. In (restricted) horizontal mode these space-tokens may yield visible horizontal glue. Make sure that lines end with a comment-character (%) in cases where such glue is undesired.baris already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the macro-names I use the uppercase-variantsFOOandBAR.
Some remarks related to the problem which probably may be useful:
With the keyval package you cannot have the function underlying keyB act depending on the value of keyA/depending on the result of carrying out the function underlying keyA—as you stated yourself:
The order in which these functions get carried out is not predictable.
Also it is not predictable whether these functions get carried out at all or get carried out more than once.
Instead you can define a family of keys with functions for setting some flags (which is a method of storing true/false-values or 0/1-values) and storing as macros other values that are to be used while/after evaluating the flags.
(In the example below as flags I use switches created via newif. I do so because I don't want the example to load a large amount packages. For setting flags, the packages flags and bitset, both of Heiko Oberdiek, may be of interest to you.
I assume that the "arbitrary values" for keyB are sequences of tokens which are brace-balanced. ;-) When storing such arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing as during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.
That's why I used the MyScratchtoks..-edefmacrotheMyScratchtoks-thingie: When the is carried out during edef for delivering the content of a token-register, e.g., theMyScratchtoks, the content of the token-register MyScratchtoks will be delivered and hereby hashes will be doubled.)
Then you can define a macro which does setkeys for setting flags and storing values and then does evaluate the flags for printing error-messages and in case of not having printed any error message processing the stored values accordingly.
You also need forking depending on the phrase/token-sequence provided as value to keyA:
In the example below you find DetectYesNo which does the forking by means of delimited macro arguments.
You wish to have (almost) arbitrary things added to the macro foo.
When doing this, you need to make sure that the new definition text of foo also contains the tokens that form the old definition text of foo by top-level-expanding foo.
There are various methods for this. The answer of Phelype Oleinik exhibits the usage of edef and unexpanded.
I tend to use expandafter.
You could be tempted to do:
expandafterdefexpandafterfooexpandafterfoo<tokens to append>
But you need to be careful with such approaches as the amount of hashes will be halved during expansion of foo.
If, e.g., foo is defined via
deffoo%
defbas##1argument of bas: ##1.%
and you do:
expandafterdefexpandafterfooexpandafter%
foo
defbat##1argument of bat: ##1.%
, then this will not yield
deffoo%
defbas##1argument of bas: ##1.%
defbat##1argument of bat: ##1.%
, but it will yield:
deffoo%
defbas#1argument of bas: #1.%
defbat##1argument of bat: ##1.%
This will be a problem and therefore will yield an error message as foo does not process parameters while foo's definition text does contain #1.
Therefore in such situations I also tend to go the MyScratchtoks..-edef...theMyScratchtoks-route:
newtoksMyScratchtoks
deffoo%
defbas##1argument of bas: ##1.%
%
% Now let's append something to `foo`:
MyScratchtoksexpandafterfoo%
MyScratchtoksexpandafter%
theMyScratchtoks
defbat#1argument of bat: #1.%
%
edeffootheMyScratchtoks%
showfoo
I used this technique in the example below.
If eTeX-extensions are available you can go the edef-unexpanded-route as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.
documentclassarticle
usepackagekeyval
makeatletter
%%----------------------------------------------------------------------
newcommandUD@firstofone[1]#1%
newcommandUD@firstoftwo[2]#1%
newcommandUD@secondoftwo[2]#2%
newcommandUD@Exchange[2]#2#1%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% UD@CheckWhetherNull<Argument which is to be checked>%
%% <Tokens to be delivered in case that argument
%% which is to be checked is empty>%
%% <Tokens to be delivered in case that argument
%% which is to be checked is not empty>%
%%
%% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
newcommandUD@CheckWhetherNull[1]%
romannumeral0expandafterUD@secondoftwostringexpandafter
UD@secondoftwoexpandafterexpandafterstring#1expandafter
UD@secondoftwostringexpandafterUD@firstoftwoexpandafter%
expandafterUD@secondoftwostringexpandafterexpandafter
UD@firstoftwo UD@secondoftwoexpandafterexpandafter
UD@firstoftwo UD@firstoftwo%
%
%%----------------------------------------------------------------------
%% Macros FOO and BAR
%%----------------------------------------------------------------------
newcommand*FOO%
newcommand*BAR%
%%----------------------------------------------------------------------
%% Flags / if-switches
%%----------------------------------------------------------------------
%% Flag: Is there a need to print an error-message about keyA not
%% being set?
newififDeliverErrMsgKeyANotProvided
DeliverErrMsgKeyANotProvidedtrue
%% Flag: Is there a need to print an error-message about keyB not
%% being set?
newififDeliverErrMsgKeyBNotProvided
DeliverErrMsgKeyBNotProvidedtrue
%% Flag: Is there a need to print an error-message about keyA
%% being provided multiple times?
newififDeliverErrMsgKeyAProvidedSeveralTimes
DeliverErrMsgKeyAProvidedSeveralTimesfalse
%% Flag: Is there a need to print an error-message about keyB
%% being provided multiple times?
newififDeliverErrMsgKeyBProvidedSeveralTimes
DeliverErrMsgKeyBProvidedSeveralTimesfalse
%% Flag: Is there a need to print an error-message about keyA
%% having a value which is not in the true/false-spectrum?
newififDeliverErrMsgKeyANeitherTrueNorFalse
DeliverErrMsgKeyANeitherTrueNorFalsefalse
%% Flag: Has keyA the value true?
newififKeyAsValueIsTrue
KeyAsValueIsTruetrue
%%----------------------------------------------------------------------
%% Place-holders for values that are to be used after flag-evaluation:
%%----------------------------------------------------------------------
newtoksMyScratchtoks
newcommandMyKeyBvalue%
%%----------------------------------------------------------------------
%% Error-messages:
%%----------------------------------------------------------------------
%% PreambleMacroError
%%......................................................................
%% This macro takes three arguments:
%% A macro name. An error message. The help information.
%% It displays the error message, and sets the error help (the result of
%% typing h to the prompt).
%%----------------------------------------------------------------------
newcommand*PreambleMacroError[3]%
GenericError%
spacespacespace@spaces@spaces@spaces
%
LaTeX Error: Inapproriate usage of macro string#1on@line.MessageBreak
(string#1 is defined in the document's preamble.)MessageBreak
Problem: #2%
%
Have a look at the comments in the preamble of this document.%
#3%
%
%%----------------------------------------------------------------------
%% Error-message in case a flag-key does not have a value of the
%% true/false-spectrum:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyNeitherTrueNorFalse[1]%
PreambleMacroErrormacroInvalid value for #1%
%PackageErrorMyPackagestringmacro: Invalid value for #1on@line%
%@latex@errorstringmacro: Invalid value for #1on@line%
%
#1 must have one of the following values:%
MessageBreak true/yes/on/1 or or false/no/off/0.%
%
%
%%----------------------------------------------------------------------
%% Error-message in case a mandatory key is not set at all:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyNotProvided[1]%
PreambleMacroErrormacroSetting for #1 is missing%
%PackageErrorMyPackagestringmacro: Setting for #1 is missingon@line%
%@latex@errorstringmacro: Setting for #1 is missingon@line%
%
Setting #1 cannot be omitted.%
%
%
%%----------------------------------------------------------------------
%% Error-message in case a keys is set more times than once:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyProvidedSeveralTimes[1]%
PreambleMacroErrormacroMore than one value for #1%
%PackageErrorMyPackagestringmacro: More than one value for #1on@line%
%@latex@errorstringmacro: More than one value for #1on@line%
%
For the sake of unambiguity provide a value for #1 exactly once.%
%
%
%%----------------------------------------------------------------------
%% DetectYesNo detects whether value is either one of
%% true/yes/on/1 or one of false/no/off/0
%% DetectYesNo<value>%
%% <tokens if value neither is "yes" nor is "no">%
%% <tokens if value is "yes">%
%% <tokens if value is "no">
%%----------------------------------------------------------------------
%% Check whether argument contains no exclamation mark which is not
%% nested in braces:
%%......................................................................
%% UD@CheckWhetherNoExclam<Argument which is to be checked>%
%% <Tokens to be delivered in case that
%% argument contains no exclamation mark>%
%% <Tokens to be delivered in case that
%% argument contains exclamation mark>%
%%
newcommandUD@GobbleToExclamlongdefUD@GobbleToExclam#1!%
newcommandUD@CheckWhetherNoExclam[1]%
expandafterUD@CheckWhetherNullexpandafterUD@GobbleToExclam#1!%
%
newcommandTrueFalseFork
longdefTrueFalseFork#1!!true!yes!on!1!false!no!off!0!#2#3!!!!#2%
newcommandDetectYesNo[1]lowercaseInnerDetectYesNo#1%
newcommandInnerDetectYesNo[4]%
romannumeral0UD@CheckWhetherNoExclam#1%
TrueFalseFork!#1!true!yes!on!1!false!no!off!0! #2%<-case #1 is empty/has no tokens
!!#1!yes!on!1!false!no!off!0! #3%<-case #1 = true
!!true!#1!on!1!false!no!off!0! #3%<-case #1 = yes
!!true!yes!#1!1!false!no!off!0! #3%<-case #1 = on
!!true!yes!on!#1!false!no!off!0! #3%<-case #1 = 1
!!true!yes!on!1!#1!no!off!0! #4%<-case #1 = false
!!true!yes!on!1!false!#1!off!0! #4%<-case #1 = no
!!true!yes!on!1!false!no!#1!0! #4%<-case #1 = off
!!true!yes!on!1!false!no!off!#1! #4%<-case #1 = 0
!!true!yes!on!1!false!no!off!0! #2%<-case #1 something else without exclamation mark
!!!!%
#2%<-case #1 = something else with exclamation-mark.
%
%%----------------------------------------------------------------------
%% Use the keys of the family MyFamily to change flags and to
%% save values that are to be used later
%%----------------------------------------------------------------------
define@keyMyFamilykeyA[true]%
DeliverErrMsgKeyANotProvidedfalse
define@keyMyFamilykeyA[true]%
DeliverErrMsgKeyAProvidedSeveralTimestrue
%
DetectYesNo#1%
DeliverErrMsgKeyANeitherTrueNorFalsetrue%
KeyAsValueIsTruetrue%
KeyAsValueIsTruefalse%
%
define@keyMyFamilykeyB%
DeliverErrMsgKeyBNotProvidedfalse
define@keyMyFamilykeyB%
DeliverErrMsgKeyBProvidedSeveralTimestrue
%
expandafterUD@Exchange
expandafter%
expandafterMyScratchtoks
expandaftertheMyScratchtoks%
MyScratchtoks#1%
edefMyKeyBvaluetheMyScratchtoks%
%
%
%%----------------------------------------------------------------------
%% Now the macro with evaluation and error-messages ad nauseam ;-) :
%%----------------------------------------------------------------------
newcommandmacro[1]%
begingroup
setkeysMyFamily#1%
%
% Print Error-Messages if necessary:
%
ifDeliverErrMsgKeyANeitherTrueNorFalseDeliverErrMsgKeyNeitherTrueNorFalsekeyAfi
ifDeliverErrMsgKeyANotProvidedDeliverErrMsgKeyNotProvidedkeyAfi
ifDeliverErrMsgKeyBNotProvidedDeliverErrMsgKeyNotProvidedkeyBfi
ifDeliverErrMsgKeyAProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyAfi
ifDeliverErrMsgKeyBProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyBfi
%
% Perform the adding to FOO or BAR in case no error-messages were
% printed:
%
ifDeliverErrMsgKeyANotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyBNotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyAProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyBProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyANeitherTrueNorFalseexpandafterUD@secondoftwoelseexpandafterUD@firstoftwofi%
ifKeyAsValueIsTrueKeyAsValueIsTruetrueexpandafterUD@firstoftwoelseexpandafterUD@secondoftwofi%
expandafterUD@Exchangeexpandafter%
expandafterMyScratchtoksexpandaftertheMyScratchtoks%
%
MyScratchtoksexpandafterFOO%
expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter(MyKeyBvalue)%
edefFOOtheMyScratchtoks%
%
%
expandafterUD@Exchangeexpandafter%
expandafterMyScratchtoksexpandaftertheMyScratchtoks%
%
MyScratchtoksexpandafterBAR%
expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter[MyKeyBvalue]%
edefBARtheMyScratchtoks%
%
%
%
%
%
%
%
endgroup
%
makeatother
begindocument
noindent
verb|macrokeyA=true, keyB=Value in first call| - now you have:\
macrokeyA=true, keyB=Value in first call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=yes, keyB=Value in second call| - now you have:\
macrokeyA=yes, keyB=Value in second call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=0, keyB=Value in third call| - now you have:\
macrokeyA=0, keyB=Value in third call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=Off, keyB=Value in fourth call| - now you have:\
macrokeyA=Off, keyB=Value in fourth call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA, keyB=Value in fifth call| - now you have:\
macrokeyA, keyB=Value in fifth call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
% Let's trigger some error-messages:
%
%nullhrulefillnull\
%verb|macrokeyA=woozle, | - now you have:\
%macrokeyA=woozle, %
%textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
%nullhrulefillnull\
%verb|macrokeyA=1, keyA=false, keyB=Value in sixth call| - now you have:\
%macrokeyA=1, keyA=false, keyB=Value in sixth call%
%textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
enddocument

Some remarks not related to the problem which nonetheless may be useful:
If lines end with curly braces (
or), LaTeX usually will insert space characters at the ends of these lines. This is due to theendlinechar-thingie. These space characters will get tokenized as space-tokens (character tokens of category code 10(space) and character code 32. In (restricted) horizontal mode these space-tokens may yield visible horizontal glue. Make sure that lines end with a comment-character (%) in cases where such glue is undesired.baris already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the macro-names I use the uppercase-variantsFOOandBAR.
Some remarks related to the problem which probably may be useful:
With the keyval package you cannot have the function underlying keyB act depending on the value of keyA/depending on the result of carrying out the function underlying keyA—as you stated yourself:
The order in which these functions get carried out is not predictable.
Also it is not predictable whether these functions get carried out at all or get carried out more than once.
Instead you can define a family of keys with functions for setting some flags (which is a method of storing true/false-values or 0/1-values) and storing as macros other values that are to be used while/after evaluating the flags.
(In the example below as flags I use switches created via newif. I do so because I don't want the example to load a large amount packages. For setting flags, the packages flags and bitset, both of Heiko Oberdiek, may be of interest to you.
I assume that the "arbitrary values" for keyB are sequences of tokens which are brace-balanced. ;-) When storing such arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing as during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.
That's why I used the MyScratchtoks..-edefmacrotheMyScratchtoks-thingie: When the is carried out during edef for delivering the content of a token-register, e.g., theMyScratchtoks, the content of the token-register MyScratchtoks will be delivered and hereby hashes will be doubled.)
Then you can define a macro which does setkeys for setting flags and storing values and then does evaluate the flags for printing error-messages and in case of not having printed any error message processing the stored values accordingly.
You also need forking depending on the phrase/token-sequence provided as value to keyA:
In the example below you find DetectYesNo which does the forking by means of delimited macro arguments.
You wish to have (almost) arbitrary things added to the macro foo.
When doing this, you need to make sure that the new definition text of foo also contains the tokens that form the old definition text of foo by top-level-expanding foo.
There are various methods for this. The answer of Phelype Oleinik exhibits the usage of edef and unexpanded.
I tend to use expandafter.
You could be tempted to do:
expandafterdefexpandafterfooexpandafterfoo<tokens to append>
But you need to be careful with such approaches as the amount of hashes will be halved during expansion of foo.
If, e.g., foo is defined via
deffoo%
defbas##1argument of bas: ##1.%
and you do:
expandafterdefexpandafterfooexpandafter%
foo
defbat##1argument of bat: ##1.%
, then this will not yield
deffoo%
defbas##1argument of bas: ##1.%
defbat##1argument of bat: ##1.%
, but it will yield:
deffoo%
defbas#1argument of bas: #1.%
defbat##1argument of bat: ##1.%
This will be a problem and therefore will yield an error message as foo does not process parameters while foo's definition text does contain #1.
Therefore in such situations I also tend to go the MyScratchtoks..-edef...theMyScratchtoks-route:
newtoksMyScratchtoks
deffoo%
defbas##1argument of bas: ##1.%
%
% Now let's append something to `foo`:
MyScratchtoksexpandafterfoo%
MyScratchtoksexpandafter%
theMyScratchtoks
defbat#1argument of bat: #1.%
%
edeffootheMyScratchtoks%
showfoo
I used this technique in the example below.
If eTeX-extensions are available you can go the edef-unexpanded-route as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.
documentclassarticle
usepackagekeyval
makeatletter
%%----------------------------------------------------------------------
newcommandUD@firstofone[1]#1%
newcommandUD@firstoftwo[2]#1%
newcommandUD@secondoftwo[2]#2%
newcommandUD@Exchange[2]#2#1%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% UD@CheckWhetherNull<Argument which is to be checked>%
%% <Tokens to be delivered in case that argument
%% which is to be checked is empty>%
%% <Tokens to be delivered in case that argument
%% which is to be checked is not empty>%
%%
%% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
newcommandUD@CheckWhetherNull[1]%
romannumeral0expandafterUD@secondoftwostringexpandafter
UD@secondoftwoexpandafterexpandafterstring#1expandafter
UD@secondoftwostringexpandafterUD@firstoftwoexpandafter%
expandafterUD@secondoftwostringexpandafterexpandafter
UD@firstoftwo UD@secondoftwoexpandafterexpandafter
UD@firstoftwo UD@firstoftwo%
%
%%----------------------------------------------------------------------
%% Macros FOO and BAR
%%----------------------------------------------------------------------
newcommand*FOO%
newcommand*BAR%
%%----------------------------------------------------------------------
%% Flags / if-switches
%%----------------------------------------------------------------------
%% Flag: Is there a need to print an error-message about keyA not
%% being set?
newififDeliverErrMsgKeyANotProvided
DeliverErrMsgKeyANotProvidedtrue
%% Flag: Is there a need to print an error-message about keyB not
%% being set?
newififDeliverErrMsgKeyBNotProvided
DeliverErrMsgKeyBNotProvidedtrue
%% Flag: Is there a need to print an error-message about keyA
%% being provided multiple times?
newififDeliverErrMsgKeyAProvidedSeveralTimes
DeliverErrMsgKeyAProvidedSeveralTimesfalse
%% Flag: Is there a need to print an error-message about keyB
%% being provided multiple times?
newififDeliverErrMsgKeyBProvidedSeveralTimes
DeliverErrMsgKeyBProvidedSeveralTimesfalse
%% Flag: Is there a need to print an error-message about keyA
%% having a value which is not in the true/false-spectrum?
newififDeliverErrMsgKeyANeitherTrueNorFalse
DeliverErrMsgKeyANeitherTrueNorFalsefalse
%% Flag: Has keyA the value true?
newififKeyAsValueIsTrue
KeyAsValueIsTruetrue
%%----------------------------------------------------------------------
%% Place-holders for values that are to be used after flag-evaluation:
%%----------------------------------------------------------------------
newtoksMyScratchtoks
newcommandMyKeyBvalue%
%%----------------------------------------------------------------------
%% Error-messages:
%%----------------------------------------------------------------------
%% PreambleMacroError
%%......................................................................
%% This macro takes three arguments:
%% A macro name. An error message. The help information.
%% It displays the error message, and sets the error help (the result of
%% typing h to the prompt).
%%----------------------------------------------------------------------
newcommand*PreambleMacroError[3]%
GenericError%
spacespacespace@spaces@spaces@spaces
%
LaTeX Error: Inapproriate usage of macro string#1on@line.MessageBreak
(string#1 is defined in the document's preamble.)MessageBreak
Problem: #2%
%
Have a look at the comments in the preamble of this document.%
#3%
%
%%----------------------------------------------------------------------
%% Error-message in case a flag-key does not have a value of the
%% true/false-spectrum:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyNeitherTrueNorFalse[1]%
PreambleMacroErrormacroInvalid value for #1%
%PackageErrorMyPackagestringmacro: Invalid value for #1on@line%
%@latex@errorstringmacro: Invalid value for #1on@line%
%
#1 must have one of the following values:%
MessageBreak true/yes/on/1 or or false/no/off/0.%
%
%
%%----------------------------------------------------------------------
%% Error-message in case a mandatory key is not set at all:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyNotProvided[1]%
PreambleMacroErrormacroSetting for #1 is missing%
%PackageErrorMyPackagestringmacro: Setting for #1 is missingon@line%
%@latex@errorstringmacro: Setting for #1 is missingon@line%
%
Setting #1 cannot be omitted.%
%
%
%%----------------------------------------------------------------------
%% Error-message in case a keys is set more times than once:
%%----------------------------------------------------------------------
newcommandDeliverErrMsgKeyProvidedSeveralTimes[1]%
PreambleMacroErrormacroMore than one value for #1%
%PackageErrorMyPackagestringmacro: More than one value for #1on@line%
%@latex@errorstringmacro: More than one value for #1on@line%
%
For the sake of unambiguity provide a value for #1 exactly once.%
%
%
%%----------------------------------------------------------------------
%% DetectYesNo detects whether value is either one of
%% true/yes/on/1 or one of false/no/off/0
%% DetectYesNo<value>%
%% <tokens if value neither is "yes" nor is "no">%
%% <tokens if value is "yes">%
%% <tokens if value is "no">
%%----------------------------------------------------------------------
%% Check whether argument contains no exclamation mark which is not
%% nested in braces:
%%......................................................................
%% UD@CheckWhetherNoExclam<Argument which is to be checked>%
%% <Tokens to be delivered in case that
%% argument contains no exclamation mark>%
%% <Tokens to be delivered in case that
%% argument contains exclamation mark>%
%%
newcommandUD@GobbleToExclamlongdefUD@GobbleToExclam#1!%
newcommandUD@CheckWhetherNoExclam[1]%
expandafterUD@CheckWhetherNullexpandafterUD@GobbleToExclam#1!%
%
newcommandTrueFalseFork
longdefTrueFalseFork#1!!true!yes!on!1!false!no!off!0!#2#3!!!!#2%
newcommandDetectYesNo[1]lowercaseInnerDetectYesNo#1%
newcommandInnerDetectYesNo[4]%
romannumeral0UD@CheckWhetherNoExclam#1%
TrueFalseFork!#1!true!yes!on!1!false!no!off!0! #2%<-case #1 is empty/has no tokens
!!#1!yes!on!1!false!no!off!0! #3%<-case #1 = true
!!true!#1!on!1!false!no!off!0! #3%<-case #1 = yes
!!true!yes!#1!1!false!no!off!0! #3%<-case #1 = on
!!true!yes!on!#1!false!no!off!0! #3%<-case #1 = 1
!!true!yes!on!1!#1!no!off!0! #4%<-case #1 = false
!!true!yes!on!1!false!#1!off!0! #4%<-case #1 = no
!!true!yes!on!1!false!no!#1!0! #4%<-case #1 = off
!!true!yes!on!1!false!no!off!#1! #4%<-case #1 = 0
!!true!yes!on!1!false!no!off!0! #2%<-case #1 something else without exclamation mark
!!!!%
#2%<-case #1 = something else with exclamation-mark.
%
%%----------------------------------------------------------------------
%% Use the keys of the family MyFamily to change flags and to
%% save values that are to be used later
%%----------------------------------------------------------------------
define@keyMyFamilykeyA[true]%
DeliverErrMsgKeyANotProvidedfalse
define@keyMyFamilykeyA[true]%
DeliverErrMsgKeyAProvidedSeveralTimestrue
%
DetectYesNo#1%
DeliverErrMsgKeyANeitherTrueNorFalsetrue%
KeyAsValueIsTruetrue%
KeyAsValueIsTruefalse%
%
define@keyMyFamilykeyB%
DeliverErrMsgKeyBNotProvidedfalse
define@keyMyFamilykeyB%
DeliverErrMsgKeyBProvidedSeveralTimestrue
%
expandafterUD@Exchange
expandafter%
expandafterMyScratchtoks
expandaftertheMyScratchtoks%
MyScratchtoks#1%
edefMyKeyBvaluetheMyScratchtoks%
%
%
%%----------------------------------------------------------------------
%% Now the macro with evaluation and error-messages ad nauseam ;-) :
%%----------------------------------------------------------------------
newcommandmacro[1]%
begingroup
setkeysMyFamily#1%
%
% Print Error-Messages if necessary:
%
ifDeliverErrMsgKeyANeitherTrueNorFalseDeliverErrMsgKeyNeitherTrueNorFalsekeyAfi
ifDeliverErrMsgKeyANotProvidedDeliverErrMsgKeyNotProvidedkeyAfi
ifDeliverErrMsgKeyBNotProvidedDeliverErrMsgKeyNotProvidedkeyBfi
ifDeliverErrMsgKeyAProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyAfi
ifDeliverErrMsgKeyBProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyBfi
%
% Perform the adding to FOO or BAR in case no error-messages were
% printed:
%
ifDeliverErrMsgKeyANotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyBNotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyAProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyBProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
ifDeliverErrMsgKeyANeitherTrueNorFalseexpandafterUD@secondoftwoelseexpandafterUD@firstoftwofi%
ifKeyAsValueIsTrueKeyAsValueIsTruetrueexpandafterUD@firstoftwoelseexpandafterUD@secondoftwofi%
expandafterUD@Exchangeexpandafter%
expandafterMyScratchtoksexpandaftertheMyScratchtoks%
%
MyScratchtoksexpandafterFOO%
expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter(MyKeyBvalue)%
edefFOOtheMyScratchtoks%
%
%
expandafterUD@Exchangeexpandafter%
expandafterMyScratchtoksexpandaftertheMyScratchtoks%
%
MyScratchtoksexpandafterBAR%
expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter[MyKeyBvalue]%
edefBARtheMyScratchtoks%
%
%
%
%
%
%
%
endgroup
%
makeatother
begindocument
noindent
verb|macrokeyA=true, keyB=Value in first call| - now you have:\
macrokeyA=true, keyB=Value in first call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=yes, keyB=Value in second call| - now you have:\
macrokeyA=yes, keyB=Value in second call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=0, keyB=Value in third call| - now you have:\
macrokeyA=0, keyB=Value in third call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA=Off, keyB=Value in fourth call| - now you have:\
macrokeyA=Off, keyB=Value in fourth call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
nullhrulefillnull\
verb|macrokeyA, keyB=Value in fifth call| - now you have:\
macrokeyA, keyB=Value in fifth call%
textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
% Let's trigger some error-messages:
%
%nullhrulefillnull\
%verb|macrokeyA=woozle, | - now you have:\
%macrokeyA=woozle, %
%textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
%
%nullhrulefillnull\
%verb|macrokeyA=1, keyA=false, keyB=Value in sixth call| - now you have:\
%macrokeyA=1, keyA=false, keyB=Value in sixth call%
%textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
enddocument

edited 3 hours ago
answered 8 hours ago
Ulrich DiezUlrich Diez
6,060620
6,060620
add a comment |
add a comment |
Jevdokija is a new contributor. Be nice, and check out our Code of Conduct.
Jevdokija is a new contributor. Be nice, and check out our Code of Conduct.
Jevdokija is a new contributor. Be nice, and check out our Code of Conduct.
Jevdokija is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to TeX - LaTeX Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f492597%2fkeyval-function-for-keyb-should-act-dependent-on-value-of-keya-how-to-do-thi%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown