Of course, they have different syntax.
\ifx\cyrdash\undefined
is traditional way to test if a macro is undefined. Of course, \undefined
sould not be defined.
There are \ifdefined
and \ifcsname
primitives in eTeX. They have no other side effects.
LaTeX's \@ifundefined
test if a macro is defined or it has the same meaning of \relax
. Furthermore, \@ifundefined{undefinedfoo}{...}{...}
will makes \undefinedfoo
to be \relax
.
Normally, there is no difference between
\ifx\foo\undefined A \else B \fi
and
\ifdefined\foo B \else A \fi
eTeX primitives may be a little safer. Say, we don't need to worry about whether \undefined
is really undefined.
However, these two usage are different:
% wrong
% \expandafter\ifx\csname foo\endcsname\undefined A \else B \fi % This is always false
\expandafter\ifx\csname foo\endcsname\relax A \else B \fi % This is \@ifundefined
and
\ifcsname foo\endcsname B \else A \fi
In fact, \csname undefined\endcsname
makes \undefined
to be \relax
, while \ifcsname undefined\endcsname
makes \undefined
unchanged. That's why \ifcsname
in eTeX is necessary.
Test code:
\documentclass{minimal}
\long\def\themeaning#1{\string#1: \meaning#1\par}
\begin{document}
\makeatletter\ttfamily
\def\known{abc}
\let\empty\relax
% initial
\themeaning\known
\themeaning\empty
\themeaning\unknown
\hrulefill
% useful eTeX extension
\ifdefined\known yes\else no\fi\par
\ifdefined\empty yes\else no\fi\par
\ifdefined\unknown yes\else no\fi\par
\hrulefill
% Or
\ifcsname known\endcsname yes\else no\fi\par
\ifcsname empty\endcsname yes\else no\fi\par
\ifcsname unknown\endcsname yes\else no\fi\par
\hrulefill
% the meanings are unchanged
\themeaning\known
\themeaning\empty
\themeaning\unknown
\hrulefill
% LaTeX2e kernel
\@ifundefined{known}{yes}{no}\par
\@ifundefined{empty}{yes}{no}\par
\@ifundefined{unknown}{yes}{no}\par
\hrulefill
% \unknown is changed
\themeaning\known
\themeaning\empty
\themeaning\unknown
\end{document}
It's one of the uncountable ways to check whether an argument is empty. Almost all of them suffer of some drawback: in the case at hand, the argument mustn't be \\
.
What does \ifx
do? It compares the two tokens following it, without expanding them. The test return success if both tokens are equal in the sense of \meaning
or \show
:
if they are characters (or control sequences \let
to a character), they must represent the same (character code, category code) pair
if they are control sequences (but not \let
to a character), they must have the same meaning; the cases are too many to list them all.
Before analyzing the macro, we should know something about TeX conditional: the abstract form is
\ifZ<test><true text>\else<false text>\fi
where \ifZ
denotes one of the primitive conditionals. The <test>
can take many forms, depending on \ifZ
(it's empty, in some cases); what is important is that the <true text>
is everything that goes from the end of <test>
to \else
(or \fi
if \else
is missing).
So let's see what happens when we call \mycommand{abc}
, that is, the default argument, which is empty, is substituted to #1
: the tokens that TeX sees are
\ifx\\\\<true>\else<false>\fi
Of course the test is true, because the tokens that follow \ifx
are both \\
.
When we call \mycommand[xyz]{abc}
, instead, TeX sees
\ifx\\xyz\\<true>\else<false>\fi
and TeX compares \\
to x
and they are different. But now the <true text>
is
yz\\<true>
(remember: the <true text>
is what goes from the test to the \else
) and it will be ignored altogether.
This test is quite safe, because it doesn't do expansion; however it might fail miserably if the user calls \mycommand[\\]{abc}
, so often different delimiter tokens are used, which are supposed not to creep into user's input (for instance \uchyph
or \vfuzz
). Another similar test might be
\if\relax\noexpand#1\relax
with the caveat that \if
does expand what's next until two unexpandable tokens remain. The first token is \relax
, which is unexpandable. The second one will be what becomes of the expansion of \noexpand
, that is, the first token of #1
(if non empty) but made unexpandable; if #1
is empty, then \noexpand
applies to \relax
, resulting again in \relax
because it's unexpandable.
In both cases some token is reserved (\\
in the first case, \relax
in the second one). The safest test is
\if\relax\detokenize{#1}\relax
because the expansion of a non empty #1
can never give a first token equivalent to \relax
: even if #1
is \relax
, \detokenize{\relax}
will give the string \relax
(the first token of which is the character \
).
Again, if #1
is empty, the test compares \relax
to \relax
; if #1
is not empty, everything will be ignored up to the \else
. Of course it requires that an engine with the e-TeX extensions is run: it won't work with "Knuth TeX".
Best Answer
The conditional
\ifx
does no expansion and it compares the next two tokens. So in your test it compares\myarg
with the first token in#1
.Therefore the test will be always false, because
#1
is the name of a section level counter.Adding
\expandafter
before\ifx
would not help, even adding braces around#1
:would become
which returns false, as it compares
s
withe
.You could use
\pdfstrcmp
(or, better,\pdf@strcmp
with thepdftexcmds
package):Complete example:
Note that the attempt of doing the comparison with
\ifx
in the following waywouldn't work either. This is because
\@seccntformat
is passed through an\edef
(in the form\protected@edef
), so we'd get an errorfor
\@argument
. Adding\noexpand
in front of\@argument
wouldn't help, because the conditional is expanded during the\edef
. This instead would workbecause the argument to
\unexpanded
is not changed during an\edef
, so it will be processed afterwards, when typesetting is involved.Note that the
\protected@edef
is necessary because we must get the expansion of\thesection
or\thesubsection
at the moment the macro\@seccntformat
is executed. An\unexpanded
free solution is possible, by hiding the part in a macro: