5

It's in the title. I'm trying to make a command-creating macro which would quickly create a length and setting it to some \settowidth{}{} output, then making it a command for horizontal spacing.

I also tried with \csname#1\endcsname but it doesn't work either.

\documentclass{article}

\newcommand{\newspace}[2]{% \newlength{#1aux}% \settowidth{#1aux}{#2}% \newcommand{#1}{\hspace*{#1aux}}}

\begin{document}

\newspace{\QQspace}{QQ}

AQQA
A\QQspace A

\end{document}

3 Answers3

6

You don't need to allocate a length: just define \QQspace to \hspace*{<width>}, where the width can be computed using a scratch length.

\documentclass{article}

\makeatletter \newcommand{\newspace}[2]{% \settowidth{\dimen@}{#2}% \ExpandArgs{ne}\newcommand{#1}{\noexpand\hspace*{\the\dimen@}}% } \makeatother

\begin{document}

\newspace{\QQspace}{QQ}

AQQA

A\QQspace A

\texttt{\meaning\QQspace}

\end{document}

This uses \ExpandArgs{ne} that

  1. jumps over the next token, here \newcommand;
  2. jumps over the next braced argument, because of n, here {#1};
  3. fully expands the contents of the next braced argument.

This explains the \noexpand in front of \hspace and \the in front of \dimen@.

enter image description here

With “classic” code, the \ExpandArgs line might be

\begingroup\edef\x{\endgroup
  \noexpand\newcommand{\noexpand#1}{\noexpand\hspace*{\the\dimen@}}%
}\x
egreg
  • 1,121,712
4

The crux of the answer is given in How to define a macro to create a new macro with a name passed as its argument? with the #1aux macro requiring additional \csnames and associated \expandafters to ensure the \csname happens first.

\newcommand{\newspace}[2]{%
    \expandafter\newlength\expandafter{\csname#1aux\endcsname}%
    \expandafter\settowidth\expandafter{\csname#1aux\endcsname}{#2}%
    \expandafter\newcommand\expandafter{\csname#1\endcsname}{%
        \expandafter\hspace\expandafter*\expandafter{\csname#1aux\endcsname}}}

However with the aid of the calc package, \widthof circumvents a lot of unpleasant boilerplate code

\newcommand{\betternewspace}[2]{%
    \expandafter\newcommand\expandafter{\csname#1\endcsname}{\hspace*{\widthof{#2}}}}

\hphantom{#2} (which doesn't require calc) has a similar effect to \hspace*{\widthof{#2}} but acts as \hspace rather than \hspace*.

\documentclass{article}
\usepackage{calc}

\newcommand{\newspace}[2]{% \expandafter\newlength\expandafter{\csname#1aux\endcsname}% \expandafter\settowidth\expandafter{\csname#1aux\endcsname}{#2}% \expandafter\newcommand\expandafter{\csname#1\endcsname}{\expandafter\hspace\expandafter*\expandafter{\csname#1aux\endcsname}}}

\newcommand{\betternewspace}[2]{% \expandafter\newcommand\expandafter{\csname#1\endcsname}{\hspace*{\widthof{#2}}}}

\begin{document}

\newspace{QQspace}{QQ} \betternewspace{QQspaceTwo}{QQ} \noindent% AQQA\ A\QQspace B\ A\QQspaceTwo B\ \end{document}

Compiled code showing AQQA on one line and a gap equal to QQ between A and B on the second and third lines

Dai Bowen
  • 6,117
  • 1
    You're trying to create two distinct macros from a single macro, if #1 is a macro then \newlength{#1aux} is just going to create a length equal to #1 (and then do something undesirable with the remaining tokens), to generate a new macro name you'll have to use \csname to append the aux and you could work around that with \expandafter\string\@gobble as in this answer but that seems to make life more difficult not less. – Dai Bowen Apr 25 '23 at 01:44
1

Here is a solution based on Dai Bowen's answer, but restablishing the backslash in the argument.

Credit: Martin Scharrer.

\documentclass{article}
\usepackage{calc} % Provides the useful \widthof command.

\makeatletter % Makes the character "@" a normal letter (here needed to call the @gobble macro).

\newcommand{\newspace}[2]{% \expandafter\newcommand\csname\expandafter@gobble\string#1\endcsname{% \hspace*{\widthof{#2}}% }% }

\makeatother % Resets @'s catcode to default.

\begin{document}

\newspace{\QQspace}{QQ} 

AQQA

A\QQspace A

\end{document}