How does this TeX code generate automatic \hline's in tabular environments?

Breaking the code

\catcode`@=11
\let \savecr \@tabularcr
\def\@tabularcr{\savecr\hline}
\catcode`@=12

down step-by-step:

  • \catcode`@=11

    This changes the category code for @ to 'letter', since @ may otherwise not be used inside macro names. You'll note that the subsequent line uses \@tabularcr, for which we need @ to be of type 'letter'. Others like to use \makeatletter for this (see What do \makeatletter and \makeatother do?).

  • \let \savecr \@tabularcr

    This makes an immediate copy of \@tabularcr and stores it in \savecr. It allows you to now modify \@tabularcr (subsequent line) yet still have a copy of the older version.

  • \def\@tabularcr{\savecr\hline}

    Here \@tabularcr is actually redefined to be \savecr (stored above as a copy of \@tabularcr before this redefinition followed by \hline - the horizontal rule you're after. Why (re)define \@tabularcr? From the LaTeX kernel (latex.ltx), the definition of the tabular environment executes (at some later stage)

    \def\@tabular{\leavevmode \hbox \bgroup $\let\@acol\@tabacol
       \let\@classz\@tabclassz
       \let\@classiv\@tabclassiv \let\\\@tabularcr\@tabarray}
    

    Here you can see (in the last line) that \\ is set to be equivalent to \@tabularcr before starting the actual tabular, so \\ will have the new definition of \@tabularcr which now includes an appended \hline.

  • \catcode`@=12

    This restores the category code of @ to other. Some users prefer using \makeatother.

For a reference on what category codes are, see What are category codes?


Of course, the addition of this before a tabular makes it global within your document. It's perhaps more wise to contain this within a group (or as an environment) to localize the change. Alternatively, you can define command switches to "activate" this \hline automation and "deactivate" it later.


That code seems to work, but it doesn't. Let's look at an example:

\documentclass{article}

\catcode`@=11
\let \savecr \@tabularcr
\def\@tabularcr{\savecr\hline}
\catcode`@=12

\begin{document}
\begin{tabular}{c}
a\\[1cm]
b
\end{tabular}
\end{document}

that outputs

enter image description here

Let's see why. The original definition of \@tabularcr is

% latex.ltx, line 5040:
\def\@tabularcr{%
  {\ifnum0=`}\fi\@ifstar\@xtabularcr\@xtabularcr}

and already something is to be noted: if one, by mistake or because some code is being shared between tabular and longtable, types \\* for ending a row, the * will not be recognized and removed by the redefined command, because the token after \savecr is always \hline.

So the new \@tabularcr leaves in the input stream

\@xtabularcr\hline

and here's where things go wrong again, because the definition of \@xtabularcr is

% latex.ltx, line 5042:
\def\@xtabularcr{\@ifnextchar[\@argtabularcr{\ifnum0=`{\fi}\cr}}

The token next to \@xtabularcr will never be [, and this is the cause for the bad output of my example.


What's the best way to automatically add a rule after every row? None, for at least two reasons:

  1. Horizontal rules are not necessary after every row.
  2. You can't add \cline where you need it.

Tags:

Tables