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

Why Does Mama Coco Look Old After Going to the Other World?

How to befriend someone who doesn't like to talk?

2019 gold coins to share

Is using 'echo' to display attacker-controlled data on the terminal dangerous?

Can a human be transformed into a Mind Flayer?

Are polynomials with the same roots identical?

Varying the size of dots in a plot according to information contained in list

Who voices the small round football sized demon in Good Omens?

Live action TV show where High school Kids go into the virtual world and have to clear levels

How to avoid typing 'git' at the begining of every Git command

Why did Intel abandon unified CPU cache?

If I leave the US through an airport, do I have to return through the same airport?

Fermat's statement about the ancients: How serious was he?

Does putting salt first make it easier for attacker to bruteforce the hash?

Why is long-term living in Almost-Earth causing severe health problems?

What aircraft was used as Air Force One for the flight between Southampton and Shannon?

Was Self-modifying-code possible just using BASIC?

tabular: caption and align problem

How do free-speech protections in the United States apply in public to corporate misrepresentations?

Should I put programming books I wrote a few years ago on my resume?

A map of non-pathological topology?

How to make the letter "K" that denote Krylov space

Why is Na5 not played in this line of the French Defense, Advance Variation?

Section numbering in binary



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













3















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.




  • keyA may take values true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).


  • keyA default 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.










share|improve this question




























    3















    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.




    • keyA may take values true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).


    • keyA default 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.










    share|improve this question


























      3












      3








      3








      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.




      • keyA may take values true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).


      • keyA default 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.










      share|improve this question
















      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.




      • keyA may take values true/yes/on/1 (yes-branch) and false/no/off/0 (no-branch).


      • keyA default 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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited May 25 at 14:35









      Phelype Oleinik

      29.5k64897




      29.5k64897










      asked May 25 at 13:27









      JevdokijaJevdokija

      232




      232




















          2 Answers
          2






          active

          oldest

          votes


















          3














          Some remarks not related to the problem which nonetheless may be useful:




          1. While line-wise and then character-wise reading/processing a .tex-input-file, (La)TeX usually will first remove all space characters (ASCII 32) at the right end of a line of input and then append a return character (ASCII 13) at the right end of that line of input. This is due to the integer parameter endlinechar usually having the value 13. Then (La)TeX will "look" at that line of input character by character and hereby take that line for a set of instructions for appending tokens (character tokens and control sequence tokens) to the token stream. The return character usually has category code 5 (end of line). After tokenizing a curly brace ( or ), (La)TeX's reading apparatus usually is in state M (middle of line). (Generally (La)TeX's reading apparatus will be in state M after tokenizing a control symbol token (other than control space; a control symbol token is a control sequence token whose name consists of a single character which does not have category code 11(letter)) or a character-token other than a space token.)



            When (La)TeX encounters a character of category code 5 (end of line) while in state M, (La)TeX will append a space token (character token of category code 10(space) and character code 32) to the token stream.

            In (restricted) horizontal mode such a space token may yield visible horizontal glue.



            Thus: Make sure that lines of input, where the last thing that during reading and tokenizing shall be appended to the token stream either is a control symbol token (control sequence token whose name consists o a single character which is not of category code 11(letter)) or is a character token, end with a comment character (%) in situations where such horizontal glue is undesired.

            As a rule of thumb make sure that lines of input, where the last thing that shall be appended to the token stream is some brace token (-character-token of category code 1 (begin group) or -character-token of category code 2 (end group)), end with a comment-character (%).



          2. bar is already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the names of the corresponding macros I use the all caps variants FOO and BAR.



          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 underlying "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.



          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.



          In the example below as flags I use switches created via newif. I do so because with that example I don't wish LaTeX to load a large amount of packages. For setting flags, the packages flags and bitset, both of Heiko Oberdiek, may be of interest to you.



          Processing comma separated key-value-lists is about processing macro arguments.

          Braces need to be balanced with macro arguments.

          Therefore I assume that the "arbitrary values" for keyB are sequences of tokens which are brace balanced. ;-)



          When storing such (almost) arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing. The reason is: You retrieve these token sequences by expanding these macros, while during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.



          For achieving the doubling of hashes you can use the fact that when the delivers the content of a token register during an edef, the hashes therein will be doubled.

          In the example below LaTeX 2e's reserved scratch token register toks@ is used according to the pattern toks@⟨tokens⟩...edefmacrothetoks@.



          If e-TeX extensions are available, you can do edefmacrounexpanded⟨tokens⟩

          as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.




          You also need forking depending on the phrase/depending on the 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 appended to the definition text of 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.



          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. This may lead to problems.



          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.



          For achieving the hash-doubling you can again apply either the-expansion on a token register (without e-TeX extensions) or unexpanded (this requires e-TeX extensions) during edef, but this time you need to combine that with expandafter for obtaining the expansion of foo:



          Without e-TeX extensions, using LaTeX 2e's scratch token register toks@, you can do something like:



          deffoo%
          defbas##1argument of bas: ##1.%
          %
          % Now let's apply expandafter for obtaining the expansion of `foo`:
          toks@expandafter%
          foo
          defbat#1argument of bat: #1.%
          %
          edeffoothetoks@%
          showfoo


          But the scratch token register toks@ is reserved for the LaTeX 2e-kernel. This means that it should be reset before any token defined in the kernel will be carried out. This can be achieved by adding a bit of exchange-trickery:



          longdefexchange#1#2#2#1%
          deffoo%
          defbas##1argument of bas: ##1.%
          %
          expandafterexchangeexpandafter%
          expandaftertoks@expandafterthetoks@%
          %
          % Now let's apply expandafter for obtaining the expansion of `foo`:
          toks@expandafter%
          foo
          defbat#1argument of bat: #1.%
          %
          edeffoothetoks@%
          %
          showfoo


          When e-TeX extensions are available, you can use a combination of edef, expandafter and unexpanded as shown in the answer of Phelype Oleinik:



          deffoo%
          defbas##1argument of bas: ##1.%
          %
          edeffoo%
          unexpandedexpandafter%
          foo
          defbat#1argument of bat: #1.%
          %
          %
          showfoo




          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%
          %
          %%
          %% A variant using e-TeX extensions could be:
          %%
          %%newcommandUD@CheckWhetherNull[1]%
          %% romannumeral0ifrelaxdetokenize#1relax
          %% expandafterUD@firstoftwoelseexpandafterUD@secondoftwofi
          %% UD@firstoftwoexpandafter UD@firstoftwo%
          %% UD@firstoftwoexpandafter UD@secondoftwo%
          %%%
          %%----------------------------------------------------------------------
          %% AppendTokensToMacrotokensmacro%
          %% Appends tokens to definition text of macro.%
          %% (toks@ is a reserved scratch-token-register in LaTeX 2e.
          %% Thus when using it it must be ensured to reset it afterwards.)
          %%----------------------------------------------------------------------
          newcommandAppendTokensToMacro[2]%
          expandafterUD@Exchangeexpandafter%
          expandaftertoks@expandafterthetoks@%
          %
          toks@expandafter#2#1%
          edef#2thetoks@%
          %
          %
          %%
          %% A variant using e-TeX extensions could be:
          %%
          %%newcommandAppendTokensToMacro[2]%
          %% edef#2unexpandedexpandafter#2#1%
          %%%
          %%----------------------------------------------------------------------
          %% 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:
          %%----------------------------------------------------------------------
          newcommand*MyKeyBvalue%
          %%----------------------------------------------------------------------
          %% 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 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
          %
          AppendTokensToMacro#1MyKeyBvalue%
          %
          %%----------------------------------------------------------------------
          %% 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%
          expandafterendgroupexpandafterAppendTokensToMacroexpandafterexpandafter(MyKeyBvalue)FOO%
          %
          expandafterendgroupexpandafterAppendTokensToMacroexpandafterexpandafter[MyKeyBvalue]BAR%
          %
          %
          %
          %
          %
          %
          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


          enter image description here






          share|improve this answer
































            4














            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





            share|improve this answer

























              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
              );



              );













              draft saved

              draft discarded


















              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









              3














              Some remarks not related to the problem which nonetheless may be useful:




              1. While line-wise and then character-wise reading/processing a .tex-input-file, (La)TeX usually will first remove all space characters (ASCII 32) at the right end of a line of input and then append a return character (ASCII 13) at the right end of that line of input. This is due to the integer parameter endlinechar usually having the value 13. Then (La)TeX will "look" at that line of input character by character and hereby take that line for a set of instructions for appending tokens (character tokens and control sequence tokens) to the token stream. The return character usually has category code 5 (end of line). After tokenizing a curly brace ( or ), (La)TeX's reading apparatus usually is in state M (middle of line). (Generally (La)TeX's reading apparatus will be in state M after tokenizing a control symbol token (other than control space; a control symbol token is a control sequence token whose name consists of a single character which does not have category code 11(letter)) or a character-token other than a space token.)



                When (La)TeX encounters a character of category code 5 (end of line) while in state M, (La)TeX will append a space token (character token of category code 10(space) and character code 32) to the token stream.

                In (restricted) horizontal mode such a space token may yield visible horizontal glue.



                Thus: Make sure that lines of input, where the last thing that during reading and tokenizing shall be appended to the token stream either is a control symbol token (control sequence token whose name consists o a single character which is not of category code 11(letter)) or is a character token, end with a comment character (%) in situations where such horizontal glue is undesired.

                As a rule of thumb make sure that lines of input, where the last thing that shall be appended to the token stream is some brace token (-character-token of category code 1 (begin group) or -character-token of category code 2 (end group)), end with a comment-character (%).



              2. bar is already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the names of the corresponding macros I use the all caps variants FOO and BAR.



              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 underlying "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.



              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.



              In the example below as flags I use switches created via newif. I do so because with that example I don't wish LaTeX to load a large amount of packages. For setting flags, the packages flags and bitset, both of Heiko Oberdiek, may be of interest to you.



              Processing comma separated key-value-lists is about processing macro arguments.

              Braces need to be balanced with macro arguments.

              Therefore I assume that the "arbitrary values" for keyB are sequences of tokens which are brace balanced. ;-)



              When storing such (almost) arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing. The reason is: You retrieve these token sequences by expanding these macros, while during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.



              For achieving the doubling of hashes you can use the fact that when the delivers the content of a token register during an edef, the hashes therein will be doubled.

              In the example below LaTeX 2e's reserved scratch token register toks@ is used according to the pattern toks@&langle;tokens&rangle;...edefmacrothetoks@.



              If e-TeX extensions are available, you can do edefmacrounexpanded&langle;tokens&rangle;

              as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.




              You also need forking depending on the phrase/depending on the 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 appended to the definition text of 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.



              You could be tempted to do:



              expandafterdefexpandafterfooexpandafterfoo&langle;tokens to append&rangle;


              But you need to be careful with such approaches as the amount of hashes will be halved during expansion of foo. This may lead to problems.



              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.



              For achieving the hash-doubling you can again apply either the-expansion on a token register (without e-TeX extensions) or unexpanded (this requires e-TeX extensions) during edef, but this time you need to combine that with expandafter for obtaining the expansion of foo:



              Without e-TeX extensions, using LaTeX 2e's scratch token register toks@, you can do something like:



              deffoo%
              defbas##1argument of bas: ##1.%
              %
              % Now let's apply expandafter for obtaining the expansion of `foo`:
              toks@expandafter%
              foo
              defbat#1argument of bat: #1.%
              %
              edeffoothetoks@%
              showfoo


              But the scratch token register toks@ is reserved for the LaTeX 2e-kernel. This means that it should be reset before any token defined in the kernel will be carried out. This can be achieved by adding a bit of exchange-trickery:



              longdefexchange#1#2#2#1%
              deffoo%
              defbas##1argument of bas: ##1.%
              %
              expandafterexchangeexpandafter%
              expandaftertoks@expandafterthetoks@%
              %
              % Now let's apply expandafter for obtaining the expansion of `foo`:
              toks@expandafter%
              foo
              defbat#1argument of bat: #1.%
              %
              edeffoothetoks@%
              %
              showfoo


              When e-TeX extensions are available, you can use a combination of edef, expandafter and unexpanded as shown in the answer of Phelype Oleinik:



              deffoo%
              defbas##1argument of bas: ##1.%
              %
              edeffoo%
              unexpandedexpandafter%
              foo
              defbat#1argument of bat: #1.%
              %
              %
              showfoo




              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%
              %
              %%
              %% A variant using e-TeX extensions could be:
              %%
              %%newcommandUD@CheckWhetherNull[1]%
              %% romannumeral0ifrelaxdetokenize#1relax
              %% expandafterUD@firstoftwoelseexpandafterUD@secondoftwofi
              %% UD@firstoftwoexpandafter UD@firstoftwo%
              %% UD@firstoftwoexpandafter UD@secondoftwo%
              %%%
              %%----------------------------------------------------------------------
              %% AppendTokensToMacrotokensmacro%
              %% Appends tokens to definition text of macro.%
              %% (toks@ is a reserved scratch-token-register in LaTeX 2e.
              %% Thus when using it it must be ensured to reset it afterwards.)
              %%----------------------------------------------------------------------
              newcommandAppendTokensToMacro[2]%
              expandafterUD@Exchangeexpandafter%
              expandaftertoks@expandafterthetoks@%
              %
              toks@expandafter#2#1%
              edef#2thetoks@%
              %
              %
              %%
              %% A variant using e-TeX extensions could be:
              %%
              %%newcommandAppendTokensToMacro[2]%
              %% edef#2unexpandedexpandafter#2#1%
              %%%
              %%----------------------------------------------------------------------
              %% 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:
              %%----------------------------------------------------------------------
              newcommand*MyKeyBvalue%
              %%----------------------------------------------------------------------
              %% 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 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
              %
              AppendTokensToMacro#1MyKeyBvalue%
              %
              %%----------------------------------------------------------------------
              %% 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%
              expandafterendgroupexpandafterAppendTokensToMacroexpandafterexpandafter(MyKeyBvalue)FOO%
              %
              expandafterendgroupexpandafterAppendTokensToMacroexpandafterexpandafter[MyKeyBvalue]BAR%
              %
              %
              %
              %
              %
              %
              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


              enter image description here






              share|improve this answer





























                3














                Some remarks not related to the problem which nonetheless may be useful:




                1. While line-wise and then character-wise reading/processing a .tex-input-file, (La)TeX usually will first remove all space characters (ASCII 32) at the right end of a line of input and then append a return character (ASCII 13) at the right end of that line of input. This is due to the integer parameter endlinechar usually having the value 13. Then (La)TeX will "look" at that line of input character by character and hereby take that line for a set of instructions for appending tokens (character tokens and control sequence tokens) to the token stream. The return character usually has category code 5 (end of line). After tokenizing a curly brace ( or ), (La)TeX's reading apparatus usually is in state M (middle of line). (Generally (La)TeX's reading apparatus will be in state M after tokenizing a control symbol token (other than control space; a control symbol token is a control sequence token whose name consists of a single character which does not have category code 11(letter)) or a character-token other than a space token.)



                  When (La)TeX encounters a character of category code 5 (end of line) while in state M, (La)TeX will append a space token (character token of category code 10(space) and character code 32) to the token stream.

                  In (restricted) horizontal mode such a space token may yield visible horizontal glue.



                  Thus: Make sure that lines of input, where the last thing that during reading and tokenizing shall be appended to the token stream either is a control symbol token (control sequence token whose name consists o a single character which is not of category code 11(letter)) or is a character token, end with a comment character (%) in situations where such horizontal glue is undesired.

                  As a rule of thumb make sure that lines of input, where the last thing that shall be appended to the token stream is some brace token (-character-token of category code 1 (begin group) or -character-token of category code 2 (end group)), end with a comment-character (%).



                2. bar is already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the names of the corresponding macros I use the all caps variants FOO and BAR.



                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 underlying "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.



                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.



                In the example below as flags I use switches created via newif. I do so because with that example I don't wish LaTeX to load a large amount of packages. For setting flags, the packages flags and bitset, both of Heiko Oberdiek, may be of interest to you.



                Processing comma separated key-value-lists is about processing macro arguments.

                Braces need to be balanced with macro arguments.

                Therefore I assume that the "arbitrary values" for keyB are sequences of tokens which are brace balanced. ;-)



                When storing such (almost) arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing. The reason is: You retrieve these token sequences by expanding these macros, while during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.



                For achieving the doubling of hashes you can use the fact that when the delivers the content of a token register during an edef, the hashes therein will be doubled.

                In the example below LaTeX 2e's reserved scratch token register toks@ is used according to the pattern toks@&langle;tokens&rangle;...edefmacrothetoks@.



                If e-TeX extensions are available, you can do edefmacrounexpanded&langle;tokens&rangle;

                as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.




                You also need forking depending on the phrase/depending on the 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 appended to the definition text of 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.



                You could be tempted to do:



                expandafterdefexpandafterfooexpandafterfoo&langle;tokens to append&rangle;


                But you need to be careful with such approaches as the amount of hashes will be halved during expansion of foo. This may lead to problems.



                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.



                For achieving the hash-doubling you can again apply either the-expansion on a token register (without e-TeX extensions) or unexpanded (this requires e-TeX extensions) during edef, but this time you need to combine that with expandafter for obtaining the expansion of foo:



                Without e-TeX extensions, using LaTeX 2e's scratch token register toks@, you can do something like:



                deffoo%
                defbas##1argument of bas: ##1.%
                %
                % Now let's apply expandafter for obtaining the expansion of `foo`:
                toks@expandafter%
                foo
                defbat#1argument of bat: #1.%
                %
                edeffoothetoks@%
                showfoo


                But the scratch token register toks@ is reserved for the LaTeX 2e-kernel. This means that it should be reset before any token defined in the kernel will be carried out. This can be achieved by adding a bit of exchange-trickery:



                longdefexchange#1#2#2#1%
                deffoo%
                defbas##1argument of bas: ##1.%
                %
                expandafterexchangeexpandafter%
                expandaftertoks@expandafterthetoks@%
                %
                % Now let's apply expandafter for obtaining the expansion of `foo`:
                toks@expandafter%
                foo
                defbat#1argument of bat: #1.%
                %
                edeffoothetoks@%
                %
                showfoo


                When e-TeX extensions are available, you can use a combination of edef, expandafter and unexpanded as shown in the answer of Phelype Oleinik:



                deffoo%
                defbas##1argument of bas: ##1.%
                %
                edeffoo%
                unexpandedexpandafter%
                foo
                defbat#1argument of bat: #1.%
                %
                %
                showfoo




                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%
                %
                %%
                %% A variant using e-TeX extensions could be:
                %%
                %%newcommandUD@CheckWhetherNull[1]%
                %% romannumeral0ifrelaxdetokenize#1relax
                %% expandafterUD@firstoftwoelseexpandafterUD@secondoftwofi
                %% UD@firstoftwoexpandafter UD@firstoftwo%
                %% UD@firstoftwoexpandafter UD@secondoftwo%
                %%%
                %%----------------------------------------------------------------------
                %% AppendTokensToMacrotokensmacro%
                %% Appends tokens to definition text of macro.%
                %% (toks@ is a reserved scratch-token-register in LaTeX 2e.
                %% Thus when using it it must be ensured to reset it afterwards.)
                %%----------------------------------------------------------------------
                newcommandAppendTokensToMacro[2]%
                expandafterUD@Exchangeexpandafter%
                expandaftertoks@expandafterthetoks@%
                %
                toks@expandafter#2#1%
                edef#2thetoks@%
                %
                %
                %%
                %% A variant using e-TeX extensions could be:
                %%
                %%newcommandAppendTokensToMacro[2]%
                %% edef#2unexpandedexpandafter#2#1%
                %%%
                %%----------------------------------------------------------------------
                %% 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:
                %%----------------------------------------------------------------------
                newcommand*MyKeyBvalue%
                %%----------------------------------------------------------------------
                %% 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 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
                %
                AppendTokensToMacro#1MyKeyBvalue%
                %
                %%----------------------------------------------------------------------
                %% 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%
                expandafterendgroupexpandafterAppendTokensToMacroexpandafterexpandafter(MyKeyBvalue)FOO%
                %
                expandafterendgroupexpandafterAppendTokensToMacroexpandafterexpandafter[MyKeyBvalue]BAR%
                %
                %
                %
                %
                %
                %
                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


                enter image description here






                share|improve this answer



























                  3












                  3








                  3







                  Some remarks not related to the problem which nonetheless may be useful:




                  1. While line-wise and then character-wise reading/processing a .tex-input-file, (La)TeX usually will first remove all space characters (ASCII 32) at the right end of a line of input and then append a return character (ASCII 13) at the right end of that line of input. This is due to the integer parameter endlinechar usually having the value 13. Then (La)TeX will "look" at that line of input character by character and hereby take that line for a set of instructions for appending tokens (character tokens and control sequence tokens) to the token stream. The return character usually has category code 5 (end of line). After tokenizing a curly brace ( or ), (La)TeX's reading apparatus usually is in state M (middle of line). (Generally (La)TeX's reading apparatus will be in state M after tokenizing a control symbol token (other than control space; a control symbol token is a control sequence token whose name consists of a single character which does not have category code 11(letter)) or a character-token other than a space token.)



                    When (La)TeX encounters a character of category code 5 (end of line) while in state M, (La)TeX will append a space token (character token of category code 10(space) and character code 32) to the token stream.

                    In (restricted) horizontal mode such a space token may yield visible horizontal glue.



                    Thus: Make sure that lines of input, where the last thing that during reading and tokenizing shall be appended to the token stream either is a control symbol token (control sequence token whose name consists o a single character which is not of category code 11(letter)) or is a character token, end with a comment character (%) in situations where such horizontal glue is undesired.

                    As a rule of thumb make sure that lines of input, where the last thing that shall be appended to the token stream is some brace token (-character-token of category code 1 (begin group) or -character-token of category code 2 (end group)), end with a comment-character (%).



                  2. bar is already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the names of the corresponding macros I use the all caps variants FOO and BAR.



                  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 underlying "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.



                  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.



                  In the example below as flags I use switches created via newif. I do so because with that example I don't wish LaTeX to load a large amount of packages. For setting flags, the packages flags and bitset, both of Heiko Oberdiek, may be of interest to you.



                  Processing comma separated key-value-lists is about processing macro arguments.

                  Braces need to be balanced with macro arguments.

                  Therefore I assume that the "arbitrary values" for keyB are sequences of tokens which are brace balanced. ;-)



                  When storing such (almost) arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing. The reason is: You retrieve these token sequences by expanding these macros, while during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.



                  For achieving the doubling of hashes you can use the fact that when the delivers the content of a token register during an edef, the hashes therein will be doubled.

                  In the example below LaTeX 2e's reserved scratch token register toks@ is used according to the pattern toks@&langle;tokens&rangle;...edefmacrothetoks@.



                  If e-TeX extensions are available, you can do edefmacrounexpanded&langle;tokens&rangle;

                  as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.




                  You also need forking depending on the phrase/depending on the 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 appended to the definition text of 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.



                  You could be tempted to do:



                  expandafterdefexpandafterfooexpandafterfoo&langle;tokens to append&rangle;


                  But you need to be careful with such approaches as the amount of hashes will be halved during expansion of foo. This may lead to problems.



                  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.



                  For achieving the hash-doubling you can again apply either the-expansion on a token register (without e-TeX extensions) or unexpanded (this requires e-TeX extensions) during edef, but this time you need to combine that with expandafter for obtaining the expansion of foo:



                  Without e-TeX extensions, using LaTeX 2e's scratch token register toks@, you can do something like:



                  deffoo%
                  defbas##1argument of bas: ##1.%
                  %
                  % Now let's apply expandafter for obtaining the expansion of `foo`:
                  toks@expandafter%
                  foo
                  defbat#1argument of bat: #1.%
                  %
                  edeffoothetoks@%
                  showfoo


                  But the scratch token register toks@ is reserved for the LaTeX 2e-kernel. This means that it should be reset before any token defined in the kernel will be carried out. This can be achieved by adding a bit of exchange-trickery:



                  longdefexchange#1#2#2#1%
                  deffoo%
                  defbas##1argument of bas: ##1.%
                  %
                  expandafterexchangeexpandafter%
                  expandaftertoks@expandafterthetoks@%
                  %
                  % Now let's apply expandafter for obtaining the expansion of `foo`:
                  toks@expandafter%
                  foo
                  defbat#1argument of bat: #1.%
                  %
                  edeffoothetoks@%
                  %
                  showfoo


                  When e-TeX extensions are available, you can use a combination of edef, expandafter and unexpanded as shown in the answer of Phelype Oleinik:



                  deffoo%
                  defbas##1argument of bas: ##1.%
                  %
                  edeffoo%
                  unexpandedexpandafter%
                  foo
                  defbat#1argument of bat: #1.%
                  %
                  %
                  showfoo




                  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%
                  %
                  %%
                  %% A variant using e-TeX extensions could be:
                  %%
                  %%newcommandUD@CheckWhetherNull[1]%
                  %% romannumeral0ifrelaxdetokenize#1relax
                  %% expandafterUD@firstoftwoelseexpandafterUD@secondoftwofi
                  %% UD@firstoftwoexpandafter UD@firstoftwo%
                  %% UD@firstoftwoexpandafter UD@secondoftwo%
                  %%%
                  %%----------------------------------------------------------------------
                  %% AppendTokensToMacrotokensmacro%
                  %% Appends tokens to definition text of macro.%
                  %% (toks@ is a reserved scratch-token-register in LaTeX 2e.
                  %% Thus when using it it must be ensured to reset it afterwards.)
                  %%----------------------------------------------------------------------
                  newcommandAppendTokensToMacro[2]%
                  expandafterUD@Exchangeexpandafter%
                  expandaftertoks@expandafterthetoks@%
                  %
                  toks@expandafter#2#1%
                  edef#2thetoks@%
                  %
                  %
                  %%
                  %% A variant using e-TeX extensions could be:
                  %%
                  %%newcommandAppendTokensToMacro[2]%
                  %% edef#2unexpandedexpandafter#2#1%
                  %%%
                  %%----------------------------------------------------------------------
                  %% 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:
                  %%----------------------------------------------------------------------
                  newcommand*MyKeyBvalue%
                  %%----------------------------------------------------------------------
                  %% 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 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
                  %
                  AppendTokensToMacro#1MyKeyBvalue%
                  %
                  %%----------------------------------------------------------------------
                  %% 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%
                  expandafterendgroupexpandafterAppendTokensToMacroexpandafterexpandafter(MyKeyBvalue)FOO%
                  %
                  expandafterendgroupexpandafterAppendTokensToMacroexpandafterexpandafter[MyKeyBvalue]BAR%
                  %
                  %
                  %
                  %
                  %
                  %
                  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


                  enter image description here






                  share|improve this answer















                  Some remarks not related to the problem which nonetheless may be useful:




                  1. While line-wise and then character-wise reading/processing a .tex-input-file, (La)TeX usually will first remove all space characters (ASCII 32) at the right end of a line of input and then append a return character (ASCII 13) at the right end of that line of input. This is due to the integer parameter endlinechar usually having the value 13. Then (La)TeX will "look" at that line of input character by character and hereby take that line for a set of instructions for appending tokens (character tokens and control sequence tokens) to the token stream. The return character usually has category code 5 (end of line). After tokenizing a curly brace ( or ), (La)TeX's reading apparatus usually is in state M (middle of line). (Generally (La)TeX's reading apparatus will be in state M after tokenizing a control symbol token (other than control space; a control symbol token is a control sequence token whose name consists of a single character which does not have category code 11(letter)) or a character-token other than a space token.)



                    When (La)TeX encounters a character of category code 5 (end of line) while in state M, (La)TeX will append a space token (character token of category code 10(space) and character code 32) to the token stream.

                    In (restricted) horizontal mode such a space token may yield visible horizontal glue.



                    Thus: Make sure that lines of input, where the last thing that during reading and tokenizing shall be appended to the token stream either is a control symbol token (control sequence token whose name consists o a single character which is not of category code 11(letter)) or is a character token, end with a comment character (%) in situations where such horizontal glue is undesired.

                    As a rule of thumb make sure that lines of input, where the last thing that shall be appended to the token stream is some brace token (-character-token of category code 1 (begin group) or -character-token of category code 2 (end group)), end with a comment-character (%).



                  2. bar is already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the names of the corresponding macros I use the all caps variants FOO and BAR.



                  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 underlying "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.



                  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.



                  In the example below as flags I use switches created via newif. I do so because with that example I don't wish LaTeX to load a large amount of packages. For setting flags, the packages flags and bitset, both of Heiko Oberdiek, may be of interest to you.



                  Processing comma separated key-value-lists is about processing macro arguments.

                  Braces need to be balanced with macro arguments.

                  Therefore I assume that the "arbitrary values" for keyB are sequences of tokens which are brace balanced. ;-)



                  When storing such (almost) arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing. The reason is: You retrieve these token sequences by expanding these macros, while during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.



                  For achieving the doubling of hashes you can use the fact that when the delivers the content of a token register during an edef, the hashes therein will be doubled.

                  In the example below LaTeX 2e's reserved scratch token register toks@ is used according to the pattern toks@&langle;tokens&rangle;...edefmacrothetoks@.



                  If e-TeX extensions are available, you can do edefmacrounexpanded&langle;tokens&rangle;

                  as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.




                  You also need forking depending on the phrase/depending on the 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 appended to the definition text of 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.



                  You could be tempted to do:



                  expandafterdefexpandafterfooexpandafterfoo&langle;tokens to append&rangle;


                  But you need to be careful with such approaches as the amount of hashes will be halved during expansion of foo. This may lead to problems.



                  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.



                  For achieving the hash-doubling you can again apply either the-expansion on a token register (without e-TeX extensions) or unexpanded (this requires e-TeX extensions) during edef, but this time you need to combine that with expandafter for obtaining the expansion of foo:



                  Without e-TeX extensions, using LaTeX 2e's scratch token register toks@, you can do something like:



                  deffoo%
                  defbas##1argument of bas: ##1.%
                  %
                  % Now let's apply expandafter for obtaining the expansion of `foo`:
                  toks@expandafter%
                  foo
                  defbat#1argument of bat: #1.%
                  %
                  edeffoothetoks@%
                  showfoo


                  But the scratch token register toks@ is reserved for the LaTeX 2e-kernel. This means that it should be reset before any token defined in the kernel will be carried out. This can be achieved by adding a bit of exchange-trickery:



                  longdefexchange#1#2#2#1%
                  deffoo%
                  defbas##1argument of bas: ##1.%
                  %
                  expandafterexchangeexpandafter%
                  expandaftertoks@expandafterthetoks@%
                  %
                  % Now let's apply expandafter for obtaining the expansion of `foo`:
                  toks@expandafter%
                  foo
                  defbat#1argument of bat: #1.%
                  %
                  edeffoothetoks@%
                  %
                  showfoo


                  When e-TeX extensions are available, you can use a combination of edef, expandafter and unexpanded as shown in the answer of Phelype Oleinik:



                  deffoo%
                  defbas##1argument of bas: ##1.%
                  %
                  edeffoo%
                  unexpandedexpandafter%
                  foo
                  defbat#1argument of bat: #1.%
                  %
                  %
                  showfoo




                  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%
                  %
                  %%
                  %% A variant using e-TeX extensions could be:
                  %%
                  %%newcommandUD@CheckWhetherNull[1]%
                  %% romannumeral0ifrelaxdetokenize#1relax
                  %% expandafterUD@firstoftwoelseexpandafterUD@secondoftwofi
                  %% UD@firstoftwoexpandafter UD@firstoftwo%
                  %% UD@firstoftwoexpandafter UD@secondoftwo%
                  %%%
                  %%----------------------------------------------------------------------
                  %% AppendTokensToMacrotokensmacro%
                  %% Appends tokens to definition text of macro.%
                  %% (toks@ is a reserved scratch-token-register in LaTeX 2e.
                  %% Thus when using it it must be ensured to reset it afterwards.)
                  %%----------------------------------------------------------------------
                  newcommandAppendTokensToMacro[2]%
                  expandafterUD@Exchangeexpandafter%
                  expandaftertoks@expandafterthetoks@%
                  %
                  toks@expandafter#2#1%
                  edef#2thetoks@%
                  %
                  %
                  %%
                  %% A variant using e-TeX extensions could be:
                  %%
                  %%newcommandAppendTokensToMacro[2]%
                  %% edef#2unexpandedexpandafter#2#1%
                  %%%
                  %%----------------------------------------------------------------------
                  %% 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:
                  %%----------------------------------------------------------------------
                  newcommand*MyKeyBvalue%
                  %%----------------------------------------------------------------------
                  %% 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 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
                  %
                  AppendTokensToMacro#1MyKeyBvalue%
                  %
                  %%----------------------------------------------------------------------
                  %% 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%
                  expandafterendgroupexpandafterAppendTokensToMacroexpandafterexpandafter(MyKeyBvalue)FOO%
                  %
                  expandafterendgroupexpandafterAppendTokensToMacroexpandafterexpandafter[MyKeyBvalue]BAR%
                  %
                  %
                  %
                  %
                  %
                  %
                  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


                  enter image description here







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited May 26 at 15:57

























                  answered May 25 at 14:11









                  Ulrich DiezUlrich Diez

                  6,217621




                  6,217621





















                      4














                      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





                      share|improve this answer





























                        4














                        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





                        share|improve this answer



























                          4












                          4








                          4







                          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





                          share|improve this answer















                          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






                          share|improve this answer














                          share|improve this answer



                          share|improve this answer








                          edited May 25 at 14:33

























                          answered May 25 at 13:56









                          Phelype OleinikPhelype Oleinik

                          29.5k64897




                          29.5k64897



























                              draft saved

                              draft discarded
















































                              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.




                              draft saved


                              draft discarded














                              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





















































                              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







                              Popular posts from this blog

                              Wikipedia:Vital articles Мазмуну Biography - Өмүр баян Philosophy and psychology - Философия жана психология Religion - Дин Social sciences - Коомдук илимдер Language and literature - Тил жана адабият Science - Илим Technology - Технология Arts and recreation - Искусство жана эс алуу History and geography - Тарых жана география Навигация менюсу

                              Bruxelas-Capital Índice Historia | Composición | Situación lingüística | Clima | Cidades irmandadas | Notas | Véxase tamén | Menú de navegacióneO uso das linguas en Bruxelas e a situación do neerlandés"Rexión de Bruxelas Capital"o orixinalSitio da rexiónPáxina de Bruselas no sitio da Oficina de Promoción Turística de Valonia e BruxelasMapa Interactivo da Rexión de Bruxelas-CapitaleeWorldCat332144929079854441105155190212ID28008674080552-90000 0001 0666 3698n94104302ID540940339365017018237

                              What should I write in an apology letter, since I have decided not to join a company after accepting an offer letterShould I keep looking after accepting a job offer?What should I do when I've been verbally told I would get an offer letter, but still haven't gotten one after 4 weeks?Do I accept an offer from a company that I am not likely to join?New job hasn't confirmed starting date and I want to give current employer as much notice as possibleHow should I address my manager in my resignation letter?HR delayed background verification, now jobless as resignedNo email communication after accepting a formal written offer. How should I phrase the call?What should I do if after receiving a verbal offer letter I am informed that my written job offer is put on hold due to some internal issues?Should I inform the current employer that I am about to resign within 1-2 weeks since I have signed the offer letter and waiting for visa?What company will do, if I send their offer letter to another company