keyval - function for keyB should act dependent on value of keyA - how to do this?Test if token is a control sequenceThe definition of key default value by keyval packageUse alternate cite keys depending on document versionA list of pairs in pgfkeysExpansion problems with pgfkeysXparse macro with optional parameter breaks value for PGF foreachHow to define a function with optional key-value argumentsGenerating a table from key-value pairsHow to create and manage hierarchically dependent PGF keys?LaTeX Utility/Template for Generic Reusable Code

Count rotary dial pulses in a phone number (including letters)

My employer faked my resume to acquire projects

Simple function that simulates survey results based on sample size and probability

What are these arcade games in Ghostbusters 1984?

Looking for a soft substance that doesn't dissolve underwater

Where's this lookout in Nova Scotia?

Is it rude to call a professor by their last name with no prefix in a non-academic setting?

Are there any well known academic philosophy forums?

Where is the logic in castrating fighters?

Should breaking down something like a door be adjudicated as an attempt to beat its AC and HP, or as an ability check against a set DC?

I unknowingly submitted plagarised work

How to use " shadow " in pstricks?

Compactness of finite sets

Were pens caps holes designed to prevent death by suffocation if swallowed?

Crossing US border with music files I'm legally allowed to possess

When and what was the first 3D acceleration device ever released?

Make 24 using exactly three 3s

Simple fuzz pedal using breadboard

Defining the standard model of PA so that a space alien could understand

Why do airplanes use an axial flow jet engine instead of a more compact centrifugal jet engine?

How to remove the trailing ` in StringForm["Mean `1`", 2.2]?

Who will lead the country until there is a new Tory leader?

Pirate democracy at its finest

Would jet fuel for an F-16 or F-35 be producible during WW2?



keyval - function for keyB should act dependent on value of keyA - how to do this?


Test if token is a control sequenceThe definition of key default value by keyval packageUse alternate cite keys depending on document versionA list of pairs in pgfkeysExpansion problems with pgfkeysXparse macro with optional parameter breaks value for PGF foreachHow to define a function with optional key-value argumentsGenerating a table from key-value pairsHow to create and manage hierarchically dependent PGF keys?LaTeX Utility/Template for Generic Reusable Code













4















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









New contributor



Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.























    4















    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









    New contributor



    Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.





















      4












      4








      4








      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









      New contributor



      Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      I'm new to LaTeX. I learn and I use article class. I struggle learning working with keyval package:



      I try to write macro macro which only uses one mandatory argument where user can provide key value list.



      Possible keys should be keyA and keyB.




      • 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









      New contributor



      Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.










      share|improve this question









      New contributor



      Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.








      share|improve this question




      share|improve this question








      edited 8 hours ago









      Phelype Oleinik

      28.3k64894




      28.3k64894






      New contributor



      Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.








      asked 9 hours ago









      JevdokijaJevdokija

      211




      211




      New contributor



      Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.




      New contributor




      Jevdokija is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






















          2 Answers
          2






          active

          oldest

          votes


















          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
































            3














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



            1. If lines end with curly braces ( or ), LaTeX usually will insert space characters at the ends of these lines. This is due to the endlinechar-thingie. These space characters will get tokenized as space-tokens (character tokens of category code 10(space) and character code 32. In (restricted) horizontal mode these space-tokens may yield visible horizontal glue. Make sure that lines end with a comment-character (%) in cases where such glue is undesired.


            2. bar is already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the macro-names I use the uppercase-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 functions for setting some flags (which is a method of storing true/false-values or 0/1-values) and storing as macros other values that are to be used while/after evaluating the flags.

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

            I assume that the "arbitrary values" for keyB are sequences of tokens which are brace-balanced. ;-) When storing such arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing as during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.

            That's why I used the MyScratchtoks..-edefmacrotheMyScratchtoks-thingie: When the is carried out during edef for delivering the content of a token-register, e.g., theMyScratchtoks, the content of the token-register MyScratchtoks will be delivered and hereby hashes will be doubled.)



            Then you can define a macro which does setkeys for setting flags and storing values and then does evaluate the flags for printing error-messages and in case of not having printed any error message processing the stored values accordingly.




            You also need forking depending on the phrase/token-sequence provided as value to keyA:

            In the example below you find DetectYesNo which does the forking by means of delimited macro arguments.




            You wish to have (almost) arbitrary things added to the macro foo.



            When doing this, you need to make sure that the new definition text of foo also contains the tokens that form the old definition text of foo by top-level-expanding foo.



            There are various methods for this. The answer of Phelype Oleinik exhibits the usage of edef and unexpanded.



            I tend to use expandafter.

            You could be tempted to do:



            expandafterdefexpandafterfooexpandafterfoo<tokens to append>


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



            If, e.g., foo is defined via



            deffoo%
            defbas##1argument of bas: ##1.%



            and you do:



            expandafterdefexpandafterfooexpandafter%
            foo
            defbat##1argument of bat: ##1.%



            , then this will not yield



            deffoo%
            defbas##1argument of bas: ##1.%
            defbat##1argument of bat: ##1.%



            , but it will yield:



            deffoo%
            defbas#1argument of bas: #1.%
            defbat##1argument of bat: ##1.%



            This will be a problem and therefore will yield an error message as foo does not process parameters while foo's definition text does contain #1.



            Therefore in such situations I also tend to go the MyScratchtoks..-edef...theMyScratchtoks-route:



            newtoksMyScratchtoks
            deffoo%
            defbas##1argument of bas: ##1.%
            %
            % Now let's append something to `foo`:
            MyScratchtoksexpandafterfoo%
            MyScratchtoksexpandafter%
            theMyScratchtoks
            defbat#1argument of bat: #1.%
            %
            edeffootheMyScratchtoks%
            showfoo


            I used this technique in the example below.
            If eTeX-extensions are available you can go the edef-unexpanded-route as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.



            documentclassarticle
            usepackagekeyval

            makeatletter
            %%----------------------------------------------------------------------
            newcommandUD@firstofone[1]#1%
            newcommandUD@firstoftwo[2]#1%
            newcommandUD@secondoftwo[2]#2%
            newcommandUD@Exchange[2]#2#1%
            %%----------------------------------------------------------------------
            %% Check whether argument is empty:
            %%......................................................................
            %% UD@CheckWhetherNull<Argument which is to be checked>%
            %% <Tokens to be delivered in case that argument
            %% which is to be checked is empty>%
            %% <Tokens to be delivered in case that argument
            %% which is to be checked is not empty>%
            %%
            %% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
            %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
            newcommandUD@CheckWhetherNull[1]%
            romannumeral0expandafterUD@secondoftwostringexpandafter
            UD@secondoftwoexpandafterexpandafterstring#1expandafter
            UD@secondoftwostringexpandafterUD@firstoftwoexpandafter%
            expandafterUD@secondoftwostringexpandafterexpandafter
            UD@firstoftwo UD@secondoftwoexpandafterexpandafter
            UD@firstoftwo UD@firstoftwo%
            %
            %%----------------------------------------------------------------------
            %% Macros FOO and BAR
            %%----------------------------------------------------------------------
            newcommand*FOO%
            newcommand*BAR%
            %%----------------------------------------------------------------------
            %% Flags / if-switches
            %%----------------------------------------------------------------------
            %% Flag: Is there a need to print an error-message about keyA not
            %% being set?
            newififDeliverErrMsgKeyANotProvided
            DeliverErrMsgKeyANotProvidedtrue
            %% Flag: Is there a need to print an error-message about keyB not
            %% being set?
            newififDeliverErrMsgKeyBNotProvided
            DeliverErrMsgKeyBNotProvidedtrue
            %% Flag: Is there a need to print an error-message about keyA
            %% being provided multiple times?
            newififDeliverErrMsgKeyAProvidedSeveralTimes
            DeliverErrMsgKeyAProvidedSeveralTimesfalse
            %% Flag: Is there a need to print an error-message about keyB
            %% being provided multiple times?
            newififDeliverErrMsgKeyBProvidedSeveralTimes
            DeliverErrMsgKeyBProvidedSeveralTimesfalse
            %% Flag: Is there a need to print an error-message about keyA
            %% having a value which is not in the true/false-spectrum?
            newififDeliverErrMsgKeyANeitherTrueNorFalse
            DeliverErrMsgKeyANeitherTrueNorFalsefalse
            %% Flag: Has keyA the value true?
            newififKeyAsValueIsTrue
            KeyAsValueIsTruetrue
            %%----------------------------------------------------------------------
            %% Place-holders for values that are to be used after flag-evaluation:
            %%----------------------------------------------------------------------
            newtoksMyScratchtoks
            newcommandMyKeyBvalue%
            %%----------------------------------------------------------------------
            %% Error-messages:
            %%----------------------------------------------------------------------
            %% PreambleMacroError
            %%......................................................................
            %% This macro takes three arguments:
            %% A macro name. An error message. The help information.
            %% It displays the error message, and sets the error help (the result of
            %% typing h to the prompt).
            %%----------------------------------------------------------------------
            newcommand*PreambleMacroError[3]%
            GenericError%
            spacespacespace@spaces@spaces@spaces
            %
            LaTeX Error: Inapproriate usage of macro string#1on@line.MessageBreak
            (string#1 is defined in the document's preamble.)MessageBreak
            Problem: #2%
            %
            Have a look at the comments in the preamble of this document.%
            #3%
            %
            %%----------------------------------------------------------------------
            %% Error-message in case a flag-key does not have a value of the
            %% true/false-spectrum:
            %%----------------------------------------------------------------------
            newcommandDeliverErrMsgKeyNeitherTrueNorFalse[1]%
            PreambleMacroErrormacroInvalid value for #1%
            %PackageErrorMyPackagestringmacro: Invalid value for #1on@line%
            %@latex@errorstringmacro: Invalid value for #1on@line%
            %
            #1 must have one of the following values:%
            MessageBreak true/yes/on/1 or or false/no/off/0.%
            %
            %
            %%----------------------------------------------------------------------
            %% Error-message in case a mandatory key is not set at all:
            %%----------------------------------------------------------------------
            newcommandDeliverErrMsgKeyNotProvided[1]%
            PreambleMacroErrormacroSetting for #1 is missing%
            %PackageErrorMyPackagestringmacro: Setting for #1 is missingon@line%
            %@latex@errorstringmacro: Setting for #1 is missingon@line%
            %
            Setting #1 cannot be omitted.%
            %
            %
            %%----------------------------------------------------------------------
            %% Error-message in case a keys is set more times than once:
            %%----------------------------------------------------------------------
            newcommandDeliverErrMsgKeyProvidedSeveralTimes[1]%
            PreambleMacroErrormacroMore than one value for #1%
            %PackageErrorMyPackagestringmacro: More than one value for #1on@line%
            %@latex@errorstringmacro: More than one value for #1on@line%
            %
            For the sake of unambiguity provide a value for #1 exactly once.%
            %
            %
            %%----------------------------------------------------------------------
            %% DetectYesNo detects whether value is either one of
            %% true/yes/on/1 or one of false/no/off/0
            %% DetectYesNo<value>%
            %% <tokens if value neither is "yes" nor is "no">%
            %% <tokens if value is "yes">%
            %% <tokens if value is "no">
            %%----------------------------------------------------------------------
            %% Check whether argument contains no exclamation mark which is not
            %% nested in braces:
            %%......................................................................
            %% UD@CheckWhetherNoExclam<Argument which is to be checked>%
            %% <Tokens to be delivered in case that
            %% argument contains no exclamation mark>%
            %% <Tokens to be delivered in case that
            %% argument contains exclamation mark>%
            %%
            newcommandUD@GobbleToExclamlongdefUD@GobbleToExclam#1!%
            newcommandUD@CheckWhetherNoExclam[1]%
            expandafterUD@CheckWhetherNullexpandafterUD@GobbleToExclam#1!%
            %
            newcommandTrueFalseFork
            longdefTrueFalseFork#1!!true!yes!on!1!false!no!off!0!#2#3!!!!#2%
            newcommandDetectYesNo[1]lowercaseInnerDetectYesNo#1%
            newcommandInnerDetectYesNo[4]%
            romannumeral0UD@CheckWhetherNoExclam#1%
            TrueFalseFork!#1!true!yes!on!1!false!no!off!0! #2%<-case #1 is empty/has no tokens
            !!#1!yes!on!1!false!no!off!0! #3%<-case #1 = true
            !!true!#1!on!1!false!no!off!0! #3%<-case #1 = yes
            !!true!yes!#1!1!false!no!off!0! #3%<-case #1 = on
            !!true!yes!on!#1!false!no!off!0! #3%<-case #1 = 1
            !!true!yes!on!1!#1!no!off!0! #4%<-case #1 = false
            !!true!yes!on!1!false!#1!off!0! #4%<-case #1 = no
            !!true!yes!on!1!false!no!#1!0! #4%<-case #1 = off
            !!true!yes!on!1!false!no!off!#1! #4%<-case #1 = 0
            !!true!yes!on!1!false!no!off!0! #2%<-case #1 something else without exclamation mark
            !!!!%
            #2%<-case #1 = something else with exclamation-mark.
            %
            %%----------------------------------------------------------------------
            %% Use the keys of the family MyFamily to change flags and to
            %% save values that are to be used later
            %%----------------------------------------------------------------------
            define@keyMyFamilykeyA[true]%
            DeliverErrMsgKeyANotProvidedfalse
            define@keyMyFamilykeyA[true]%
            DeliverErrMsgKeyAProvidedSeveralTimestrue
            %
            DetectYesNo#1%
            DeliverErrMsgKeyANeitherTrueNorFalsetrue%
            KeyAsValueIsTruetrue%
            KeyAsValueIsTruefalse%
            %
            define@keyMyFamilykeyB%
            DeliverErrMsgKeyBNotProvidedfalse
            define@keyMyFamilykeyB%
            DeliverErrMsgKeyBProvidedSeveralTimestrue
            %
            expandafterUD@Exchange
            expandafter%
            expandafterMyScratchtoks
            expandaftertheMyScratchtoks%
            MyScratchtoks#1%
            edefMyKeyBvaluetheMyScratchtoks%
            %
            %
            %%----------------------------------------------------------------------
            %% Now the macro with evaluation and error-messages ad nauseam ;-) :
            %%----------------------------------------------------------------------
            newcommandmacro[1]%
            begingroup
            setkeysMyFamily#1%
            %
            % Print Error-Messages if necessary:
            %
            ifDeliverErrMsgKeyANeitherTrueNorFalseDeliverErrMsgKeyNeitherTrueNorFalsekeyAfi
            ifDeliverErrMsgKeyANotProvidedDeliverErrMsgKeyNotProvidedkeyAfi
            ifDeliverErrMsgKeyBNotProvidedDeliverErrMsgKeyNotProvidedkeyBfi
            ifDeliverErrMsgKeyAProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyAfi
            ifDeliverErrMsgKeyBProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyBfi
            %
            % Perform the adding to FOO or BAR in case no error-messages were
            % printed:
            %
            ifDeliverErrMsgKeyANotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
            ifDeliverErrMsgKeyBNotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
            ifDeliverErrMsgKeyAProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
            ifDeliverErrMsgKeyBProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
            ifDeliverErrMsgKeyANeitherTrueNorFalseexpandafterUD@secondoftwoelseexpandafterUD@firstoftwofi%
            ifKeyAsValueIsTrueKeyAsValueIsTruetrueexpandafterUD@firstoftwoelseexpandafterUD@secondoftwofi%
            expandafterUD@Exchangeexpandafter%
            expandafterMyScratchtoksexpandaftertheMyScratchtoks%
            %
            MyScratchtoksexpandafterFOO%
            expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter(MyKeyBvalue)%
            edefFOOtheMyScratchtoks%
            %
            %
            expandafterUD@Exchangeexpandafter%
            expandafterMyScratchtoksexpandaftertheMyScratchtoks%
            %
            MyScratchtoksexpandafterBAR%
            expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter[MyKeyBvalue]%
            edefBARtheMyScratchtoks%
            %
            %
            %
            %
            %
            %
            %
            endgroup
            %
            makeatother

            begindocument
            noindent
            verb|macrokeyA=true, keyB=Value in first call| - now you have:\
            macrokeyA=true, keyB=Value in first call%
            textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
            %
            nullhrulefillnull\
            verb|macrokeyA=yes, keyB=Value in second call| - now you have:\
            macrokeyA=yes, keyB=Value in second call%
            textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
            %
            nullhrulefillnull\
            verb|macrokeyA=0, keyB=Value in third call| - now you have:\
            macrokeyA=0, keyB=Value in third call%
            textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
            %
            nullhrulefillnull\
            verb|macrokeyA=Off, keyB=Value in fourth call| - now you have:\
            macrokeyA=Off, keyB=Value in fourth call%
            textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
            %
            nullhrulefillnull\
            verb|macrokeyA, keyB=Value in fifth call| - now you have:\
            macrokeyA, keyB=Value in fifth call%
            textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
            %
            % Let's trigger some error-messages:
            %
            %nullhrulefillnull\
            %verb|macrokeyA=woozle, | - now you have:\
            %macrokeyA=woozle, %
            %textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
            %
            %nullhrulefillnull\
            %verb|macrokeyA=1, keyA=false, keyB=Value in sixth call| - now you have:\
            %macrokeyA=1, keyA=false, keyB=Value in sixth call%
            %textttstringFOO: meaningFOO\stringBAR: meaningBAR\%

            enddocument


            enter image description here






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



              );






              Jevdokija is a new contributor. Be nice, and check out our Code of Conduct.









              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









              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 8 hours ago

























                  answered 8 hours ago









                  Phelype OleinikPhelype Oleinik

                  28.3k64894




                  28.3k64894





















                      3














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



                      1. If lines end with curly braces ( or ), LaTeX usually will insert space characters at the ends of these lines. This is due to the endlinechar-thingie. These space characters will get tokenized as space-tokens (character tokens of category code 10(space) and character code 32. In (restricted) horizontal mode these space-tokens may yield visible horizontal glue. Make sure that lines end with a comment-character (%) in cases where such glue is undesired.


                      2. bar is already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the macro-names I use the uppercase-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 functions for setting some flags (which is a method of storing true/false-values or 0/1-values) and storing as macros other values that are to be used while/after evaluating the flags.

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

                      I assume that the "arbitrary values" for keyB are sequences of tokens which are brace-balanced. ;-) When storing such arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing as during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.

                      That's why I used the MyScratchtoks..-edefmacrotheMyScratchtoks-thingie: When the is carried out during edef for delivering the content of a token-register, e.g., theMyScratchtoks, the content of the token-register MyScratchtoks will be delivered and hereby hashes will be doubled.)



                      Then you can define a macro which does setkeys for setting flags and storing values and then does evaluate the flags for printing error-messages and in case of not having printed any error message processing the stored values accordingly.




                      You also need forking depending on the phrase/token-sequence provided as value to keyA:

                      In the example below you find DetectYesNo which does the forking by means of delimited macro arguments.




                      You wish to have (almost) arbitrary things added to the macro foo.



                      When doing this, you need to make sure that the new definition text of foo also contains the tokens that form the old definition text of foo by top-level-expanding foo.



                      There are various methods for this. The answer of Phelype Oleinik exhibits the usage of edef and unexpanded.



                      I tend to use expandafter.

                      You could be tempted to do:



                      expandafterdefexpandafterfooexpandafterfoo<tokens to append>


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



                      If, e.g., foo is defined via



                      deffoo%
                      defbas##1argument of bas: ##1.%



                      and you do:



                      expandafterdefexpandafterfooexpandafter%
                      foo
                      defbat##1argument of bat: ##1.%



                      , then this will not yield



                      deffoo%
                      defbas##1argument of bas: ##1.%
                      defbat##1argument of bat: ##1.%



                      , but it will yield:



                      deffoo%
                      defbas#1argument of bas: #1.%
                      defbat##1argument of bat: ##1.%



                      This will be a problem and therefore will yield an error message as foo does not process parameters while foo's definition text does contain #1.



                      Therefore in such situations I also tend to go the MyScratchtoks..-edef...theMyScratchtoks-route:



                      newtoksMyScratchtoks
                      deffoo%
                      defbas##1argument of bas: ##1.%
                      %
                      % Now let's append something to `foo`:
                      MyScratchtoksexpandafterfoo%
                      MyScratchtoksexpandafter%
                      theMyScratchtoks
                      defbat#1argument of bat: #1.%
                      %
                      edeffootheMyScratchtoks%
                      showfoo


                      I used this technique in the example below.
                      If eTeX-extensions are available you can go the edef-unexpanded-route as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.



                      documentclassarticle
                      usepackagekeyval

                      makeatletter
                      %%----------------------------------------------------------------------
                      newcommandUD@firstofone[1]#1%
                      newcommandUD@firstoftwo[2]#1%
                      newcommandUD@secondoftwo[2]#2%
                      newcommandUD@Exchange[2]#2#1%
                      %%----------------------------------------------------------------------
                      %% Check whether argument is empty:
                      %%......................................................................
                      %% UD@CheckWhetherNull<Argument which is to be checked>%
                      %% <Tokens to be delivered in case that argument
                      %% which is to be checked is empty>%
                      %% <Tokens to be delivered in case that argument
                      %% which is to be checked is not empty>%
                      %%
                      %% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
                      %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
                      newcommandUD@CheckWhetherNull[1]%
                      romannumeral0expandafterUD@secondoftwostringexpandafter
                      UD@secondoftwoexpandafterexpandafterstring#1expandafter
                      UD@secondoftwostringexpandafterUD@firstoftwoexpandafter%
                      expandafterUD@secondoftwostringexpandafterexpandafter
                      UD@firstoftwo UD@secondoftwoexpandafterexpandafter
                      UD@firstoftwo UD@firstoftwo%
                      %
                      %%----------------------------------------------------------------------
                      %% Macros FOO and BAR
                      %%----------------------------------------------------------------------
                      newcommand*FOO%
                      newcommand*BAR%
                      %%----------------------------------------------------------------------
                      %% Flags / if-switches
                      %%----------------------------------------------------------------------
                      %% Flag: Is there a need to print an error-message about keyA not
                      %% being set?
                      newififDeliverErrMsgKeyANotProvided
                      DeliverErrMsgKeyANotProvidedtrue
                      %% Flag: Is there a need to print an error-message about keyB not
                      %% being set?
                      newififDeliverErrMsgKeyBNotProvided
                      DeliverErrMsgKeyBNotProvidedtrue
                      %% Flag: Is there a need to print an error-message about keyA
                      %% being provided multiple times?
                      newififDeliverErrMsgKeyAProvidedSeveralTimes
                      DeliverErrMsgKeyAProvidedSeveralTimesfalse
                      %% Flag: Is there a need to print an error-message about keyB
                      %% being provided multiple times?
                      newififDeliverErrMsgKeyBProvidedSeveralTimes
                      DeliverErrMsgKeyBProvidedSeveralTimesfalse
                      %% Flag: Is there a need to print an error-message about keyA
                      %% having a value which is not in the true/false-spectrum?
                      newififDeliverErrMsgKeyANeitherTrueNorFalse
                      DeliverErrMsgKeyANeitherTrueNorFalsefalse
                      %% Flag: Has keyA the value true?
                      newififKeyAsValueIsTrue
                      KeyAsValueIsTruetrue
                      %%----------------------------------------------------------------------
                      %% Place-holders for values that are to be used after flag-evaluation:
                      %%----------------------------------------------------------------------
                      newtoksMyScratchtoks
                      newcommandMyKeyBvalue%
                      %%----------------------------------------------------------------------
                      %% Error-messages:
                      %%----------------------------------------------------------------------
                      %% PreambleMacroError
                      %%......................................................................
                      %% This macro takes three arguments:
                      %% A macro name. An error message. The help information.
                      %% It displays the error message, and sets the error help (the result of
                      %% typing h to the prompt).
                      %%----------------------------------------------------------------------
                      newcommand*PreambleMacroError[3]%
                      GenericError%
                      spacespacespace@spaces@spaces@spaces
                      %
                      LaTeX Error: Inapproriate usage of macro string#1on@line.MessageBreak
                      (string#1 is defined in the document's preamble.)MessageBreak
                      Problem: #2%
                      %
                      Have a look at the comments in the preamble of this document.%
                      #3%
                      %
                      %%----------------------------------------------------------------------
                      %% Error-message in case a flag-key does not have a value of the
                      %% true/false-spectrum:
                      %%----------------------------------------------------------------------
                      newcommandDeliverErrMsgKeyNeitherTrueNorFalse[1]%
                      PreambleMacroErrormacroInvalid value for #1%
                      %PackageErrorMyPackagestringmacro: Invalid value for #1on@line%
                      %@latex@errorstringmacro: Invalid value for #1on@line%
                      %
                      #1 must have one of the following values:%
                      MessageBreak true/yes/on/1 or or false/no/off/0.%
                      %
                      %
                      %%----------------------------------------------------------------------
                      %% Error-message in case a mandatory key is not set at all:
                      %%----------------------------------------------------------------------
                      newcommandDeliverErrMsgKeyNotProvided[1]%
                      PreambleMacroErrormacroSetting for #1 is missing%
                      %PackageErrorMyPackagestringmacro: Setting for #1 is missingon@line%
                      %@latex@errorstringmacro: Setting for #1 is missingon@line%
                      %
                      Setting #1 cannot be omitted.%
                      %
                      %
                      %%----------------------------------------------------------------------
                      %% Error-message in case a keys is set more times than once:
                      %%----------------------------------------------------------------------
                      newcommandDeliverErrMsgKeyProvidedSeveralTimes[1]%
                      PreambleMacroErrormacroMore than one value for #1%
                      %PackageErrorMyPackagestringmacro: More than one value for #1on@line%
                      %@latex@errorstringmacro: More than one value for #1on@line%
                      %
                      For the sake of unambiguity provide a value for #1 exactly once.%
                      %
                      %
                      %%----------------------------------------------------------------------
                      %% DetectYesNo detects whether value is either one of
                      %% true/yes/on/1 or one of false/no/off/0
                      %% DetectYesNo<value>%
                      %% <tokens if value neither is "yes" nor is "no">%
                      %% <tokens if value is "yes">%
                      %% <tokens if value is "no">
                      %%----------------------------------------------------------------------
                      %% Check whether argument contains no exclamation mark which is not
                      %% nested in braces:
                      %%......................................................................
                      %% UD@CheckWhetherNoExclam<Argument which is to be checked>%
                      %% <Tokens to be delivered in case that
                      %% argument contains no exclamation mark>%
                      %% <Tokens to be delivered in case that
                      %% argument contains exclamation mark>%
                      %%
                      newcommandUD@GobbleToExclamlongdefUD@GobbleToExclam#1!%
                      newcommandUD@CheckWhetherNoExclam[1]%
                      expandafterUD@CheckWhetherNullexpandafterUD@GobbleToExclam#1!%
                      %
                      newcommandTrueFalseFork
                      longdefTrueFalseFork#1!!true!yes!on!1!false!no!off!0!#2#3!!!!#2%
                      newcommandDetectYesNo[1]lowercaseInnerDetectYesNo#1%
                      newcommandInnerDetectYesNo[4]%
                      romannumeral0UD@CheckWhetherNoExclam#1%
                      TrueFalseFork!#1!true!yes!on!1!false!no!off!0! #2%<-case #1 is empty/has no tokens
                      !!#1!yes!on!1!false!no!off!0! #3%<-case #1 = true
                      !!true!#1!on!1!false!no!off!0! #3%<-case #1 = yes
                      !!true!yes!#1!1!false!no!off!0! #3%<-case #1 = on
                      !!true!yes!on!#1!false!no!off!0! #3%<-case #1 = 1
                      !!true!yes!on!1!#1!no!off!0! #4%<-case #1 = false
                      !!true!yes!on!1!false!#1!off!0! #4%<-case #1 = no
                      !!true!yes!on!1!false!no!#1!0! #4%<-case #1 = off
                      !!true!yes!on!1!false!no!off!#1! #4%<-case #1 = 0
                      !!true!yes!on!1!false!no!off!0! #2%<-case #1 something else without exclamation mark
                      !!!!%
                      #2%<-case #1 = something else with exclamation-mark.
                      %
                      %%----------------------------------------------------------------------
                      %% Use the keys of the family MyFamily to change flags and to
                      %% save values that are to be used later
                      %%----------------------------------------------------------------------
                      define@keyMyFamilykeyA[true]%
                      DeliverErrMsgKeyANotProvidedfalse
                      define@keyMyFamilykeyA[true]%
                      DeliverErrMsgKeyAProvidedSeveralTimestrue
                      %
                      DetectYesNo#1%
                      DeliverErrMsgKeyANeitherTrueNorFalsetrue%
                      KeyAsValueIsTruetrue%
                      KeyAsValueIsTruefalse%
                      %
                      define@keyMyFamilykeyB%
                      DeliverErrMsgKeyBNotProvidedfalse
                      define@keyMyFamilykeyB%
                      DeliverErrMsgKeyBProvidedSeveralTimestrue
                      %
                      expandafterUD@Exchange
                      expandafter%
                      expandafterMyScratchtoks
                      expandaftertheMyScratchtoks%
                      MyScratchtoks#1%
                      edefMyKeyBvaluetheMyScratchtoks%
                      %
                      %
                      %%----------------------------------------------------------------------
                      %% Now the macro with evaluation and error-messages ad nauseam ;-) :
                      %%----------------------------------------------------------------------
                      newcommandmacro[1]%
                      begingroup
                      setkeysMyFamily#1%
                      %
                      % Print Error-Messages if necessary:
                      %
                      ifDeliverErrMsgKeyANeitherTrueNorFalseDeliverErrMsgKeyNeitherTrueNorFalsekeyAfi
                      ifDeliverErrMsgKeyANotProvidedDeliverErrMsgKeyNotProvidedkeyAfi
                      ifDeliverErrMsgKeyBNotProvidedDeliverErrMsgKeyNotProvidedkeyBfi
                      ifDeliverErrMsgKeyAProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyAfi
                      ifDeliverErrMsgKeyBProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyBfi
                      %
                      % Perform the adding to FOO or BAR in case no error-messages were
                      % printed:
                      %
                      ifDeliverErrMsgKeyANotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                      ifDeliverErrMsgKeyBNotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                      ifDeliverErrMsgKeyAProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                      ifDeliverErrMsgKeyBProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                      ifDeliverErrMsgKeyANeitherTrueNorFalseexpandafterUD@secondoftwoelseexpandafterUD@firstoftwofi%
                      ifKeyAsValueIsTrueKeyAsValueIsTruetrueexpandafterUD@firstoftwoelseexpandafterUD@secondoftwofi%
                      expandafterUD@Exchangeexpandafter%
                      expandafterMyScratchtoksexpandaftertheMyScratchtoks%
                      %
                      MyScratchtoksexpandafterFOO%
                      expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter(MyKeyBvalue)%
                      edefFOOtheMyScratchtoks%
                      %
                      %
                      expandafterUD@Exchangeexpandafter%
                      expandafterMyScratchtoksexpandaftertheMyScratchtoks%
                      %
                      MyScratchtoksexpandafterBAR%
                      expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter[MyKeyBvalue]%
                      edefBARtheMyScratchtoks%
                      %
                      %
                      %
                      %
                      %
                      %
                      %
                      endgroup
                      %
                      makeatother

                      begindocument
                      noindent
                      verb|macrokeyA=true, keyB=Value in first call| - now you have:\
                      macrokeyA=true, keyB=Value in first call%
                      textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                      %
                      nullhrulefillnull\
                      verb|macrokeyA=yes, keyB=Value in second call| - now you have:\
                      macrokeyA=yes, keyB=Value in second call%
                      textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                      %
                      nullhrulefillnull\
                      verb|macrokeyA=0, keyB=Value in third call| - now you have:\
                      macrokeyA=0, keyB=Value in third call%
                      textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                      %
                      nullhrulefillnull\
                      verb|macrokeyA=Off, keyB=Value in fourth call| - now you have:\
                      macrokeyA=Off, keyB=Value in fourth call%
                      textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                      %
                      nullhrulefillnull\
                      verb|macrokeyA, keyB=Value in fifth call| - now you have:\
                      macrokeyA, keyB=Value in fifth call%
                      textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                      %
                      % Let's trigger some error-messages:
                      %
                      %nullhrulefillnull\
                      %verb|macrokeyA=woozle, | - now you have:\
                      %macrokeyA=woozle, %
                      %textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                      %
                      %nullhrulefillnull\
                      %verb|macrokeyA=1, keyA=false, keyB=Value in sixth call| - now you have:\
                      %macrokeyA=1, keyA=false, keyB=Value in sixth call%
                      %textttstringFOO: meaningFOO\stringBAR: meaningBAR\%

                      enddocument


                      enter image description here






                      share|improve this answer





























                        3














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



                        1. If lines end with curly braces ( or ), LaTeX usually will insert space characters at the ends of these lines. This is due to the endlinechar-thingie. These space characters will get tokenized as space-tokens (character tokens of category code 10(space) and character code 32. In (restricted) horizontal mode these space-tokens may yield visible horizontal glue. Make sure that lines end with a comment-character (%) in cases where such glue is undesired.


                        2. bar is already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the macro-names I use the uppercase-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 functions for setting some flags (which is a method of storing true/false-values or 0/1-values) and storing as macros other values that are to be used while/after evaluating the flags.

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

                        I assume that the "arbitrary values" for keyB are sequences of tokens which are brace-balanced. ;-) When storing such arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing as during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.

                        That's why I used the MyScratchtoks..-edefmacrotheMyScratchtoks-thingie: When the is carried out during edef for delivering the content of a token-register, e.g., theMyScratchtoks, the content of the token-register MyScratchtoks will be delivered and hereby hashes will be doubled.)



                        Then you can define a macro which does setkeys for setting flags and storing values and then does evaluate the flags for printing error-messages and in case of not having printed any error message processing the stored values accordingly.




                        You also need forking depending on the phrase/token-sequence provided as value to keyA:

                        In the example below you find DetectYesNo which does the forking by means of delimited macro arguments.




                        You wish to have (almost) arbitrary things added to the macro foo.



                        When doing this, you need to make sure that the new definition text of foo also contains the tokens that form the old definition text of foo by top-level-expanding foo.



                        There are various methods for this. The answer of Phelype Oleinik exhibits the usage of edef and unexpanded.



                        I tend to use expandafter.

                        You could be tempted to do:



                        expandafterdefexpandafterfooexpandafterfoo<tokens to append>


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



                        If, e.g., foo is defined via



                        deffoo%
                        defbas##1argument of bas: ##1.%



                        and you do:



                        expandafterdefexpandafterfooexpandafter%
                        foo
                        defbat##1argument of bat: ##1.%



                        , then this will not yield



                        deffoo%
                        defbas##1argument of bas: ##1.%
                        defbat##1argument of bat: ##1.%



                        , but it will yield:



                        deffoo%
                        defbas#1argument of bas: #1.%
                        defbat##1argument of bat: ##1.%



                        This will be a problem and therefore will yield an error message as foo does not process parameters while foo's definition text does contain #1.



                        Therefore in such situations I also tend to go the MyScratchtoks..-edef...theMyScratchtoks-route:



                        newtoksMyScratchtoks
                        deffoo%
                        defbas##1argument of bas: ##1.%
                        %
                        % Now let's append something to `foo`:
                        MyScratchtoksexpandafterfoo%
                        MyScratchtoksexpandafter%
                        theMyScratchtoks
                        defbat#1argument of bat: #1.%
                        %
                        edeffootheMyScratchtoks%
                        showfoo


                        I used this technique in the example below.
                        If eTeX-extensions are available you can go the edef-unexpanded-route as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.



                        documentclassarticle
                        usepackagekeyval

                        makeatletter
                        %%----------------------------------------------------------------------
                        newcommandUD@firstofone[1]#1%
                        newcommandUD@firstoftwo[2]#1%
                        newcommandUD@secondoftwo[2]#2%
                        newcommandUD@Exchange[2]#2#1%
                        %%----------------------------------------------------------------------
                        %% Check whether argument is empty:
                        %%......................................................................
                        %% UD@CheckWhetherNull<Argument which is to be checked>%
                        %% <Tokens to be delivered in case that argument
                        %% which is to be checked is empty>%
                        %% <Tokens to be delivered in case that argument
                        %% which is to be checked is not empty>%
                        %%
                        %% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
                        %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
                        newcommandUD@CheckWhetherNull[1]%
                        romannumeral0expandafterUD@secondoftwostringexpandafter
                        UD@secondoftwoexpandafterexpandafterstring#1expandafter
                        UD@secondoftwostringexpandafterUD@firstoftwoexpandafter%
                        expandafterUD@secondoftwostringexpandafterexpandafter
                        UD@firstoftwo UD@secondoftwoexpandafterexpandafter
                        UD@firstoftwo UD@firstoftwo%
                        %
                        %%----------------------------------------------------------------------
                        %% Macros FOO and BAR
                        %%----------------------------------------------------------------------
                        newcommand*FOO%
                        newcommand*BAR%
                        %%----------------------------------------------------------------------
                        %% Flags / if-switches
                        %%----------------------------------------------------------------------
                        %% Flag: Is there a need to print an error-message about keyA not
                        %% being set?
                        newififDeliverErrMsgKeyANotProvided
                        DeliverErrMsgKeyANotProvidedtrue
                        %% Flag: Is there a need to print an error-message about keyB not
                        %% being set?
                        newififDeliverErrMsgKeyBNotProvided
                        DeliverErrMsgKeyBNotProvidedtrue
                        %% Flag: Is there a need to print an error-message about keyA
                        %% being provided multiple times?
                        newififDeliverErrMsgKeyAProvidedSeveralTimes
                        DeliverErrMsgKeyAProvidedSeveralTimesfalse
                        %% Flag: Is there a need to print an error-message about keyB
                        %% being provided multiple times?
                        newififDeliverErrMsgKeyBProvidedSeveralTimes
                        DeliverErrMsgKeyBProvidedSeveralTimesfalse
                        %% Flag: Is there a need to print an error-message about keyA
                        %% having a value which is not in the true/false-spectrum?
                        newififDeliverErrMsgKeyANeitherTrueNorFalse
                        DeliverErrMsgKeyANeitherTrueNorFalsefalse
                        %% Flag: Has keyA the value true?
                        newififKeyAsValueIsTrue
                        KeyAsValueIsTruetrue
                        %%----------------------------------------------------------------------
                        %% Place-holders for values that are to be used after flag-evaluation:
                        %%----------------------------------------------------------------------
                        newtoksMyScratchtoks
                        newcommandMyKeyBvalue%
                        %%----------------------------------------------------------------------
                        %% Error-messages:
                        %%----------------------------------------------------------------------
                        %% PreambleMacroError
                        %%......................................................................
                        %% This macro takes three arguments:
                        %% A macro name. An error message. The help information.
                        %% It displays the error message, and sets the error help (the result of
                        %% typing h to the prompt).
                        %%----------------------------------------------------------------------
                        newcommand*PreambleMacroError[3]%
                        GenericError%
                        spacespacespace@spaces@spaces@spaces
                        %
                        LaTeX Error: Inapproriate usage of macro string#1on@line.MessageBreak
                        (string#1 is defined in the document's preamble.)MessageBreak
                        Problem: #2%
                        %
                        Have a look at the comments in the preamble of this document.%
                        #3%
                        %
                        %%----------------------------------------------------------------------
                        %% Error-message in case a flag-key does not have a value of the
                        %% true/false-spectrum:
                        %%----------------------------------------------------------------------
                        newcommandDeliverErrMsgKeyNeitherTrueNorFalse[1]%
                        PreambleMacroErrormacroInvalid value for #1%
                        %PackageErrorMyPackagestringmacro: Invalid value for #1on@line%
                        %@latex@errorstringmacro: Invalid value for #1on@line%
                        %
                        #1 must have one of the following values:%
                        MessageBreak true/yes/on/1 or or false/no/off/0.%
                        %
                        %
                        %%----------------------------------------------------------------------
                        %% Error-message in case a mandatory key is not set at all:
                        %%----------------------------------------------------------------------
                        newcommandDeliverErrMsgKeyNotProvided[1]%
                        PreambleMacroErrormacroSetting for #1 is missing%
                        %PackageErrorMyPackagestringmacro: Setting for #1 is missingon@line%
                        %@latex@errorstringmacro: Setting for #1 is missingon@line%
                        %
                        Setting #1 cannot be omitted.%
                        %
                        %
                        %%----------------------------------------------------------------------
                        %% Error-message in case a keys is set more times than once:
                        %%----------------------------------------------------------------------
                        newcommandDeliverErrMsgKeyProvidedSeveralTimes[1]%
                        PreambleMacroErrormacroMore than one value for #1%
                        %PackageErrorMyPackagestringmacro: More than one value for #1on@line%
                        %@latex@errorstringmacro: More than one value for #1on@line%
                        %
                        For the sake of unambiguity provide a value for #1 exactly once.%
                        %
                        %
                        %%----------------------------------------------------------------------
                        %% DetectYesNo detects whether value is either one of
                        %% true/yes/on/1 or one of false/no/off/0
                        %% DetectYesNo<value>%
                        %% <tokens if value neither is "yes" nor is "no">%
                        %% <tokens if value is "yes">%
                        %% <tokens if value is "no">
                        %%----------------------------------------------------------------------
                        %% Check whether argument contains no exclamation mark which is not
                        %% nested in braces:
                        %%......................................................................
                        %% UD@CheckWhetherNoExclam<Argument which is to be checked>%
                        %% <Tokens to be delivered in case that
                        %% argument contains no exclamation mark>%
                        %% <Tokens to be delivered in case that
                        %% argument contains exclamation mark>%
                        %%
                        newcommandUD@GobbleToExclamlongdefUD@GobbleToExclam#1!%
                        newcommandUD@CheckWhetherNoExclam[1]%
                        expandafterUD@CheckWhetherNullexpandafterUD@GobbleToExclam#1!%
                        %
                        newcommandTrueFalseFork
                        longdefTrueFalseFork#1!!true!yes!on!1!false!no!off!0!#2#3!!!!#2%
                        newcommandDetectYesNo[1]lowercaseInnerDetectYesNo#1%
                        newcommandInnerDetectYesNo[4]%
                        romannumeral0UD@CheckWhetherNoExclam#1%
                        TrueFalseFork!#1!true!yes!on!1!false!no!off!0! #2%<-case #1 is empty/has no tokens
                        !!#1!yes!on!1!false!no!off!0! #3%<-case #1 = true
                        !!true!#1!on!1!false!no!off!0! #3%<-case #1 = yes
                        !!true!yes!#1!1!false!no!off!0! #3%<-case #1 = on
                        !!true!yes!on!#1!false!no!off!0! #3%<-case #1 = 1
                        !!true!yes!on!1!#1!no!off!0! #4%<-case #1 = false
                        !!true!yes!on!1!false!#1!off!0! #4%<-case #1 = no
                        !!true!yes!on!1!false!no!#1!0! #4%<-case #1 = off
                        !!true!yes!on!1!false!no!off!#1! #4%<-case #1 = 0
                        !!true!yes!on!1!false!no!off!0! #2%<-case #1 something else without exclamation mark
                        !!!!%
                        #2%<-case #1 = something else with exclamation-mark.
                        %
                        %%----------------------------------------------------------------------
                        %% Use the keys of the family MyFamily to change flags and to
                        %% save values that are to be used later
                        %%----------------------------------------------------------------------
                        define@keyMyFamilykeyA[true]%
                        DeliverErrMsgKeyANotProvidedfalse
                        define@keyMyFamilykeyA[true]%
                        DeliverErrMsgKeyAProvidedSeveralTimestrue
                        %
                        DetectYesNo#1%
                        DeliverErrMsgKeyANeitherTrueNorFalsetrue%
                        KeyAsValueIsTruetrue%
                        KeyAsValueIsTruefalse%
                        %
                        define@keyMyFamilykeyB%
                        DeliverErrMsgKeyBNotProvidedfalse
                        define@keyMyFamilykeyB%
                        DeliverErrMsgKeyBProvidedSeveralTimestrue
                        %
                        expandafterUD@Exchange
                        expandafter%
                        expandafterMyScratchtoks
                        expandaftertheMyScratchtoks%
                        MyScratchtoks#1%
                        edefMyKeyBvaluetheMyScratchtoks%
                        %
                        %
                        %%----------------------------------------------------------------------
                        %% Now the macro with evaluation and error-messages ad nauseam ;-) :
                        %%----------------------------------------------------------------------
                        newcommandmacro[1]%
                        begingroup
                        setkeysMyFamily#1%
                        %
                        % Print Error-Messages if necessary:
                        %
                        ifDeliverErrMsgKeyANeitherTrueNorFalseDeliverErrMsgKeyNeitherTrueNorFalsekeyAfi
                        ifDeliverErrMsgKeyANotProvidedDeliverErrMsgKeyNotProvidedkeyAfi
                        ifDeliverErrMsgKeyBNotProvidedDeliverErrMsgKeyNotProvidedkeyBfi
                        ifDeliverErrMsgKeyAProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyAfi
                        ifDeliverErrMsgKeyBProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyBfi
                        %
                        % Perform the adding to FOO or BAR in case no error-messages were
                        % printed:
                        %
                        ifDeliverErrMsgKeyANotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                        ifDeliverErrMsgKeyBNotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                        ifDeliverErrMsgKeyAProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                        ifDeliverErrMsgKeyBProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                        ifDeliverErrMsgKeyANeitherTrueNorFalseexpandafterUD@secondoftwoelseexpandafterUD@firstoftwofi%
                        ifKeyAsValueIsTrueKeyAsValueIsTruetrueexpandafterUD@firstoftwoelseexpandafterUD@secondoftwofi%
                        expandafterUD@Exchangeexpandafter%
                        expandafterMyScratchtoksexpandaftertheMyScratchtoks%
                        %
                        MyScratchtoksexpandafterFOO%
                        expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter(MyKeyBvalue)%
                        edefFOOtheMyScratchtoks%
                        %
                        %
                        expandafterUD@Exchangeexpandafter%
                        expandafterMyScratchtoksexpandaftertheMyScratchtoks%
                        %
                        MyScratchtoksexpandafterBAR%
                        expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter[MyKeyBvalue]%
                        edefBARtheMyScratchtoks%
                        %
                        %
                        %
                        %
                        %
                        %
                        %
                        endgroup
                        %
                        makeatother

                        begindocument
                        noindent
                        verb|macrokeyA=true, keyB=Value in first call| - now you have:\
                        macrokeyA=true, keyB=Value in first call%
                        textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                        %
                        nullhrulefillnull\
                        verb|macrokeyA=yes, keyB=Value in second call| - now you have:\
                        macrokeyA=yes, keyB=Value in second call%
                        textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                        %
                        nullhrulefillnull\
                        verb|macrokeyA=0, keyB=Value in third call| - now you have:\
                        macrokeyA=0, keyB=Value in third call%
                        textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                        %
                        nullhrulefillnull\
                        verb|macrokeyA=Off, keyB=Value in fourth call| - now you have:\
                        macrokeyA=Off, keyB=Value in fourth call%
                        textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                        %
                        nullhrulefillnull\
                        verb|macrokeyA, keyB=Value in fifth call| - now you have:\
                        macrokeyA, keyB=Value in fifth call%
                        textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                        %
                        % Let's trigger some error-messages:
                        %
                        %nullhrulefillnull\
                        %verb|macrokeyA=woozle, | - now you have:\
                        %macrokeyA=woozle, %
                        %textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                        %
                        %nullhrulefillnull\
                        %verb|macrokeyA=1, keyA=false, keyB=Value in sixth call| - now you have:\
                        %macrokeyA=1, keyA=false, keyB=Value in sixth call%
                        %textttstringFOO: meaningFOO\stringBAR: meaningBAR\%

                        enddocument


                        enter image description here






                        share|improve this answer



























                          3












                          3








                          3







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



                          1. If lines end with curly braces ( or ), LaTeX usually will insert space characters at the ends of these lines. This is due to the endlinechar-thingie. These space characters will get tokenized as space-tokens (character tokens of category code 10(space) and character code 32. In (restricted) horizontal mode these space-tokens may yield visible horizontal glue. Make sure that lines end with a comment-character (%) in cases where such glue is undesired.


                          2. bar is already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the macro-names I use the uppercase-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 functions for setting some flags (which is a method of storing true/false-values or 0/1-values) and storing as macros other values that are to be used while/after evaluating the flags.

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

                          I assume that the "arbitrary values" for keyB are sequences of tokens which are brace-balanced. ;-) When storing such arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing as during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.

                          That's why I used the MyScratchtoks..-edefmacrotheMyScratchtoks-thingie: When the is carried out during edef for delivering the content of a token-register, e.g., theMyScratchtoks, the content of the token-register MyScratchtoks will be delivered and hereby hashes will be doubled.)



                          Then you can define a macro which does setkeys for setting flags and storing values and then does evaluate the flags for printing error-messages and in case of not having printed any error message processing the stored values accordingly.




                          You also need forking depending on the phrase/token-sequence provided as value to keyA:

                          In the example below you find DetectYesNo which does the forking by means of delimited macro arguments.




                          You wish to have (almost) arbitrary things added to the macro foo.



                          When doing this, you need to make sure that the new definition text of foo also contains the tokens that form the old definition text of foo by top-level-expanding foo.



                          There are various methods for this. The answer of Phelype Oleinik exhibits the usage of edef and unexpanded.



                          I tend to use expandafter.

                          You could be tempted to do:



                          expandafterdefexpandafterfooexpandafterfoo<tokens to append>


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



                          If, e.g., foo is defined via



                          deffoo%
                          defbas##1argument of bas: ##1.%



                          and you do:



                          expandafterdefexpandafterfooexpandafter%
                          foo
                          defbat##1argument of bat: ##1.%



                          , then this will not yield



                          deffoo%
                          defbas##1argument of bas: ##1.%
                          defbat##1argument of bat: ##1.%



                          , but it will yield:



                          deffoo%
                          defbas#1argument of bas: #1.%
                          defbat##1argument of bat: ##1.%



                          This will be a problem and therefore will yield an error message as foo does not process parameters while foo's definition text does contain #1.



                          Therefore in such situations I also tend to go the MyScratchtoks..-edef...theMyScratchtoks-route:



                          newtoksMyScratchtoks
                          deffoo%
                          defbas##1argument of bas: ##1.%
                          %
                          % Now let's append something to `foo`:
                          MyScratchtoksexpandafterfoo%
                          MyScratchtoksexpandafter%
                          theMyScratchtoks
                          defbat#1argument of bat: #1.%
                          %
                          edeffootheMyScratchtoks%
                          showfoo


                          I used this technique in the example below.
                          If eTeX-extensions are available you can go the edef-unexpanded-route as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.



                          documentclassarticle
                          usepackagekeyval

                          makeatletter
                          %%----------------------------------------------------------------------
                          newcommandUD@firstofone[1]#1%
                          newcommandUD@firstoftwo[2]#1%
                          newcommandUD@secondoftwo[2]#2%
                          newcommandUD@Exchange[2]#2#1%
                          %%----------------------------------------------------------------------
                          %% Check whether argument is empty:
                          %%......................................................................
                          %% UD@CheckWhetherNull<Argument which is to be checked>%
                          %% <Tokens to be delivered in case that argument
                          %% which is to be checked is empty>%
                          %% <Tokens to be delivered in case that argument
                          %% which is to be checked is not empty>%
                          %%
                          %% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
                          %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
                          newcommandUD@CheckWhetherNull[1]%
                          romannumeral0expandafterUD@secondoftwostringexpandafter
                          UD@secondoftwoexpandafterexpandafterstring#1expandafter
                          UD@secondoftwostringexpandafterUD@firstoftwoexpandafter%
                          expandafterUD@secondoftwostringexpandafterexpandafter
                          UD@firstoftwo UD@secondoftwoexpandafterexpandafter
                          UD@firstoftwo UD@firstoftwo%
                          %
                          %%----------------------------------------------------------------------
                          %% Macros FOO and BAR
                          %%----------------------------------------------------------------------
                          newcommand*FOO%
                          newcommand*BAR%
                          %%----------------------------------------------------------------------
                          %% Flags / if-switches
                          %%----------------------------------------------------------------------
                          %% Flag: Is there a need to print an error-message about keyA not
                          %% being set?
                          newififDeliverErrMsgKeyANotProvided
                          DeliverErrMsgKeyANotProvidedtrue
                          %% Flag: Is there a need to print an error-message about keyB not
                          %% being set?
                          newififDeliverErrMsgKeyBNotProvided
                          DeliverErrMsgKeyBNotProvidedtrue
                          %% Flag: Is there a need to print an error-message about keyA
                          %% being provided multiple times?
                          newififDeliverErrMsgKeyAProvidedSeveralTimes
                          DeliverErrMsgKeyAProvidedSeveralTimesfalse
                          %% Flag: Is there a need to print an error-message about keyB
                          %% being provided multiple times?
                          newififDeliverErrMsgKeyBProvidedSeveralTimes
                          DeliverErrMsgKeyBProvidedSeveralTimesfalse
                          %% Flag: Is there a need to print an error-message about keyA
                          %% having a value which is not in the true/false-spectrum?
                          newififDeliverErrMsgKeyANeitherTrueNorFalse
                          DeliverErrMsgKeyANeitherTrueNorFalsefalse
                          %% Flag: Has keyA the value true?
                          newififKeyAsValueIsTrue
                          KeyAsValueIsTruetrue
                          %%----------------------------------------------------------------------
                          %% Place-holders for values that are to be used after flag-evaluation:
                          %%----------------------------------------------------------------------
                          newtoksMyScratchtoks
                          newcommandMyKeyBvalue%
                          %%----------------------------------------------------------------------
                          %% Error-messages:
                          %%----------------------------------------------------------------------
                          %% PreambleMacroError
                          %%......................................................................
                          %% This macro takes three arguments:
                          %% A macro name. An error message. The help information.
                          %% It displays the error message, and sets the error help (the result of
                          %% typing h to the prompt).
                          %%----------------------------------------------------------------------
                          newcommand*PreambleMacroError[3]%
                          GenericError%
                          spacespacespace@spaces@spaces@spaces
                          %
                          LaTeX Error: Inapproriate usage of macro string#1on@line.MessageBreak
                          (string#1 is defined in the document's preamble.)MessageBreak
                          Problem: #2%
                          %
                          Have a look at the comments in the preamble of this document.%
                          #3%
                          %
                          %%----------------------------------------------------------------------
                          %% Error-message in case a flag-key does not have a value of the
                          %% true/false-spectrum:
                          %%----------------------------------------------------------------------
                          newcommandDeliverErrMsgKeyNeitherTrueNorFalse[1]%
                          PreambleMacroErrormacroInvalid value for #1%
                          %PackageErrorMyPackagestringmacro: Invalid value for #1on@line%
                          %@latex@errorstringmacro: Invalid value for #1on@line%
                          %
                          #1 must have one of the following values:%
                          MessageBreak true/yes/on/1 or or false/no/off/0.%
                          %
                          %
                          %%----------------------------------------------------------------------
                          %% Error-message in case a mandatory key is not set at all:
                          %%----------------------------------------------------------------------
                          newcommandDeliverErrMsgKeyNotProvided[1]%
                          PreambleMacroErrormacroSetting for #1 is missing%
                          %PackageErrorMyPackagestringmacro: Setting for #1 is missingon@line%
                          %@latex@errorstringmacro: Setting for #1 is missingon@line%
                          %
                          Setting #1 cannot be omitted.%
                          %
                          %
                          %%----------------------------------------------------------------------
                          %% Error-message in case a keys is set more times than once:
                          %%----------------------------------------------------------------------
                          newcommandDeliverErrMsgKeyProvidedSeveralTimes[1]%
                          PreambleMacroErrormacroMore than one value for #1%
                          %PackageErrorMyPackagestringmacro: More than one value for #1on@line%
                          %@latex@errorstringmacro: More than one value for #1on@line%
                          %
                          For the sake of unambiguity provide a value for #1 exactly once.%
                          %
                          %
                          %%----------------------------------------------------------------------
                          %% DetectYesNo detects whether value is either one of
                          %% true/yes/on/1 or one of false/no/off/0
                          %% DetectYesNo<value>%
                          %% <tokens if value neither is "yes" nor is "no">%
                          %% <tokens if value is "yes">%
                          %% <tokens if value is "no">
                          %%----------------------------------------------------------------------
                          %% Check whether argument contains no exclamation mark which is not
                          %% nested in braces:
                          %%......................................................................
                          %% UD@CheckWhetherNoExclam<Argument which is to be checked>%
                          %% <Tokens to be delivered in case that
                          %% argument contains no exclamation mark>%
                          %% <Tokens to be delivered in case that
                          %% argument contains exclamation mark>%
                          %%
                          newcommandUD@GobbleToExclamlongdefUD@GobbleToExclam#1!%
                          newcommandUD@CheckWhetherNoExclam[1]%
                          expandafterUD@CheckWhetherNullexpandafterUD@GobbleToExclam#1!%
                          %
                          newcommandTrueFalseFork
                          longdefTrueFalseFork#1!!true!yes!on!1!false!no!off!0!#2#3!!!!#2%
                          newcommandDetectYesNo[1]lowercaseInnerDetectYesNo#1%
                          newcommandInnerDetectYesNo[4]%
                          romannumeral0UD@CheckWhetherNoExclam#1%
                          TrueFalseFork!#1!true!yes!on!1!false!no!off!0! #2%<-case #1 is empty/has no tokens
                          !!#1!yes!on!1!false!no!off!0! #3%<-case #1 = true
                          !!true!#1!on!1!false!no!off!0! #3%<-case #1 = yes
                          !!true!yes!#1!1!false!no!off!0! #3%<-case #1 = on
                          !!true!yes!on!#1!false!no!off!0! #3%<-case #1 = 1
                          !!true!yes!on!1!#1!no!off!0! #4%<-case #1 = false
                          !!true!yes!on!1!false!#1!off!0! #4%<-case #1 = no
                          !!true!yes!on!1!false!no!#1!0! #4%<-case #1 = off
                          !!true!yes!on!1!false!no!off!#1! #4%<-case #1 = 0
                          !!true!yes!on!1!false!no!off!0! #2%<-case #1 something else without exclamation mark
                          !!!!%
                          #2%<-case #1 = something else with exclamation-mark.
                          %
                          %%----------------------------------------------------------------------
                          %% Use the keys of the family MyFamily to change flags and to
                          %% save values that are to be used later
                          %%----------------------------------------------------------------------
                          define@keyMyFamilykeyA[true]%
                          DeliverErrMsgKeyANotProvidedfalse
                          define@keyMyFamilykeyA[true]%
                          DeliverErrMsgKeyAProvidedSeveralTimestrue
                          %
                          DetectYesNo#1%
                          DeliverErrMsgKeyANeitherTrueNorFalsetrue%
                          KeyAsValueIsTruetrue%
                          KeyAsValueIsTruefalse%
                          %
                          define@keyMyFamilykeyB%
                          DeliverErrMsgKeyBNotProvidedfalse
                          define@keyMyFamilykeyB%
                          DeliverErrMsgKeyBProvidedSeveralTimestrue
                          %
                          expandafterUD@Exchange
                          expandafter%
                          expandafterMyScratchtoks
                          expandaftertheMyScratchtoks%
                          MyScratchtoks#1%
                          edefMyKeyBvaluetheMyScratchtoks%
                          %
                          %
                          %%----------------------------------------------------------------------
                          %% Now the macro with evaluation and error-messages ad nauseam ;-) :
                          %%----------------------------------------------------------------------
                          newcommandmacro[1]%
                          begingroup
                          setkeysMyFamily#1%
                          %
                          % Print Error-Messages if necessary:
                          %
                          ifDeliverErrMsgKeyANeitherTrueNorFalseDeliverErrMsgKeyNeitherTrueNorFalsekeyAfi
                          ifDeliverErrMsgKeyANotProvidedDeliverErrMsgKeyNotProvidedkeyAfi
                          ifDeliverErrMsgKeyBNotProvidedDeliverErrMsgKeyNotProvidedkeyBfi
                          ifDeliverErrMsgKeyAProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyAfi
                          ifDeliverErrMsgKeyBProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyBfi
                          %
                          % Perform the adding to FOO or BAR in case no error-messages were
                          % printed:
                          %
                          ifDeliverErrMsgKeyANotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                          ifDeliverErrMsgKeyBNotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                          ifDeliverErrMsgKeyAProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                          ifDeliverErrMsgKeyBProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                          ifDeliverErrMsgKeyANeitherTrueNorFalseexpandafterUD@secondoftwoelseexpandafterUD@firstoftwofi%
                          ifKeyAsValueIsTrueKeyAsValueIsTruetrueexpandafterUD@firstoftwoelseexpandafterUD@secondoftwofi%
                          expandafterUD@Exchangeexpandafter%
                          expandafterMyScratchtoksexpandaftertheMyScratchtoks%
                          %
                          MyScratchtoksexpandafterFOO%
                          expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter(MyKeyBvalue)%
                          edefFOOtheMyScratchtoks%
                          %
                          %
                          expandafterUD@Exchangeexpandafter%
                          expandafterMyScratchtoksexpandaftertheMyScratchtoks%
                          %
                          MyScratchtoksexpandafterBAR%
                          expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter[MyKeyBvalue]%
                          edefBARtheMyScratchtoks%
                          %
                          %
                          %
                          %
                          %
                          %
                          %
                          endgroup
                          %
                          makeatother

                          begindocument
                          noindent
                          verb|macrokeyA=true, keyB=Value in first call| - now you have:\
                          macrokeyA=true, keyB=Value in first call%
                          textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                          %
                          nullhrulefillnull\
                          verb|macrokeyA=yes, keyB=Value in second call| - now you have:\
                          macrokeyA=yes, keyB=Value in second call%
                          textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                          %
                          nullhrulefillnull\
                          verb|macrokeyA=0, keyB=Value in third call| - now you have:\
                          macrokeyA=0, keyB=Value in third call%
                          textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                          %
                          nullhrulefillnull\
                          verb|macrokeyA=Off, keyB=Value in fourth call| - now you have:\
                          macrokeyA=Off, keyB=Value in fourth call%
                          textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                          %
                          nullhrulefillnull\
                          verb|macrokeyA, keyB=Value in fifth call| - now you have:\
                          macrokeyA, keyB=Value in fifth call%
                          textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                          %
                          % Let's trigger some error-messages:
                          %
                          %nullhrulefillnull\
                          %verb|macrokeyA=woozle, | - now you have:\
                          %macrokeyA=woozle, %
                          %textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                          %
                          %nullhrulefillnull\
                          %verb|macrokeyA=1, keyA=false, keyB=Value in sixth call| - now you have:\
                          %macrokeyA=1, keyA=false, keyB=Value in sixth call%
                          %textttstringFOO: meaningFOO\stringBAR: meaningBAR\%

                          enddocument


                          enter image description here






                          share|improve this answer















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



                          1. If lines end with curly braces ( or ), LaTeX usually will insert space characters at the ends of these lines. This is due to the endlinechar-thingie. These space characters will get tokenized as space-tokens (character tokens of category code 10(space) and character code 32. In (restricted) horizontal mode these space-tokens may yield visible horizontal glue. Make sure that lines end with a comment-character (%) in cases where such glue is undesired.


                          2. bar is already defined in LaTeX2e. I suggest not to override it. Therefore in the example below for the macro-names I use the uppercase-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 functions for setting some flags (which is a method of storing true/false-values or 0/1-values) and storing as macros other values that are to be used while/after evaluating the flags.

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

                          I assume that the "arbitrary values" for keyB are sequences of tokens which are brace-balanced. ;-) When storing such arbitrary token sequences as macros, you need to make sure that hashes (#) get doubled during the process of storing as during macro expansion two hashes (##) will collapse into one which means that during macro expansion the amount of hashes will be halved.

                          That's why I used the MyScratchtoks..-edefmacrotheMyScratchtoks-thingie: When the is carried out during edef for delivering the content of a token-register, e.g., theMyScratchtoks, the content of the token-register MyScratchtoks will be delivered and hereby hashes will be doubled.)



                          Then you can define a macro which does setkeys for setting flags and storing values and then does evaluate the flags for printing error-messages and in case of not having printed any error message processing the stored values accordingly.




                          You also need forking depending on the phrase/token-sequence provided as value to keyA:

                          In the example below you find DetectYesNo which does the forking by means of delimited macro arguments.




                          You wish to have (almost) arbitrary things added to the macro foo.



                          When doing this, you need to make sure that the new definition text of foo also contains the tokens that form the old definition text of foo by top-level-expanding foo.



                          There are various methods for this. The answer of Phelype Oleinik exhibits the usage of edef and unexpanded.



                          I tend to use expandafter.

                          You could be tempted to do:



                          expandafterdefexpandafterfooexpandafterfoo<tokens to append>


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



                          If, e.g., foo is defined via



                          deffoo%
                          defbas##1argument of bas: ##1.%



                          and you do:



                          expandafterdefexpandafterfooexpandafter%
                          foo
                          defbat##1argument of bat: ##1.%



                          , then this will not yield



                          deffoo%
                          defbas##1argument of bas: ##1.%
                          defbat##1argument of bat: ##1.%



                          , but it will yield:



                          deffoo%
                          defbas#1argument of bas: #1.%
                          defbat##1argument of bat: ##1.%



                          This will be a problem and therefore will yield an error message as foo does not process parameters while foo's definition text does contain #1.



                          Therefore in such situations I also tend to go the MyScratchtoks..-edef...theMyScratchtoks-route:



                          newtoksMyScratchtoks
                          deffoo%
                          defbas##1argument of bas: ##1.%
                          %
                          % Now let's append something to `foo`:
                          MyScratchtoksexpandafterfoo%
                          MyScratchtoksexpandafter%
                          theMyScratchtoks
                          defbat#1argument of bat: #1.%
                          %
                          edeffootheMyScratchtoks%
                          showfoo


                          I used this technique in the example below.
                          If eTeX-extensions are available you can go the edef-unexpanded-route as shown by Phelype Oleinik as unexpanded also doubles hashes when carried out during edef.



                          documentclassarticle
                          usepackagekeyval

                          makeatletter
                          %%----------------------------------------------------------------------
                          newcommandUD@firstofone[1]#1%
                          newcommandUD@firstoftwo[2]#1%
                          newcommandUD@secondoftwo[2]#2%
                          newcommandUD@Exchange[2]#2#1%
                          %%----------------------------------------------------------------------
                          %% Check whether argument is empty:
                          %%......................................................................
                          %% UD@CheckWhetherNull<Argument which is to be checked>%
                          %% <Tokens to be delivered in case that argument
                          %% which is to be checked is empty>%
                          %% <Tokens to be delivered in case that argument
                          %% which is to be checked is not empty>%
                          %%
                          %% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
                          %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
                          newcommandUD@CheckWhetherNull[1]%
                          romannumeral0expandafterUD@secondoftwostringexpandafter
                          UD@secondoftwoexpandafterexpandafterstring#1expandafter
                          UD@secondoftwostringexpandafterUD@firstoftwoexpandafter%
                          expandafterUD@secondoftwostringexpandafterexpandafter
                          UD@firstoftwo UD@secondoftwoexpandafterexpandafter
                          UD@firstoftwo UD@firstoftwo%
                          %
                          %%----------------------------------------------------------------------
                          %% Macros FOO and BAR
                          %%----------------------------------------------------------------------
                          newcommand*FOO%
                          newcommand*BAR%
                          %%----------------------------------------------------------------------
                          %% Flags / if-switches
                          %%----------------------------------------------------------------------
                          %% Flag: Is there a need to print an error-message about keyA not
                          %% being set?
                          newififDeliverErrMsgKeyANotProvided
                          DeliverErrMsgKeyANotProvidedtrue
                          %% Flag: Is there a need to print an error-message about keyB not
                          %% being set?
                          newififDeliverErrMsgKeyBNotProvided
                          DeliverErrMsgKeyBNotProvidedtrue
                          %% Flag: Is there a need to print an error-message about keyA
                          %% being provided multiple times?
                          newififDeliverErrMsgKeyAProvidedSeveralTimes
                          DeliverErrMsgKeyAProvidedSeveralTimesfalse
                          %% Flag: Is there a need to print an error-message about keyB
                          %% being provided multiple times?
                          newififDeliverErrMsgKeyBProvidedSeveralTimes
                          DeliverErrMsgKeyBProvidedSeveralTimesfalse
                          %% Flag: Is there a need to print an error-message about keyA
                          %% having a value which is not in the true/false-spectrum?
                          newififDeliverErrMsgKeyANeitherTrueNorFalse
                          DeliverErrMsgKeyANeitherTrueNorFalsefalse
                          %% Flag: Has keyA the value true?
                          newififKeyAsValueIsTrue
                          KeyAsValueIsTruetrue
                          %%----------------------------------------------------------------------
                          %% Place-holders for values that are to be used after flag-evaluation:
                          %%----------------------------------------------------------------------
                          newtoksMyScratchtoks
                          newcommandMyKeyBvalue%
                          %%----------------------------------------------------------------------
                          %% Error-messages:
                          %%----------------------------------------------------------------------
                          %% PreambleMacroError
                          %%......................................................................
                          %% This macro takes three arguments:
                          %% A macro name. An error message. The help information.
                          %% It displays the error message, and sets the error help (the result of
                          %% typing h to the prompt).
                          %%----------------------------------------------------------------------
                          newcommand*PreambleMacroError[3]%
                          GenericError%
                          spacespacespace@spaces@spaces@spaces
                          %
                          LaTeX Error: Inapproriate usage of macro string#1on@line.MessageBreak
                          (string#1 is defined in the document's preamble.)MessageBreak
                          Problem: #2%
                          %
                          Have a look at the comments in the preamble of this document.%
                          #3%
                          %
                          %%----------------------------------------------------------------------
                          %% Error-message in case a flag-key does not have a value of the
                          %% true/false-spectrum:
                          %%----------------------------------------------------------------------
                          newcommandDeliverErrMsgKeyNeitherTrueNorFalse[1]%
                          PreambleMacroErrormacroInvalid value for #1%
                          %PackageErrorMyPackagestringmacro: Invalid value for #1on@line%
                          %@latex@errorstringmacro: Invalid value for #1on@line%
                          %
                          #1 must have one of the following values:%
                          MessageBreak true/yes/on/1 or or false/no/off/0.%
                          %
                          %
                          %%----------------------------------------------------------------------
                          %% Error-message in case a mandatory key is not set at all:
                          %%----------------------------------------------------------------------
                          newcommandDeliverErrMsgKeyNotProvided[1]%
                          PreambleMacroErrormacroSetting for #1 is missing%
                          %PackageErrorMyPackagestringmacro: Setting for #1 is missingon@line%
                          %@latex@errorstringmacro: Setting for #1 is missingon@line%
                          %
                          Setting #1 cannot be omitted.%
                          %
                          %
                          %%----------------------------------------------------------------------
                          %% Error-message in case a keys is set more times than once:
                          %%----------------------------------------------------------------------
                          newcommandDeliverErrMsgKeyProvidedSeveralTimes[1]%
                          PreambleMacroErrormacroMore than one value for #1%
                          %PackageErrorMyPackagestringmacro: More than one value for #1on@line%
                          %@latex@errorstringmacro: More than one value for #1on@line%
                          %
                          For the sake of unambiguity provide a value for #1 exactly once.%
                          %
                          %
                          %%----------------------------------------------------------------------
                          %% DetectYesNo detects whether value is either one of
                          %% true/yes/on/1 or one of false/no/off/0
                          %% DetectYesNo<value>%
                          %% <tokens if value neither is "yes" nor is "no">%
                          %% <tokens if value is "yes">%
                          %% <tokens if value is "no">
                          %%----------------------------------------------------------------------
                          %% Check whether argument contains no exclamation mark which is not
                          %% nested in braces:
                          %%......................................................................
                          %% UD@CheckWhetherNoExclam<Argument which is to be checked>%
                          %% <Tokens to be delivered in case that
                          %% argument contains no exclamation mark>%
                          %% <Tokens to be delivered in case that
                          %% argument contains exclamation mark>%
                          %%
                          newcommandUD@GobbleToExclamlongdefUD@GobbleToExclam#1!%
                          newcommandUD@CheckWhetherNoExclam[1]%
                          expandafterUD@CheckWhetherNullexpandafterUD@GobbleToExclam#1!%
                          %
                          newcommandTrueFalseFork
                          longdefTrueFalseFork#1!!true!yes!on!1!false!no!off!0!#2#3!!!!#2%
                          newcommandDetectYesNo[1]lowercaseInnerDetectYesNo#1%
                          newcommandInnerDetectYesNo[4]%
                          romannumeral0UD@CheckWhetherNoExclam#1%
                          TrueFalseFork!#1!true!yes!on!1!false!no!off!0! #2%<-case #1 is empty/has no tokens
                          !!#1!yes!on!1!false!no!off!0! #3%<-case #1 = true
                          !!true!#1!on!1!false!no!off!0! #3%<-case #1 = yes
                          !!true!yes!#1!1!false!no!off!0! #3%<-case #1 = on
                          !!true!yes!on!#1!false!no!off!0! #3%<-case #1 = 1
                          !!true!yes!on!1!#1!no!off!0! #4%<-case #1 = false
                          !!true!yes!on!1!false!#1!off!0! #4%<-case #1 = no
                          !!true!yes!on!1!false!no!#1!0! #4%<-case #1 = off
                          !!true!yes!on!1!false!no!off!#1! #4%<-case #1 = 0
                          !!true!yes!on!1!false!no!off!0! #2%<-case #1 something else without exclamation mark
                          !!!!%
                          #2%<-case #1 = something else with exclamation-mark.
                          %
                          %%----------------------------------------------------------------------
                          %% Use the keys of the family MyFamily to change flags and to
                          %% save values that are to be used later
                          %%----------------------------------------------------------------------
                          define@keyMyFamilykeyA[true]%
                          DeliverErrMsgKeyANotProvidedfalse
                          define@keyMyFamilykeyA[true]%
                          DeliverErrMsgKeyAProvidedSeveralTimestrue
                          %
                          DetectYesNo#1%
                          DeliverErrMsgKeyANeitherTrueNorFalsetrue%
                          KeyAsValueIsTruetrue%
                          KeyAsValueIsTruefalse%
                          %
                          define@keyMyFamilykeyB%
                          DeliverErrMsgKeyBNotProvidedfalse
                          define@keyMyFamilykeyB%
                          DeliverErrMsgKeyBProvidedSeveralTimestrue
                          %
                          expandafterUD@Exchange
                          expandafter%
                          expandafterMyScratchtoks
                          expandaftertheMyScratchtoks%
                          MyScratchtoks#1%
                          edefMyKeyBvaluetheMyScratchtoks%
                          %
                          %
                          %%----------------------------------------------------------------------
                          %% Now the macro with evaluation and error-messages ad nauseam ;-) :
                          %%----------------------------------------------------------------------
                          newcommandmacro[1]%
                          begingroup
                          setkeysMyFamily#1%
                          %
                          % Print Error-Messages if necessary:
                          %
                          ifDeliverErrMsgKeyANeitherTrueNorFalseDeliverErrMsgKeyNeitherTrueNorFalsekeyAfi
                          ifDeliverErrMsgKeyANotProvidedDeliverErrMsgKeyNotProvidedkeyAfi
                          ifDeliverErrMsgKeyBNotProvidedDeliverErrMsgKeyNotProvidedkeyBfi
                          ifDeliverErrMsgKeyAProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyAfi
                          ifDeliverErrMsgKeyBProvidedSeveralTimesDeliverErrMsgKeyProvidedSeveralTimeskeyBfi
                          %
                          % Perform the adding to FOO or BAR in case no error-messages were
                          % printed:
                          %
                          ifDeliverErrMsgKeyANotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                          ifDeliverErrMsgKeyBNotProvidedexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                          ifDeliverErrMsgKeyAProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                          ifDeliverErrMsgKeyBProvidedSeveralTimesexpandafterUD@secondoftwoelseexpandafterUD@firstofonefi%
                          ifDeliverErrMsgKeyANeitherTrueNorFalseexpandafterUD@secondoftwoelseexpandafterUD@firstoftwofi%
                          ifKeyAsValueIsTrueKeyAsValueIsTruetrueexpandafterUD@firstoftwoelseexpandafterUD@secondoftwofi%
                          expandafterUD@Exchangeexpandafter%
                          expandafterMyScratchtoksexpandaftertheMyScratchtoks%
                          %
                          MyScratchtoksexpandafterFOO%
                          expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter(MyKeyBvalue)%
                          edefFOOtheMyScratchtoks%
                          %
                          %
                          expandafterUD@Exchangeexpandafter%
                          expandafterMyScratchtoksexpandaftertheMyScratchtoks%
                          %
                          MyScratchtoksexpandafterBAR%
                          expandafterendgroupexpandafterMyScratchtoksexpandaftertheexpandafterMyScratchtoksexpandafter[MyKeyBvalue]%
                          edefBARtheMyScratchtoks%
                          %
                          %
                          %
                          %
                          %
                          %
                          %
                          endgroup
                          %
                          makeatother

                          begindocument
                          noindent
                          verb|macrokeyA=true, keyB=Value in first call| - now you have:\
                          macrokeyA=true, keyB=Value in first call%
                          textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                          %
                          nullhrulefillnull\
                          verb|macrokeyA=yes, keyB=Value in second call| - now you have:\
                          macrokeyA=yes, keyB=Value in second call%
                          textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                          %
                          nullhrulefillnull\
                          verb|macrokeyA=0, keyB=Value in third call| - now you have:\
                          macrokeyA=0, keyB=Value in third call%
                          textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                          %
                          nullhrulefillnull\
                          verb|macrokeyA=Off, keyB=Value in fourth call| - now you have:\
                          macrokeyA=Off, keyB=Value in fourth call%
                          textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                          %
                          nullhrulefillnull\
                          verb|macrokeyA, keyB=Value in fifth call| - now you have:\
                          macrokeyA, keyB=Value in fifth call%
                          textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                          %
                          % Let's trigger some error-messages:
                          %
                          %nullhrulefillnull\
                          %verb|macrokeyA=woozle, | - now you have:\
                          %macrokeyA=woozle, %
                          %textttstringFOO: meaningFOO\stringBAR: meaningBAR\%
                          %
                          %nullhrulefillnull\
                          %verb|macrokeyA=1, keyA=false, keyB=Value in sixth call| - now you have:\
                          %macrokeyA=1, keyA=false, keyB=Value in sixth call%
                          %textttstringFOO: meaningFOO\stringBAR: meaningBAR\%

                          enddocument


                          enter image description here







                          share|improve this answer














                          share|improve this answer



                          share|improve this answer








                          edited 3 hours ago

























                          answered 8 hours ago









                          Ulrich DiezUlrich Diez

                          6,060620




                          6,060620




















                              Jevdokija is a new contributor. Be nice, and check out our Code of Conduct.









                              draft saved

                              draft discarded


















                              Jevdokija is a new contributor. Be nice, and check out our Code of Conduct.












                              Jevdokija is a new contributor. Be nice, and check out our Code of Conduct.











                              Jevdokija is a new contributor. Be nice, and check out our Code of Conduct.














                              Thanks for contributing an answer to TeX - LaTeX Stack Exchange!


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid


                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.

                              To learn more, see our tips on writing great answers.




                              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

                              Canceling a color specificationRandomly assigning color to Graphics3D objects?Default color for Filling in Mathematica 9Coloring specific elements of sets with a prime modified order in an array plotHow to pick a color differing significantly from the colors already in a given color list?Detection of the text colorColor numbers based on their valueCan color schemes for use with ColorData include opacity specification?My dynamic color schemes

                              Invision Community Contents History See also References External links Navigation menuProprietaryinvisioncommunity.comIPS Community ForumsIPS Community Forumsthis blog entry"License Changes, IP.Board 3.4, and the Future""Interview -- Matt Mecham of Ibforums""CEO Invision Power Board, Matt Mecham Is a Liar, Thief!"IPB License Explanation 1.3, 1.3.1, 2.0, and 2.1ArchivedSecurity Fixes, Updates And Enhancements For IPB 1.3.1Archived"New Demo Accounts - Invision Power Services"the original"New Default Skin"the original"Invision Power Board 3.0.0 and Applications Released"the original"Archived copy"the original"Perpetual licenses being done away with""Release Notes - Invision Power Services""Introducing: IPS Community Suite 4!"Invision Community Release Notes

                              François Viète Contents Biography Work and thought Bibliography See also Notes Further reading External links Navigation menup. 21Google Bookspp. 75–77Google BooksDe thou (from University of Saint Andrews)ArchivedGoogle BooksGoogle BooksGoogle BooksGoogle booksGoogle Bookscc-parthenay.frL'histoire universelle (fr)Universal History (en)ArchivedAdsabs.harvard.eduPagesperso-orange.frArchive.orgChikara Sasaki. Descartes' mathematical thought p.259Google BooksGoogle BooksGoogle Bookspp. 152 and onwardGoogle BooksGoogle BooksScribd.comGoogle Books1257-7979Google BooksGoogle BooksGoogle BooksGoogle BooksGoogle BooksGoogle BooksGallica.bnf.frGoogle BooksGoogle Books"François Viète"Francois Viète: Father of Modern Algebraic NotationThe Lawyer and the GamblerAbout TarporleySite de Jean-Paul GuichardL'algèbre nouvelle"About the Harmonicon"cb120511976(data)1188044800000 0001 0913 5903n82164680ola2013766880073431702w6vt1sb70287374827140948071409480