15

Often I have separate .tikz files, which may contain stuff like \newlength{\mytmplen}; and given I'm not very original with names, the same statement ends up in multiple such files. Which turns out to be a problem, if I want to include all those files in a single .tex document -- then upon the second \newlength with the same argument, the compilation process would stop with:

! LaTeX Error: Command \mytmplen already defined.
               Or name \end... illegal, see p.192 of the manual.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.30 \newlength{\mytmplen}

? 

So, I thought of devising a new newlength command, which would define the new length if it doesn't exist; but it would simply typeout a message, and go on if it does exist.

Obviously, I'd have to have a method to check if a command exists; and possibly a method to obtain the command name (e.g. mytmplen) from the command token (e.g. \mytmplen). So far, I found these posts relevant:

I will be posting my solution as answer below; of course, corrections or alternative methods for solving this would be much appreciated!

3 Answers 3

9

Your answer appears to work. A simpler method might consist of first \let-ting the length variable in question to \relax and then applying \newlength to it. Put differently, if you can't remember if a certain macro name has been used before to denote a length parameter (or anything else, really!) and if you're comfortable with (re)using it anyway, you can first \let it to \relax and then issue a \newlength directive.

\documentclass{article}
\begin{document}

\newlength\mytmplen
\setlength\mytmplen{10pt}
\the\mytmplen

\let\mytmplen\relax % let \mytmplen to \relax

\newlength\mytmplen % now it's safe to "re-issue" it as a length 
\setlength\mytmplen{20pt}
\the\mytmplen

\end{document}

enter image description here

7
  • Many thanks for that, @Mico - that is indeed a much simpler approach. Cheers!
    – sdaau
    Jun 15, 2014 at 16:06
  • 3
    @sdaau: Until the lengths registers run out. They are a limited resource. Jun 15, 2014 at 16:57
  • @HeikoOberdiek - Is there a problem with running out of length registers if the same few ones get re-used over and over?
    – Mico
    Jun 15, 2014 at 18:07
  • 2
    @HeikoOberdiek - I guess my question is trying to get at a different issue: Is there a risk of running out of registers if the same macro name, say \mytmplen, is repeatedly first \let to \relax and then reissued as a length register via \newlength?
    – Mico
    Jun 15, 2014 at 18:46
  • 1
    @Mico: Each \newlength allocates a fresh new register, the name of the command sequence does not matter. If you can limit the code inside a group, then \locskip or \locdimen of package etex can be used instead. They are allocating locally and the registers are freed after the group is closed. Jun 15, 2014 at 18:54
9

Here is a \providelength command that will define a new length if not already defined, but also checks whether the command passed as argument has been defined with \newlength, in order to issue an error message if you try to use, say, \providelength{\textit}.

\documentclass{article}

\makeatletter
\newcommand{\providelength}[1]{%
  \@ifundefined{\expandafter\@gobble\string#1}
   {% if #1 is undefined, do \newlength
    \typeout{\string\providelength: making new length \string#1}%
    \newlength{#1}%
   }
   {% else check whether #1 is actually a length
    \sdaau@checkforlength{#1}%
   }%
}

\newcommand{\sdaau@checkforlength}[1]{%
  % get the first five characters from \meaning#1
  \edef\sdaau@temp{\expandafter\sdaau@getfive\meaning#1TTTTT$}%
  % compare with the string "\skip"
  \ifx\sdaau@temp\sdaau@skipstring
    \typeout{\string\providelength: \string#1 already a length}%
  \else
    \@latex@error
      {\string#1 illegal in \string\providelength}
      {\string#1 is defined, but not with \string\newlength}%
  \fi
}
\def\sdaau@getfive#1#2#3#4#5#6${#1#2#3#4#5}
\edef\sdaau@skipstring{\string\skip}
\makeatother

\begin{document}

\providelength{\foo}
\providelength{\foo}
\providelength{\textit}

\stop

Here is the terminal output:

\providelength: making new length \foo
\providelength: \foo already a length

! LaTeX Error: \textit illegal in \providelength.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.31 \providelength{\textit}

? h
\textit is defined, but not with \newlength
? 

A version with expl3, that avoids the trick for finding if the command is already a length.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\providelength}{m}
 {
  \cs_if_exist:NTF #1
   {
    \sdaau_providelength_check:N #1
   }
   {
    \msg_info:nnx { providelength } { making } { \token_to_str:N #1 }
    \newlength{#1}
   }
 }
\cs_new_protected:Nn \sdaau_providelength_check:N
 {
  \token_if_skip_register:NTF #1
   {
    \msg_info:nnx { providelength } { not-making } { \token_to_str:N #1 }
   }
   {
    \msg_error:nnx { providelength } { defined } { \token_to_str:N #1 }
   }
 }
\msg_new:nnn { providelength } { making }
 {
  \token_to_str:N \providelength :~making~new~length~#1
 }
\msg_new:nnn { providelength } { not-making }
 {
  \token_to_str:N \providelength :~#1~is~already~a~length
 }
\msg_new:nnnn { providelength } { defined }
 {
  #1~illegal~in~\token_to_str:N\providelength
 }
 {
  #1~is~defined,~but~not~with~\token_to_str:N\newlength
 }
\ExplSyntaxOff

\begin{document}

\providelength{\foo}
\providelength{\foo}
\providelength{\textit}

\stop

This is the excerpt from the log file

.................................................
. providelength info: "making"
. 
. \providelength: making new length \foo
.................................................
\foo=\skip49
.................................................
. providelength info: "not-making"
. 
. \providelength: \foo is already a length
.................................................


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! providelength error: "defined"
! 
! \textit illegal in \providelength
! 
! See the providelength documentation for further information.
! 
! For immediate help type H <return>.
!...............................................  

l.47 \providelength{\textit}

? h
|'''''''''''''''''''''''''''''''''''''''''''''''
| \textit is defined, but not with \newlength
|...............................................
1
  • Many thanks for that, @egreg - good to have that error checking facility; cheers!
    – sdaau
    Jun 15, 2014 at 16:27
1

Ok, so first I looked up the definitions of \newlength and further using texdef:

$ texdef -t latex \newlength

\newlength:
macro:#1->\@ifdefinable #1{\newskip #1}

$ texdef -t latex \@ifdefinable

\@ifdefinable:
\long macro:#1#2->\edef \reserved@a {\expandafter \@gobble \string #1}\@ifundefined \reserved@a {\edef \reserved@b {\expandafter \@carcube \reserved@a xxx\@nil }\ifx \reserved@b \@qend \@notdefinable \else \ifx \reserved@a \@qrelax \@notdefinable \else #2\fi \fi }\@notdefinable 

$ texdef -t latex \@ifundefined

\@ifundefined:
macro:#1->\expandafter \ifx \csname #1\endcsname \relax \expandafter \@firstoftwo \else \expandafter \@secondoftwo \fi 

So, apparently, one can get the command name from the command token, by using the stanza:

\edef\reserved@a{\expandafter\@gobble\string#1} 

... and then the name in \reserved@a (without the backslash) can be used in macros that check for macro existence by command name.

Using those definitions, I've put together this code for a reusable newlength command, called \nnewlength:

\documentclass{article}

\makeatletter
\def\nnewlength#1{%
  \edef\reserved@a{\expandafter\@gobble\string#1} %
  \@ifundefined\reserved@a{%
    \edef\reserved@b{\expandafter\@carcube\reserved@a xxx\@nil} %
    \ifx\reserved@b\@qend %
      \typeout{  -- Not definable (1, \reserved@a): #1} %\@notdefinable %
    \else %
      \ifx\reserved@a\@qrelax %
        \typeout{  -- Not definable (2, \reserved@a): #1} %\@notdefinable %
      \else %
        \typeout{  -- Making newskip: #1}
        \newskip#1%
      \fi %
    \fi %
  }%
  {\typeout{  -- Not definable (E, \reserved@a): #1}} %\@notdefinable%
}
\makeatother

\typeout{1st pass...}
\nnewlength{\mytmplen}
\typeout{2nd pass...}
\nnewlength{\mytmplen}

\begin{document}

\typeout{3rd pass...}
\nnewlength{\mytmplen}

\end{document}

The output is:

This is pdfTeX, Version 3.1415926-2.3-1.40.12 (TeX Live 2011)
...
1st pass...
 -- Making newskip: \mytmplen 
2nd pass...
 -- Not definable (E, mytmplen): \mytmplen 
(./test.aux)
3rd pass...
 -- Not definable (E, mytmplen): \mytmplen 
(./test.aux) )
No pages of output.
Transcript written on test.log.

You must log in to answer this question.

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