I have a situation where I want to define a command that takes a variable number of arguments, where the number of arguments is known programmatically via a \count
, and process the parameters in some way (say as if they are a list).
As an example, say I'd like to output the parameters as a comma-separated list.
\newcommand{\makecsv}[N]{#1, #2, ..., #N}
The code that I've come up with to do this kind of operation (in a generic-ish way) essentially takes a command, \csv
and expands it recursively N times. \csv
needs to know how to continue the recursion, and has some state that I'd like to thread through the recursion (rather than using \global
).
\documentclass{report}
\usepackage{etoolbox}
\makeatletter
\newcommand{\ifzero}[3]{%
% #1: count
% #2: state for #3
% #3: macro to expand to
% - should take at least 2 parameters
% - ##1: count threaded through
% - ##2: macro state threaded through
\ifnum\c > 0
\def\tmp@f##1##2##3{##1{##2}{##3}}%
\advance#1 -1%
\else
\def\tmp@f##1##2##3{)}% note closeparen here (could be param)
\fi
\tmp@f{#3}{#1}{#2}%
}
\makeatother
\newcommand{\csv}[3]{
% #1: count
% #2: separator state
% #3: string to concat
%
#2#3\ifzero{#1}{, }{\csv}%
}
\newcommand{\makecsv}[1]{%
\ifzero{#1}{}{\csv}%
}
\makeatletter
\newcommand{\decl}[3]{%
% #1: decl id
% #2: decl symbol
% #3: # params
\csgdef{decl@#1}{#2}%
\global\expandafter\newcount\csname decl@#1@nparams\endcsname%
\csuse{decl@#1@nparams} #3\relax%
}
\newcommand{\usedecl}[1]{%
\newcount\c
\c \the\csuse{decl@#1@nparams}
\csuse{decl@#1}(\makecsv{\c}%
}
\makeatother
% declare some interface routines
\decl{foo}{FOO}{3}
\decl{bar}{BAR}{4}
\begin{document}
\usedecl{foo}{p1}{p2}{p3}\par
\usedecl{bar}{p1}{p2}{p3}{p4}\par
\end{document}
Is this a reasonable thing to do in 2e, or is there some sort of standard approach to this that is normally used?
Edit 1
It seems like my original MWE wasn't adequate to describe why someone might want this. I've updated the MWE with a use case. \decl
allows authors to declaratively define a C-style function, and \usedecl
allows the author to generate a use of the function, with its parameters bound to specific arguments.
This is similar enough to what I'm doing that it should help motivate the example.
Best Answer
As commented, here a solution that uses
\@ifnextchar
. I also implemented checks against too many or too few arguments (or why are they provided by the user?).The
\@ifnextchar
(or its “very internal” big brother\kernel@ifnextchar
) skips spaces which results in removed spaces in the third and fourth example.Code
Output