13

This question is similar to How to conditionally define a new command in LaTeX? but I want to test if the starred version of a command is defined.

The naive thing

\makeatletter
\@ifundefined{ref*}{}{}
\makeatother

doesn't work, for example, for testing whether a starred version of \ref is defined (say, by hyperref) as it always returns true.

14

In general, there is no starred version of a command. What really happens is that the unstarred command checks to see if there is a star as the first argument and passes control to different auxiliary macros accordingly, or simply executes two different sets of commands.

So, to take a random example, we look at \endlargethispage and find that it is defined as:

\gdef \enlargethispage {%
   \@ifstar
     {%
      \@enlargepage{\hbox{\kern\p@}}}%
     {%
      \@enlargepage\@empty}%
}

The \@ifstar is the key here which tests for a wandering star.

It's clear from this example that there is no easy test for whether \enlargethispage* is valid. The most exact test that I can think of is to examine \meaning\enlargethispage for the presence of \@ifstar, but even that won't catch everything (\let\my@ifstar=\@ifstar) and might also produce a false positive (since \meaning converts to a token list of characters and so looking for an exact token inside it might be fooled). But that would be complicated to implement and maybe there is an easier way to achieve whatever it is that you want to achieve.

  • Both very good answers, accepting this one sort of randomly, and because the last sentence got me thinking enough to come up with another way around my problem. Thanks! – Willie Wong Dec 3 '13 at 11:42
12

This is not basically possible, and I'll try to explain why, on an example.

Let's \show\section:

\section=\long macro:
-> \@startsection {section}{1}{\z@ }{-3.5ex \@plus
   -1ex \@minus -.2ex}{2.3ex \@plus .2ex}{\normalfont \Large \bfseries }

Ok, not much to see, other than that \section takes no arguments at all! So let's \show\@startsection:

\@startsection=macro:
#1#2#3#4#5#6->\if@noskipsec \leavevmode \fi \par \@tempskipa #4\relax \@afterindenttrue
  \ifdim \@tempskipa <\z@ \@tempskipa -\@tempskipa \@afterindentfalse \fi
  \if@nobreak \everypar {}\else \addpenalty \@secpenalty \addvspace \@tempskipa \fi
  \@ifstar {\@ssect {#3}{#4}{#5}{#6}}{\@dblarg {\@sect {#1}{#2}{#3}{#4}{#5}{#6}}}

Voilà! It is the \@ifstar in the definition of \@startsection that tells you that \section has a starred variant. How does it work? Notice that \section passes to \@startsection all the 6 mandatory arguments, therefore if you write \section*{BLA}, it becomes \@startsection{<6 arguments>}*{BLA} and this becomes blablabla \@ifstar {\@ssect {#3}{#4}{#5}{#6}}{\@dblarg {\@sect {#1}{#2}{#3}{#4}{#5}{#6}}}*{BLA}. Finally, \@ifstar is a crazily-made command that has two arguments, 1st is used if * follows and 2nd if not.

So, as you can see, checking whether a starred variant of a command is defined, is really not simple. Not to mention that \@ifstar is not the only way how to define a starred variant of a command. The problem is that the star is not a part of the name of the command, it is a first optional argument.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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