24

Sorry this might be a very simple question, I am trying to cleanup a style file, and would like to write a command that basically runs the following code:

\newlength{<name>}\setlength{<name>}{<value>}

Based on another command that runs a \def, I though I'd be able to use expandafter and csname easily; here is what I tried:

\newcommand{\deflen}[2]{%
    \expandafter\newlength{%
    \csname #1\endcsname}\setlength{%
    \csname #1\endcsname}{#2}
}

Unfortunately, this seems to generate an error related to csname being already defined. I think it means that I can't use csname twice in the same expand, but I don't know how to define the name first, and then use it twice... Help? :)

cmhughes
  • 100,947
Jonathan H
  • 1,000

2 Answers2

21

You need to assemble the control sequence name before defining with \newcommand, and before using it. This can be achieved using \expandafter twice.

\newcommand{\deflen}[2]{%      
    \expandafter\newlength\csname #1\endcsname
    \expandafter\setlength\csname #1\endcsname{#2}%
}

\deflen{mylength}{12pt}

\showthe\mylength
David Carlisle
  • 757,742
Ian Thompson
  • 43,767
12

Ian Thompson's code is good, but you could add the check whether you can define a new length in the first place; calling, say,

\deflen{textwidth}{6pt}

would issue an error message at \newlength, but it would set \textwidth nonetheless. Since you are in a style file you don't need \makeatletter for @ commands, so you can do

\newcommand{\deflen}[2]{%
  \expandafter\@ifdefinable\csname #1\endcsname{\prefix@deflen{#1}{#2}}%
}
\newcommand{\prefix@deflen}[2]{%
  \expandafter\newlength\csname #1\endcsname
  \expandafter\setlength\csname #1\endcsname{#2}%
}

The code given as second argument to \@ifdefinable is executed only if the control sequence given as first argument is undefined; otherwise an error is raised and the code is ignored.

Note that prefix stands for the prefix used in the style file for not clobbering other packages' macros.

A LaTeX3 version could be

\RequirePackage{xparse}
\ExplSyntaxOn

\NewDocumentCommand{\deflen}{ s m m }
 {
  \IfBooleanTF { #1 }
   { \prefix_deflen:nnn { dim }  { #2 } { #3 } }
   { \prefix_deflen:nnn { skip } { #2 } { #3 } }
 }

\cs_new_protected:Npn \prefix_deflen:nnn #1 #2 #3
 {
  \cs_if_exist:cTF { #2 }
   {
    \msg_error:nnn { package } { already~defined } { #2 }
   }
   {
    \__prefix_deflen:nnn { #1 } { #2 } { #3 }
   }
 }
\cs_new_protected:Npn \__prefix_deflen:nnn #1 #2 #3
 {
  \use:c { #1_new:c } { l_prefix_#2_#1 }
  \use:c { #1_set:cn } { l_prefix_#2_#1 } { #3 }
  \cs_set_eq:cc { #2 } { l_prefix_#2_#1 } % user level name
 }

\msg_new:nnnn { package } { already~defined }
 {
  Control~sequence~\exp_not:c { #1 } already~defined.
 }
 {
  The~control~sequence~\exp_not:c { #1 } already~exists.~
  Please~use~a~new~name;~the~command~will~not~be~executed.
 }

\ExplSyntaxOff

Note that \deflen{foo}{6pt} will define \l_prefix_foo_skip as the internal name of the register, but also \foo at the user level. The *-version would allocate a rigid length register (a \dimen, in TeXspeak).

Test code:

\deflen{foo}{3pt}
\showthe\foo
\show\foo

\deflen*{baz}{3pt}
\showthe\baz
\show\baz

\deflen{textwidth}{3pt}

Here's the terminal output:

> 3.0pt.
l.40 \showthe\foo

? 
> \foo=\skip47.
l.41 \show\foo

? 
> 3.0pt.
l.44 \showthe\baz

? 
> \baz=\dimen133.
l.45 \show\baz

? 

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! package error: "already defined"
! 
! Control sequence \textwidth already defined.
! 
! See the package documentation for further information.
! 
! For immediate help type H <return>.
!...............................................  

l.46 \deflen{textwidth}{3pt}

? h
|'''''''''''''''''''''''''''''''''''''''''''''''
| The control sequence \textwidth already exists. Please use a new name; the
| command will not be executed.
|...............................................
? 
egreg
  • 1,121,712
  • Your answer is incredible, I didn't even know there was a LaTeX3.. Thank you very much! – Jonathan H Apr 18 '14 at 20:53
  • @Sh3ljohn There's not yet a LaTeX3, but its programming layer is already usable along with current LaTeX. As you see, there's no visible \expandafter. Once one gets the hang of it, programming is really easier. – egreg Apr 18 '14 at 20:55