[Tex/LaTex] testing parameter values with \ifx

conditionalsdebuggingmacrostex-core

Let's say that I would like to redefine \@seccntformat so that whenever the section counter is printed, it is prefixed by Section, but other counters should no be changed.

Here is my failed attempt:

\documentclass{article}

\makeatletter
\def\mytest{section}%
\def\myarg#1{#1}
\def\@seccntformat#1{%
  \ifx\myarg#1\mytest{Section}\fi%
  \csname the#1\endcsname\quad}
\makeatother

\begin{document}
\section{Foo}  % Should be printed as "Section 1  Foo"
\subsection{Bar}  % Should be printed as "1.1  Bar"
\end{document}

For some reason I can't manage to get \myarg#1 and \mytest to compare equal when #1 is section. Now this is my first time using \ifx, so I hope I'm missing something trivial.

I would appreciate some hints about how to debug such a macro. Is there a way to see each intermediate expansion step and observe what arguments \ifx is actually receiving?

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:

\expandafter\ifx\myarg{#1}\mytest

would become

\ifx section\mytest

which returns false, as it compares s with e.

You could use \pdfstrcmp (or, better, \pdf@strcmp with the pdftexcmds package):

\def\@seccntformat#1{%
  \ifnum\pdf@strcmp{#1}{section}=\z@
    Section % a space is wanted
  \fi
  \csname the#1\endcsname\quad}

Complete example:

\documentclass{article}
\usepackage{pdftexcmds}

\makeatletter
\def\@seccntformat#1{%
  \ifnum\pdf@strcmp{#1}{section}=\z@
    Section %
  \fi
  \csname the#1\endcsname\quad}
\makeatother

\begin{document}
\section{Foo}  % Should be printed as "Section 1  Foo"
\subsection{Bar}  % Should be printed as "1.1  Bar"
\end{document}

enter image description here

Note that the attempt of doing the comparison with \ifx in the following way

\def\section@argument{section}
\def\@seccntformat#1{%
  \def\@argument{#1}%
  \ifx\@argument\section@argument
    Section %
  \fi
  \csname the#1\endcsname\quad}

wouldn't work either. This is because \@seccntformat is passed through an \edef (in the form \protected@edef), so we'd get an error

Undefined control sequence

for \@argument. Adding \noexpand in front of \@argument wouldn't help, because the conditional is expanded during the \edef. This instead would work

\def\section@argument{section}
\def\@seccntformat#1{%
  \unexpanded{%
    \def\@argument{#1}%
    \ifx\@argument\section@argument
      Section %
    \fi
  }
  \csname the#1\endcsname\quad
}

because 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:

\def\section@argument{section}
\def\check@section#1{%
   \def\@argument{#1}%
   \ifx\@argument\section@argument
     Section %
   \fi
}
\def\@seccntformat#1{%
  \noexpand\check@section{#1}%
  \csname the#1\endcsname\quad}
Related Question