How can one write a meta-macro that adds a starred version to a command?
Intended use would be along the lines of
\newcommand\foo[1]{foo is #1}
\addstarred\foo[2]{foo is #1, bar is #2}
How can one write a meta-macro that adds a starred version to a command?
Intended use would be along the lines of
\newcommand\foo[1]{foo is #1}
\addstarred\foo[2]{foo is #1, bar is #2}
A method is already available with the package suffix by David Kastrup. Needless to say, it's full of clever tricks.
You can say
\usepackage{suffix}
\newcommand{\foo}[1]{foo is #1}
\WithSuffix\newcommand\foo*[2]{foo is #1, bar is #2}
and it may be instructive to see how the objective is achieved.
If we do \show\foo after the second instruction, we find
> \foo=\protected macro:
->\WSF@suffixcheck \foo .
so we learn that suffix requires e-TeX (not a problem nowadays) and redefines \foo to mean \WSF@suffixcheck\foo. So we add \makeatletter and try \show\WSF@suffixcheck, getting
> \WSF@suffixcheck=macro:
#1->\begingroup \def \reserved@a {#1}\futurelet \reserved@b \WSF@suffixcheckii
so the argument is saved in \reserved@a and
\futurelet\reserved@b\WSF@suffixcheckii
is executed. This makes \reserved@b to be equivalent to the token that follows \WSF@suffixcheckii. If the call is
\foo{foo}
then \reserved@b will be \bgroup; if the call is
\foo*{foo}{bar}
then \reserved@b will be *. Now we need to know what \WSF@suffixcheckii does:
> \WSF@suffixcheckii=macro:
->\ifcsname \expandafter \SuffixName \reserved@a \reserved@b \endcsname
\expandafter \WSF@suffixcheckiii \else \expandafter \WSF@suffixcheckiv \fi .
OK, let's see what happens in the \foo{foo} case: \reserved@a expands to \foo, while \reserved@b is \bgroup (unexpandable), so TeX is first presented with
\ifcsname\SuffixName\foo\reserved@b\endcsname
and \SuffixName is defined by
> \SuffixName=\long macro:
#1->WSF:\string #1 \meaning .
so the next step is
\ifcsname WSF:\string\foo \meaning\reserved@b\endcsname
and we finally get
\ifcsname WSF:\foo begin-group character {\endcsname
where all characters have category code 12 (but spaces have 10). In the \foo*{foo}{bar} case we would get
\ifcsname WSF:\foo the character *\endcsname
The command \csname WSF:\foo begin-group character {\endcsname is not defined, so the false branch is followed, that is
\expandafter \WSF@suffixcheckiv \fi
which simply leaves
\WSF@suffixcheckiv{foo}
in the input stream. Now \show\WSF@suffixcheckiv gives
> \WSF@suffixcheckiv=macro:
->\expandafter \endgroup \csname \expandafter \NoSuffixName \reserved@a \endcsname .
so the group previously opened is closed but first
\csname \expandafter \NoSuffixName \reserved@a \endcsname
is formed. Recall that \reserved@a expands to \foo, so we get
\csname \NoSuffixName \foo \endcsname
and \NoSuffixName is
> \NoSuffixName=macro:
->WSF:\string .
so finally we obtain
\csname WSF:\string\foo\encsname
OK, let's issue \expandafter\show\csname WSF:\string\foo\endcsname:
> \WSF:\foo=\long macro:
#1->foo is #1.
that is, this complicated macro is a copy of the original \foo.
In the case of \foo*{foo}{bar} we'd have
\ifcsname WSF:\foo the character *\endcsname
but in this case this is defined; indeed
\expandafter\show\csname WSF:\string\foo\space the character *\endcsname
produces
> \WSF:\foo the character *=\long macro:
#1#2->foo is #1, bar is #2.
so this macro with a complicated name is what you have defined as *-variant.
Almost any token can be used as a suffix, with this package. But the essential idea is no different from what you have devised; the protections against overwriting possible existing macro names are better. What the package does when
\WithSuffix\newcommand\foo*[2]{foo is #1, bar is #2}
is processed is
Save the original \foo command under
\csname WSF:\string\foo\endcsname
(if this already exists because of a preceding \WithSuffix applied to \foo this step is of course omitted)
Save the new definition under
\csname WSF:\string\foo\space the character *\endcsname
Use the abstract interface described above to choose among different suffixes.
if next-token == '*' into a switch-like construct selecting whatever command has been defined for a given suffix.
–
Sep 22 '14 at 12:54
xparse method.
– egreg
Sep 22 '14 at 13:18
My own attempt at a solution is below, with improvements kindly provided by @egreg and @DavidCarlisle.
\documentclass{standalone}
\makeatletter
\newcommand\addstarred[1]{%
\expandafter\let\csname\string#1@nostar\endcsname#1%
\edef#1{\noexpand\@ifstar\expandafter\noexpand\csname\string#1@star\endcsname\expandafter\noexpand\csname\string#1@nostar\endcsname}%
\expandafter\newcommand\csname\string#1@star\endcsname%
}
\makeatother
\newcommand\foo[1]{foo is #1}
\addstarred\foo[2]{foo is #1, bar is #2}
\begin{document}
\foo{red} --- \foo*{red}{green}
\end{document}
Result:

Explanation:
\foo is stored as \\foo@nostar.\foo is redefined to check for a star and call either \\foo@star or \\foo@nostar.
This is done with edef so that the constructed token names can be expanded in place and not every time the command is invoked.\newcommand for \\foo@star is started and will take the rest of the definition as follows \addstarred\foo.\foo in \\foo will definitely conflict if \foo had been defined to have an optional argument.
– egreg
Sep 22 '14 at 09:09
% on each of the lines of the definition that doesn't have a % and do not need a % on the line that has one.
– David Carlisle
Sep 22 '14 at 09:35
% to all lines now. I assumed that, since this should appear in the preamble, extra line breaks would not matter.
–
Sep 22 '14 at 09:45
\edef#1{\@ifstar\expandafter\noexpand\csname\string#1@star\endcsname\expandafter\noexpand\csname\string#1@nostar\endcsname}}% so that the defined command just had two tokens after ifstar with the new command names made at define time
– David Carlisle
Sep 22 '14 at 10:13
\WithSuffix? Than this answer can be usefull: http://tex.stackexchange.com/a/4388/3061 – quinmars Sep 22 '14 at 09:05\WithSuffixcommand works? – Sep 22 '14 at 09:11\WithSuffixworks. It does quite similarly to what you propose, by the way. The code is\WithSuffix\newcommand\foo*[2]{foo is #1, bar is #2}(with\usepackage{suffix}). – egreg Sep 22 '14 at 09:44\foois defined before the use of\addstarred\foo? Do we have to allow for LaTeX 'robust' commands (I assume we do)? What is expected if\foois redefined after\addstarredor if\addstarredis applied multiple times? – Joseph Wright Sep 22 '14 at 10:56\foowas already defined and to add the\@ifstarswitch to its definition. I have mostly asked to find out whether there are other, more natural possibilities. The answer "from the Book" would probably be a simple\newcommand\foo*that is impossible to break. – Sep 22 '14 at 11:06