Commands that may take a variable number of arguments
If you're willing to peek ahead, you can check whether there's "another argument" and keep gobbling them on the fly:
\documentclass{article}
\usepackage{etoolbox}% http://ctan.org/pkg/etoolbox
\makeatletter
\newcommand{\newdecl}[2]{\csgdef{decl@#1}{#2}}% Creates a declaration
\newcommand{\csvdel}{}% Delimiter used in CSV representation
\newcommand{\newusedecl}[2][,]{% Use a declaration
\renewcommand{\csvdel}{\renewcommand{\csvdel}{#1\,}}% Delay \csvdel one cycle.
\csname decl@#2\endcsname(\checknextarg}
\newcommand{\checknextarg}{\@ifnextchar\bgroup{\gobblenext}{}}% Check if another "argument" exists
\newcommand{\gobblenext}[1]{\csvdel#1\@ifnextchar\bgroup{\gobblenext}{)}}% Gobble next "argument"
\makeatother
% declare some interface routines
\newdecl{foo}{FOO}
\newdecl{bar}{BAR}
\begin{document}
\newusedecl{foo}{p1}{p2}{p3}\par
\newusedecl{bar}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}
{p1}{p2}{p3}{p4}\par
\newusedecl[;]{foo}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}
{p1}{p2}{p3}{p4}
\end{document}
The peeking is done using \@ifnextchar
. For some explanation around this, see Understanding \@ifnextchar
. The delayed use of \csvdel
(the CSV delimiter) stems from Cunning (La)TeX tricks).
The optional argument to \newusedecl
adapts \csvdel
.
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
\documentclass{report}
\usepackage{etoolbox}
\makeatletter
\newcommand*{\decl}[3]{%
% #1: decl id
% #2: decl symbol
% #3: # params
\csdef{decl@symbol@#1}{#2}%
\expandafter\newcount\csname c@decl@params@#1\endcsname
\csuse{c@decl@params@#1}=#3\relax
}
\newcount\decl@params@check
\newcommand*{\usedecl}[1]{%
\def\decl@name{#1}%
\edef\decl@params{\the\csuse{c@decl@params@#1}}%
\def\decl@symbol{\csuse{decl@symbol@#1}}%
\decl@params@check=\z@
\let\decl@list\@gobble % the \@gobble removes the first , (expandable)
\def\decl@next{\kernel@ifnextchar\bgroup\use@decl\use@decl@finish}%
\decl@next
}
\newcommand*{\use@decl}[1]{%
\advance\decl@params@check\@ne
\expandafter\ifnum\the\decl@params@check>\decl@params\relax % too many!
\PackageWarning{decl}{You have used more params than the \decl@name\space function expected!
I ignore this (and any following) param, ok?}% but insert the extra argument anyway?!
\def\decl@next{\use@decl@finish{#1}}% the extra pair of braces {} keeps '#1' local as it is in the input stream
\else
\expandafter\def\expandafter\decl@list\expandafter{\decl@list\decl@list@sep#1}%
\fi
\decl@next
}
\newif\ifuse@decl@message
\newcommand*{\use@decl@finish}{%
\ifnum\decl@params@check<\decl@params\relax % too few!
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{%
\ifuse@decl@message\else
\PackageWarning{decl}{You have used fewer params than the \decl@name\space function expected! I'm filling up with '??'!}%
\use@decl@messagetrue
\fi
\use@decl{??}}
{%
\decl@symbol\decl@list@start\decl@list\decl@list@end
\use@decl@messagefalse
}%
}
\newcommand*{\setdeclstart}[1]{\def\decl@list@start{#1}}
\newcommand*{\setdeclend}[1]{\def\decl@list@end{#1}}
\newcommand*{\setdeclsep}[1]{\def\decl@list@sep{#1}}
\makeatother
\setdeclstart{(}
\setdeclend{)}
\setdeclsep{, }
% declare some interface routines
\decl{foo}{FOO}{3}
\decl{bar}{BAR}{4}
\begin{document}
given $P$, $Q$ and $R$ such \dots\ that $\usedecl{foo}{P}{Q}{R}$ results in \dots\par
given P, Q and R such \dots\ that \usedecl{foo}{P}{Q}{R}\ results in \dots\par
\usedecl{foo}{p1}{p2}{p3}\par
\usedecl{bar}{p1}{p2}{p3}{p4}\par
\usedecl{bar}{p1} foo\par
\usedecl{foo}{p1}{p2}{p3} {p4}\par
\end{document}
Output
You've not given us much to go off of, but here's something that seems to implement what you want while being fed a comma separated list (in lieu of passing a variable number of arguments).
\documentclass{article}
\usepackage{xparse}
\newcounter{myargcounter}
\ExplSyntaxOn
\clist_new:N \l_myvararg_parameters_clist
\tl_new:N \l_myvararg_current_item_tl
\NewDocumentCommand{\makecsv}{ m }
{
\clist_set:Nn \l_myvararg_parameters_clist { #1 }
\int_while_do:nNnn { \clist_count:N \l_myvararg_parameters_clist } > { 1 }
{
\clist_pop:NN \l_myvararg_parameters_clist \l_myvararg_current_item_tl
\tl_use:N \l_myvararg_current_item_tl,
}
\clist_pop:NN \l_myvararg_parameters_clist
\l_myvararg_current_item_tl
{} ~ and ~ \tl_use:N \l_myvararg_current_item_tl
}
\ExplSyntaxOff
\pagestyle{empty}
\begin{document}
\makecsv{a,b,c,d}
\makecsv{a,b,c,d,e,f,g}
\makecsv{a,b}
\end{document}