3

Why is \setlength ineffective inside a tabular environment? Here is an MWE:

\documentclass{article}
\newlength{\globallength}
\newlength{\locallength}
\begin{document}
\setlength{\globallength}{10pt}%
\begin{tabular}{l}
\setlength{\locallength}{10pt}%

\hspace*{10pt}. \\

\hspace*{\globallength}. \\

\hspace*{\locallength}. \\

\hspace*{0pt}. \\

\end{tabular}
\end{document}

While the first two dots are well-aligned, the third one is different and behaves as if \locallength were 0pt. Why is that, and what can I do? I want to use \setwidth inside the tabular environment, so setting the length outside of the environment is not really an option.

bers
  • 5,404
  • 2
    Your length registers are locally set, i.e. in a group. A table cell is a group! That's why you need a \global\setlength{....} –  Mar 25 '16 at 13:16
  • @ChristianHupfer: please post answer. Or is this question a duplicate? – GuM Mar 25 '16 at 13:21
  • 1
    @ChristianHupfer \global\setlength will not work with package calc. – Heiko Oberdiek Mar 25 '16 at 13:22
  • @HeikoOberdiek: Thanks, I did not know this. But calc is no issue here, as far as I can see –  Mar 25 '16 at 13:26
  • 3
    @ChristianHupfer The other version \setlength{\global\locallength} will work with calc, but it won't for \addtolength. A way independent from the implementation of \setlength: \setlength{\locallength}{...}\global\locallength=\locallength. – Heiko Oberdiek Mar 25 '16 at 13:35
  • Prefixing \setlength, \settowidth, \settoheight or \settodepth with \global has never been supported. – egreg Mar 25 '16 at 13:40
  • Thanks, I did not know a table cell is its own group; I was aware that a tabular environment is one, but I had not suspected it even for a table line. – bers Mar 25 '16 at 13:41
  • I'm not sure what you mean by a table line here, surely if a table row is a row of cells, each of which is a group then a one-cell row, as here is naturally a group? – David Carlisle Mar 25 '16 at 13:44
  • The question simply asked "why", in that sense I thought that @ChristianHupfer had answered it. :-) – GuM Mar 25 '16 at 13:50
  • Yes, I mean a row, not a line. My comment was about my understanding of groups in a table before asking this question, when I would have guessed that even a table row is not a group. I know better now. – bers Mar 25 '16 at 13:52
  • there is no group that corresponds to a table row, only the groups that correspond to each cell. – David Carlisle Mar 25 '16 at 13:58
  • 1
    The burden of a non-native speaker... I'll try again: Before asking this question, I was sure the smallest entity in a table that would form a group is the table itself, not a row (this is what my last two comments are about), and certainly not a cell. I know now that a cell is a group, which does not mean that a row is a group, but which means that there are groups "at or below the level of a single table row". – bers Mar 25 '16 at 14:01
  • @GustavoMezzetti: Thanks, but there are answers now and my comments were opposed already (about a feature not in the OP althought ;-)) and an answer by me would be most likely disregarded –  Mar 25 '16 at 15:05

2 Answers2

3

Alignment cells are processed inside an implicit group, so local assignments to variables are undone when the group ends.

The kernel definition of \setlength is

% latex.ltx, line 2181:
\def\setlength#1#2{#1 #2\relax}

which is why \global\setlength seems to work. On the other hand, the definition of \settowidth is

% latex.ltx, line 2187:
\def\settowidth {\@settodim\wd}

and the definition of \@settodim is

% latex.ltx, line 2183:
\def\@settodim#1#2#3{\setbox\@tempboxa\hbox{{#3}}#2#1\@tempboxa
       \setbox\@tempboxa\box\voidb@x}

Thus \global\settowidth{\locallength}{abc} would become

\global\setbox\@tempboxa\hbox{{abc}}\locallength\wd\@tempboxa\setbox\@tempboxa\box\voidb@x

which of course is ineffective in making a global assignment to \locallength.

Yes, \settowidth{\global\locallength}{abc} would work, but it's just by chance.

There is no support in LaTeX for global dimension/skip assignments and you should rely on lower level commands. So a safer way is to define new commands:

\makeatletter
\newlength\local@length@for@global
\newcommand\gsetlength[2]{%
  \setlength{\local@length@for@global}{#2}%
  \global#1\local@length@for@global
}
\newcommand{\gsettowidth}[2]{%
  \settowidth{\local@length@for@global}{#2}%
  \global#1\local@length@for@global
}
\makeatother

and similarly for \gsettoheight and \gsettodepth if needed.

This will work even if calc is loaded and does not exploit any particular implementation of the “local” commands.

egreg
  • 1,121,712
0

Both \global\setlength{\locallength}{10pt} and \setlength{\global\locallength}{10pt} seem to work, but I have no idea why that would be required.

https://tex.stackexchange.com/a/210598/30810 made me try it, and @ChristianHupfer's comment explains, why.

In my longer document, \global\settowidth{...} did not work; \settowidth{\global...} did.

bers
  • 5,404