Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I struggle to create a macro that can take several options. What I want to create is for \mycommand[up,middle]{Macro} to output Macro with 'up' and 'middle' specified. Using etoolbox in the MWE below I can output single options as well as when nothing or an invalid option is given. But I don't know how to output multiple options.

I do not have to use etoolbox, but I quite like the syntax. David Carlisle suggested something like \newcommand\foo[2][]{\@for\tmp:=#1\do{.... something with \tmp and #2...}} but I don't see how I can apply this to my example.

\documentclass{article}
\usepackage{etoolbox}

\newrobustcmd{\mycommand}[2][]{%
 \ifstrequal{#1}{up}{% option up
   #2 with `up' specified
   }{% option middle
   \ifstrequal{#1}{middle}{%
    #2 with `middle' specified
    }{% nothing specified
    \ifstrempty{#1}{%
     #2 with nothing specified
     }{% something specified
     #2 is invalid
   }{\relax}%
  }{\relax}%
 }%
}%

\begin{document}

\mycommand[up]{Macro}

\mycommand[middle]{Macro}

\mycommand[]{Macro}

\mycommand[bla]{Macro}

\end{document}

enter image description here

share|improve this question
    
It depends on what up and middle should do: are they independent? –  egreg Nov 5 '12 at 13:40
    
I seem to recall that the xargs package can do something like this, as would more powerful packages that implement a key-value syntax. –  jon Nov 5 '12 at 13:41
    
Yes, they are placeholders to execute independent commands. –  Jörg Nov 5 '12 at 13:42
add comment

3 Answers 3

up vote 9 down vote accepted

The suggestion to use \@for might work out something like

\documentclass{article}

\usepackage{etoolbox}
\makeatletter
\newrobustcmd{\mycommand}[2][]{% first the options that are equal for all
  \@for\tmp:=#1\do{%
    \expandafter\ifstrequal\expandafter{\tmp}{up}
      {'up' seen}%
      {%
        \expandafter\ifstrequal\expandafter{\tmp}{middle}
          {'middle' seen}
          {%
            \expandafter\ifstrempty\expandafter{\tmp}
              {}
              {INVALID}%
          }%
      }%
  }%
}%
\makeatother

\begin{document}

\mycommand[up]{Macro}

\mycommand[middle]{Macro}

\mycommand[]{Macro}

\mycommand[bla]{Macro}


\end{document}

As I don't know what 'payload' you want, I've just typeset information on what has been seen: in a real case, this would probably be used to set up a switch or macro for use after the \do argument is complete.

share|improve this answer
    
Yes, that does exactly what I asked for. But I just realised that my objective is a bit more complex and I probably have to use keyvalues. –  Jörg Nov 5 '12 at 14:01
3  
@Jörg I was not sure, but tried to stick to what was asked :-) –  Joseph Wright Nov 5 '12 at 14:05
    
Yes, it does so perfectly :). But after thinking about my problem a bit more I think keyvalues work better for what I need. I basically have four options and they are in pairs, so Options A/B and Options X/Y. So I want to allow for A/X, A/Y, B/X, B/Y simultaneously, but not for A/B and X/Y simultaneously. I think in this case creating keyvalues with Option1=A/B, Option2=X/Y is a better strategy. –  Jörg Nov 5 '12 at 14:33
2  
@Jörg it might be better to ask a new question; follow-up questions are perfectly acceptable on the site :) –  cmhughes Nov 5 '12 at 16:25
    
@cmhughes I know :). My comment was meant as a statement, not a question. –  Jörg Nov 5 '12 at 16:42
add comment

You could give one of the keyval packages a try, eg xkeyval

\documentclass{article}
\usepackage{xkeyval}
\makeatletter
\define@choicekey+{myKeys}{foo}[\val\nr]{up,middle}{%
  \ifcase\nr\relax
      \def\keyval{up}       \or
      \def\keyval{middle}   
  \fi}{\def\keyval{\{not defined\}}} 
\makeatother

\newcommand\mycommand[2][]{%
  \ifx\relax#1\relax\def\keyval{\{empty\}} 
  \else\setkeys{myKeys}{#1}\fi
  we do something which depends to \keyval
}%

\begin{document}

\mycommand[foo=up]{Macro}

\mycommand[foo=middle]{Macro}

\mycommand[]{Macro}

\mycommand[foo=bla]{Macro}

\end{document}

enter image description here

share|improve this answer
    
In general, \ifx\relax#1\relax\else\setkeys{myKeys}{#1}\fi (i.e., not setting keys because the current key-value list is empty) isn't advisable. There may be preset/postset keys, which won't be set with this type of call. xkeyval will not process an empty key-value list, but might call preset/postset keys for the current prefix and family. –  Ahmed Musa Nov 5 '12 at 19:24
    
I know all that! And it has nothing to do with the question to test if the argument is empty or not on macro level and not on xkeyval level. –  Herbert Nov 5 '12 at 19:38
    
A question of opinion! I don't know the difference between the 'levels' you refer to. –  Ahmed Musa Nov 5 '12 at 20:43
    
This comment confuses me. @ErrMist could you elaborate a bit on why you think this is an issue? –  Jörg Nov 8 '12 at 11:54
    
See comment at the foot of my answer. –  Ahmed Musa Nov 8 '12 at 20:10
add comment

Here is another solution:

\documentclass{article}
\usepackage{keyreader}
\usepackage{xcolor}
\makeatletter
\krddefinekeys[JKV]{jorg}[jorg@]{%
  choice/key1/a+x/{
      a+x.do=\def\currcolor{blue},
      a+y.do=\def\currcolor{green},
      b+x.do=\def\currcolor{red},
      b+y.do=\def\currcolor{magenta}
    }
    /\def\val{Value of key1: #1}
  ;
  choice/align/justified/{
      center.do=\def\curralign{center},
      right.do=\def\curralign{flushright},
      left.do=\def\curralign{flushleft},
      justified.do=\def\curralign{@justified},
    }
  ;
}
\krdpresetkeys[JKV]{jorg}{key1=a+x,align=justified}
\def\@justified{}
\def\end@justified{}

% Tests:
\newcommand\jorgcommand[2][]{%
  \krdsetkeys[JKV]{jorg}{#1}%
  \endgraf\bigskip\noindent\textbf{\val}\endgraf
  \begin{\curralign}%
  \removelastskip
  \textcolor{\currcolor}{#2}%
  \end{\curralign}%
}
\makeatother

\begin{document}
\def\atext{%
  \texttt{choice} keys of pre-version~0.5 of \texttt{keyreader} package will always check 
  their values against the state pattern and raise an error if 
  there is no match. So you don't have to specify a fallback error message.
}

\jorgcommand[key1=a+x,align=justified]{\atext}
\jorgcommand[key1=a+y,align=center]{\atext}
\jorgcommand[key1=b+x,align=right]{\atext}
\jorgcommand[key1=b+y,align=left]{\atext}
\jorgcommand{\atext}
% Invalid value for key1 ('x+y' not allowed):
% \jorgcommand[key1=x+y]{\atext}
\end{document}

enter image description here

Note to answer a question by the OP about preset keys

In my example above you find the call \jorgcommand{\atext}. This calls no optional argument, so argument #1 is empty in \jorgcommand. Hence, in this case \jorgcommand uses the values of the keys in the instant

\krdpresetkeys[JKV]{jorg}{key1=a+x,align=justified}

If we were to use

\newcommand\jorgcommand[2][]{%
  \if\relax\detokenize{#1}\relax\else
    \krdsetkeys[JKV]{jorg}{#1}%
  \fi
  ...#2...
}

as Herbert suggested, the preset key-values will not be called. We would, therefore, have inadvertently applied the latest values of the keys, which isn't the intended or right step. Preset keys are set only when \setkeys or \krdsetkeys is instantiated.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.