When trying to maintain large tables with \cmidrules, I find them tedious to use, so I thought I'd define my own shortcut. Instead of writing \cmidrule{1-3} \cmidrule{4-5} I would simply write \cmidrulez{3,2} (because the first rule is 3 columns wide and the second one 2.
In order to support an arbitrary amount of comma-seperated arguments, I tried to adapt this answer:
\newcounter{mainargs}
\pgfkeys{mainargs/.is family, mainargs,
step counter/.code=\stepcounter{mainargs},
add argument/.style={step counter, arg\themainargs/.initial={#1}},
}
\newcounter{optargs}
\newif\ifoptargs
\pgfkeys{optargs/.is family, optargs,
opt args present/.is if=optargs,
step counter/.code=\stepcounter{optargs},
add argument/.style={opt args present=true, step counter, arg\theoptargs/.initial={#1}},
}
\newcommand{\cmidrulez}[2][]{%
\setcounter{mainargs}{0}%
\pgfkeys{mainargs, add argument/.list={#2}}%
\setcounter{optargs}{0}%
\pgfkeys{optargs, add argument/.list={#1}}%
%
\newcounter{cmrstart}%
\newcounter{cmrend}%
\setcounter{cmrstart}{1}%
\ifoptargs%
\foreach \n in {1,...,\theoptargs}{%
\setcounter{cmrstart}{\pgfkeysvalueof{/optargs/arg\n}}%
}%
\fi%
\foreach \n in {1,...,\themainargs}{%
\setcounter{cmrend}{\value{cmrstart}}%
\addtocounter{cmrend}{\pgfkeysvalueof{/mainargs/arg\n}}%
\addtocounter{cmrend}{-1}%
\cmidrule{\arabic{cmrstart}-\arabic{cmrend}}%
\setcounter{cmrstart}{\value{cmrend}}%
\stepcounter{cmrstart}%
}%
%
}
Essentially what I'm doing is using two counters to calculate the corresponding numbers I need to give to the \cmidrule command. When I print the numbers with \arabic{cmrstart} and \arabic{cmrend} instead of calling \cmidrule the correct values appear, so this part is working.
However, when I try to use this in a table (where a \cmidrule would work) it complains about a "misplaced \noalign":
\cmidrule ->\noalign
{\ifnum 0=`}\fi \@ifnextchar [{\@cmidrule }{\@cmidrule ...
l.66 \cmidrulez{2,1}
\\
I expect to see \noalign only after the \cr of
an alignment. Proceed, and I'll ignore this case.
! Missing } inserted.
<inserted text>
}
l.66 \cmidrulez{2,1}
\
I first thought this might have something to do with how I hand over the values, so I tried \value{cmrstart} and thecmrstart, the other two ways of which I know to get a value from a counter, but that didn't work. In fact, if I just replace the arguments of \cmidrule with hardcoded integers (inside my command definition), the problem persists, so it can't (only) be that.
When I define a much simpler command that just does something like \cmidrule{1-3} it works without problems, but as soon as there are arguments it fails:
% this works:
\newcommand{\cmidrules}{%
\cmidrule{1-3}%
}
% this doesn't:
\newcommand{\cmidrules}[1][]{%
\cmidrule{1-3}%
}
What am I doing wrong?



\cmidruleshould not be preceded by anything else than other\cmidrulecommands. With your\cmidrulezcommand you're adding something that makes TeX start a row of thetabular. – egreg Jun 17 '20 at 15:12\cmidruleinvokations with a single auto-incrementing call to my command. – L3viathan Jun 17 '20 at 16:04\newcounter{cmrstart}%inside the macro just allocate the counter once in the package or document preamble. You do not want to allocate a new counter every time the macro is used. They are a finite resource. – David Carlisle Jun 17 '20 at 18:09