eqnarray: redefinition of underscore conflicts with labels containing underscores!

The underscore redefinition I use for having roman subscripts (see example) is preventing me from defining labels within eqnarray environments. How can I solve this? (This problem doesn't appear when I use align, but I really need to use eqnarray now). Thanks, Jorge.

Example:

\documentclass[twocolumn]{article}
\usepackage{amsmath}

\catcode_=\active
\newcommand{_}{\ifmmode\expandafter\sbrm\else\string_\fi}
\newcommand{\sbrm}[1]{\sb{\mathrm{#1}}}

\begin{document}

This compiles:
\begin{align}
1 \neq 2
\label{my_label_1}
\end{align}

This doesn't:
\begin{eqnarray}
1 \neq 2
\label{my_label_2}
\end{eqnarray}

\end{document}


Compilation error:

! Missing \endcsname inserted.
\sb
l.3 ...mathrm  {l}}abel\sb {\mathrm  {2}}}{{2}{1}}

• Please expand your snipped into the code for a complete minimal document which produces the error you report. However, my understanding is that use of eqnarray is not recommended. Moreover, making _ active and then using it in labels is a recipe for disaster later, if not sooner. If you simply must do it this way, rename your labels inside these environments, as that's something you can always change. Generally, colons and hyphens are safe here - although not, of course, if you've made them active and turned them into macros. – cfr Jul 3 '18 at 2:58
• Why do you think you need to use eqnarray? Why do you need to make _ active in order to get roman subscripts? (That sounds as if something is seriously screwed up.) Why can't you avoid underscores in labels? – cfr Jul 3 '18 at 3:27

The first part of the answer you may have figured out already. The first one works because the label is in text mode then _ is _. The second one, on the other hand, is in math mode thus _ is \sbrm and then things go wild. To make sure you can redefine \label before loading amsmath to make it talk to you:

\makeatletter
\def\label#1{%
\ifmmode
\typeout{^^JLabel in math mode for \@currenvir^^J}
\else
\typeout{^^JLabel in horizontal mode for \@currenvir^^J}
\fi
\@bsphack
\protected@write\@auxout{}%
{\string\newlabel{#1}{{\@currentlabel}{\thepage}}}%
\@esphack}


the console screen will say:

Label in horizontal mode for align

Label in math mode for eqnarray


right before throwing an error.

This is because of different implementations. align is from amsmath so they probably preferred to make the labels while in horizontal mode, while eqnarray is from the LaTeX kernel.

Redefining eqnarray to make the \label in text mode looks like too much. Maybe we can do something with \label...

Please, note that I'm just doing this, not recommending it!

Old solution:

We can make it turn the \catcode of _ to other, so that we don't use its macro meaning:

\def\label{%
\begingroup
\catcode\_=12
\l@bel}


Then we define an inner \l@bel that will tokenize the argument and write to the .aux:

\def\l@bel#1{%
\@bsphack
\protected@write\@auxout{}%
{\string\newlabel{#1}{{\@currentlabel}{\thepage}}}%
\@esphack
\endgroup}


note that these changes must be done before you load amsmath because it stores the current definition of \label and you want it to remember the change you made.

New solution:

Actually, a more robust approach, would be not to redefine the internal \label, but to just patch it with the necessary stuff for the catcode change, because some packages (hyperref, I'm looking at you) might erase your definition.

Using etoolbox we can devise a solution that is also hyperref compatible. We store the old meaning of \label in \old@ltx@label, then we create the same \label wrapper as before, and then we append the \endgroup to the \old@ltx@label, all of this after the \begin{document} (not at \begin{document}):

\usepackage{etoolbox}
\AfterEndPreamble{%
\let\old@ltx@label\label
\def\label{%
\begingroup
\catcode\_=12
\old@ltx@label}%
\apptocmd\old@ltx@label\endgroup{}{}
}


I didn't test this solution with other cross-reference packages, though. It might break stuff.

Compilable document:

\documentclass[twocolumn]{article}

\makeatletter
\def\label{%
\begingroup
\catcode\_=12
\l@bel}
\def\l@bel#1{%
\@bsphack
\protected@write\@auxout{}%
{\string\newlabel{#1}{{\@currentlabel}{\thepage}}}%
\@esphack
\endgroup}
\makeatother

\usepackage{amsmath}

\catcode_=\active
\newcommand{_}{\ifmmode\expandafter\sbrm\else\string_\fi}
\newcommand{\sbrm}[1]{\sb{\mathrm{#1}}}

\begin{document}

This compiles:
\begin{align}
1 \neq 2
\label{my_label_1}
\end{align}

This doesn't:
\begin{eqnarray}
1 \neq 2
\label{my_label_2}
\end{eqnarray}

Now both equation~\eqref{my_label_1} and~\eqref{my_label_2} compile :)

\end{document}

• Thank you so much for the thorough reply, Phelype! This totally answers my question. – dontpanic Jul 3 '18 at 4:17

I consider this just for academic interest, because eqnarray should never be used.

You're taking the wrong approach to the problem: since you want the underscore to print as itself in text, the simplest way is to make it math active.

\documentclass[twocolumn]{article}
\usepackage[T1]{fontenc}
\usepackage{amsmath}

\newcommand{\sbrm}[1]{\sb{\mathrm{#1}}}
\catcode_=12
\begingroup\lccode~=_ \lowercase{\endgroup\let~}\sbrm
\AtBeginDocument{\mathcode_="8000 }

\begin{document}

This_compiles:
\begin{align}
1 \neq 2
\label{my_label_1}
\end{align}

This_compiles_too:
\begin{eqnarray}
1 \neq 2
\label{my_label_2}
\end{eqnarray}

\end{document}
`

• Thank you egreg for your solution and explanation, which greatly helps to clarify the problem. As you point out, my interest in this was rather "academic", as I decided to stay away from eqnarray altogether following everyone's advice :) – dontpanic Jul 3 '18 at 22:26