11

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?

1 Answer 1

16

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}
1
  • Using inner \defs was actually my first try, and I had to move them out of the macro because of the Undefined control sequence error which I did not understand. Thank you for explaining that as well!
    – adl
    Jul 26, 2014 at 11:15

You must log in to answer this question.

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