# Macro with optional parameter returning calculated length

I'm trying to define a macro returning a calculated length, but making the parameter optional breaks the macro (error Missing number, treated as zero.):

\documentclass{article}

\usepackage{xfp}
\usepackage{numprint}

\newcommand{\testa}[1]{\fpeval{#1*0.03}\linewidth}
\newcommand{\testb}[1][1]{\fpeval{#1*0.03}\linewidth}

\begin{document}

\lenprint[pt]{\testa{1}}% prints 10,34958 pt

\lenprint[pt]{\testa{2}}% prints 20,69915 pt

\lenprint[pt]{\testb}% gives ERROR and prints 0.030 pt

\lenprint[pt]{\testb[2]}% gives ERROR and prints 0.060 pt

\end{document}


Can you help understand why?

Is there a way to make the parameter optional without breaking the working macro?

N.B. The above MWE uses \lenprint, but I'm asking for a solution working in general or at least with \addtolength, since my real world need is to use the macro as argument of \addtolength.

• A command with an optional argument is not fully expandable, a required feature if you want to use it in the argument to \lenprint. Jun 25 '21 at 8:31
• @egreg Is there a way to make the parameter optional without breaking the working macro?
– mmj
Jun 25 '21 at 8:37
• Very short answer: no. Jun 25 '21 at 8:39

You can define \testb as expandable macro. For example:

\long\def\testb#1{\ifx[#1\afterfi{\testbA[}\else\afterfi{\testbA[1]#1}\fi}
\def\testbA[#1]{\fpeval{#1*0.03}\linewidth}
\long\def\afterfi#1#2\fi{\fi#1}


It will not work in all cases but your case works:

\lenprint[pt]{\testb}% works

\lenprint[pt]{\testb[2]}% works

• Just by chance; \lenprint does \setlength{\@tempdima}{#2} and so you get \@tempdima=\testb\relax which works because there is a token after \testb that can be taken as the argument. But it's very specific and other usages of \testb will definitely break. Jun 25 '21 at 12:29
• And it works also for my real world need which is using the macro as argument of \addtolenght, so thanks.
– mmj
Jun 25 '21 at 18:00