19

Why does the following not work at all?

\documentclass{article}

\makeatletter
\newcommand{\foo}[1]{\@ifstar{\textbf{#1}}{\textit{#1}}}
\makeatother

\begin{document}

\foo{test}
\foo*{test}

\end{document}

The output seems to be "test" written correctly as italicized for the non-star variant and "*test" ("test" written normally with no bold weight, "*" written italicized) written for the star variant.

5 Answers 5

35

The reason is that with \foo*{test} * is the argument for \foo and {test} is just the word text in a group. \foo must be a macro without argument, which calls another macro with argument

\documentclass{article}

\makeatletter
\newcommand{\foo}{\@ifstar{\@foob}{\@fooi}}
\newcommand{\@foob}[1]{\textbf{#1}}
\newcommand{\@fooi}[1]{\textit{#1}}
\makeatother

\begin{document}

\foo{test}
\foo*{test}

\end{document}
2
  • Of course, the definition of \foo could be simplified as in the other answers. But this answer is valuable because it covers the general case, if the code arguments of \@ifstar are more complex than a simple \textbf or \textit. Jun 22, 2017 at 22:37
  • 1
    I marked this as the answer because the original issue I had was with redefining \section, and I came up with \textbf vs \textit as a better minimal example, which coincidentally had a better solution not applicable to my problem. Jun 22, 2017 at 22:55
18

Macro \foo is defined with one argument. If it is called as \foo*{test}, then the star becomes the argument (#1); then \foo is expanded and \@ifstar is called. But the following token { is not a star. The argument, the star, is then set as \textit{*}. The processing of \foo is finished and {test} is interpreted as group with the word test inside.

The example can be fixed the following way:

\documentclass{article}

\makeatletter
\newcommand*{\foo}{\@ifstar\textbf\textit}
\makeatother

\begin{document}

\foo{test}
\foo*{test}

\end{document}

Result

Macro \foo is defined without argument, thus that \@ifstar can look for a following star. Then the code in the arguments for "with star" and "without star" of \@ifstar read the following argument.

If the code in the argument of \@ifstar is more complicate, then the more general answer of Mike helps.

11

In your definition the argument has already been read before the \@ifstar comes into the picture. So you would have to put the star behind the argument (remaek that it eats a space::

\documentclass{article}

\makeatletter
\newcommand{\foo}[1]{\@ifstar{\textbf{#1}}{\textit{#1}}}
\makeatother

\begin{document}

\foo{test}
\foo{test}*

\end{document}

enter image description here

But what you want is probably this:

\documentclass{article}

\makeatletter
\newcommand{\foo}{\@ifstar\textbf\textit}
\makeatother

\begin{document}

\foo{test}
\foo*{test}

\end{document}

enter image description here

10

As you have it, with \foo* the * is already consumed as #1, use:

\documentclass{article}

\makeatletter
\newcommand{\foo}{\@ifstar{\textbf}{\textit}}
\makeatother

\begin{document}

\foo{test}
\foo*{test}

\end{document}
9

With xparse it would be:

\usepackage{xparse}
\NewDocumentCommand\foo{sm}{\IfBooleanTF{#1}{\textbf{#2}}{\textit{#2}}}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .