# Variable arguments with \@ifnextchar

For consistent style of references, defined a Latex macro \secn that is able to handle multiple arguments. If I provide a single argument s1, it should return Section~\ref{sec:lbl}. If I provire multiple arguments s1, ..., sn, it should return Sections~\ref{sec:s1}, ..., \ref{sec:s(n-1)} and \ref{sec:sn}.

The following Latex macro almost accomplishes this, except that it does not generate the plural Sections in case of multiple arguments.

\documentclass{article}

\def\secn{\myref{Section}{Sections}{sec}}

\makeatletter
\newcommand{\myref}[4]{\@ifnextchar\bgroup{#2}{#1}~\myreflist{#3}{#4}}
\newcommand{\myreflist}[2]{%
\@ifnextchar\bgroup{\ref{#1:#2}\myreflisttail{#1}}{\ref{#1:#2}}}
\newcommand{\myreflisttail}[2]{\@ifnextchar\bgroup{,
\ref{#1:#2}\myreflisttail{#1}}{ and \ref{#1:#2}}}
\makeatother

\begin{document}

\section{test}
\secn{s1} \par
\secn{s1}{s2} \par
\secn{s1}{s2}{s3}

\section{test1}
\label{sec:s1}
\section{test2}
\label{sec:s2}
\section{test3}
\label{sec:s3}

\end{document}


The \@ifnextchar command in the definition of \myref always returns false. Why is this the case? How can I get the plural form?

• The cleveref package with the \cref command produces such output by default. – Andrew Swann Dec 28 '17 at 15:19
• Macros with “variable number of arguments” are not the best way to go here. – egreg Dec 28 '17 at 15:26
• A somewhat off-topic question: Am I the only one seeing the title incorrectly rendered? I see "Variable arguments with ∘ if ≠ xtchar". – md2perpe Jan 12 '18 at 18:59

Instead of using a command with variable arguments I would suggest to use a command with a comma-separated list as argument. You can also use package cleveref, that already provides such a command:

\documentclass{article}
\usepackage[sort]{cleveref}

\begin{document}

\section{test}
\cref{sec:s1} \par
\cref{sec:s1,sec:s2} \par
\cref{sec:s1,sec:s2,sec:s3}

\section{test1}
\label{sec:s1}
\section{test2}
\label{sec:s2}
\section{test3}
\label{sec:s3}

\end{document}


Without option sort the default sort&compress is used, that would result in:

The problem you get always the else clause is because you wrote \newcommand{\myref}[4]{\@ifnextchar\bgroup{#2}{#1}~\myreflist{#3}{#4}}, i.e. \@ifnextchar\bgroup{#2}{#1} always sees the ~ as next character. The if-next-char means the next char after it, not the macro it is part of!

You need to make sure that there is nothing after it, so I would suggest to move the remaining code into the clauses:

\newcommand{\myref}[4]{\@ifnextchar\bgroup{#2~\myreflist{#3}{#4}}{#1~\myreflist{#3}{#4}}}


I'd avoid “variable number of arguments”: a comma separated list is even simpler to type and to manage.

Here's an implementation with xparse and expl3:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\generalref}{mmmm}
{
\kasper_generalref:nnnn { #1 } { #2 } { #3 } { #4 }
}
\NewDocumentCommand{\secn}{m}
{
\generalref{Section}{Sections}{sec}{#1}
}

\seq_new:N \l_kasper_generalref_seq

\cs_new_protected:Nn \kasper_generalref:nnnn
{
% clear the sequence
\seq_clear:N \l_kasper_generalref_seq
% populate it
\clist_map_inline:nn { #4 }
{
\seq_put_right:Nn \l_kasper_generalref_seq { \ref{#3:##1} }
}
% use first argument if less than two items
\int_compare:nTF { \seq_count:N \l_kasper_generalref_seq < 2 } { #1 } { #2 }
% no break between label and first reference
\nobreakspace
% issue the references (here ~ means a normal space)
\seq_use:Nnnn \l_kasper_generalref_seq
{ ~and\nobreakspace } % between two
{ ,~ } % between more than two
{ ~and\nobreakspace } % between last two
}

\ExplSyntaxOff

\begin{document}

\section{test}
\secn{s1} \par
\secn{s1,s2} \par
\secn{s1,s2,s3}

\section{test1}
\label{sec:s1}
\section{test2}
\label{sec:s2}
\section{test3}
\label{sec:s3}

\end{document}


Using cleveref is simpler, though (and more customizable}. The output is the same.

\documentclass{article}
\usepackage{xparse}
\usepackage[nosort]{cleveref}

\ExplSyntaxOn

\NewDocumentCommand{\gcref}{mm}
{% #1 is the common prefix, #2 is the list of labels
\kasper_gcref:nn { #1 } { #2 }
}
\NewDocumentCommand{\secn}{m}
{
\gcref{sec}{#1}
}

\seq_new:N \l_kasper_gcref_seq

\cs_new_protected:Nn \kasper_gcref:nn
{
% clear the sequence
\seq_clear:N \l_kasper_gcref_seq
% populate it
\clist_map_inline:nn { #2 }
{
\seq_put_right:Nn \l_kasper_gcref_seq { #1:##1 }
}
\Cref {\seq_use:Nn \l_kasper_gcref_seq { , } }
}

\ExplSyntaxOff

\begin{document}

\section{test}
\secn{s1} \par
\secn{s1,s2} \par
\secn{s1,s2,s3}

\section{test1}
\label{sec:s1}
\section{test2}
\label{sec:s2}
\section{test3}
\label{sec:s3}

\end{document}