In your comment to my first answer you added an aspect which should have been mentioned in the initial question already: Breaking the nine-argument-limit:
[...] the problem is that some of my real world macros need 4-5 parameters for the prototype and 8 or more parameters for the final macro, so I need more than 9 parameters overall, [...]
I am not fond of macros taking many arguments: The more arguments, the more opportunities to erroneously put arguments in wrong order. This is not user-friendly.
Therefore I take this for an "academical" thing only—a request to see how such a thing can be done, just for the knowledge and the challenge, although one would not implement such a thing for daily usage.
As long as all "parameters" are mandatory undelimited arguments I propose two "building bricks":
Building brick 1:
Have a \romannumeral-expansion-driven "macro-mechanism"
\CollectLArgs{⟨TeX-⟨number⟩-quantity of value L⟩}{⟨Argument 1⟩}{⟨Argument 2⟩}...{⟨Argument L⟩}
which yields:
{{⟨Argument 1⟩}{⟨Argument 2⟩}...{⟨Argument L⟩}}
, i.e., which wraps a sequence of L undelimited arguments into a single argument which then contains/consists of a list of L undelimited arguments.
(With macro-mechanisms that are driven by \romannumeral-expansion you need exactly two expansion-steps/two "hits" by \expandafter for obtaining the result.)
Building brick 2:
Have a \romannumeral-expansion-driven "macro-mechanism"
\ExtractKthArg{⟨TeX-⟨number⟩-quantity of value K⟩}%
{⟨tokens in case list of undelimited arguments doesn't have a Kth argument⟩}%
{⟨list of undelimited arguments⟩}%
which works as follows:
In case there is no Kth argument in the ⟨list of undelimited arguments⟩:
Does deliver ⟨tokens in case list of undelimited arguments doesn't have a Kth argument⟩.
In case there is a Kth argument in the ⟨list of undelimited arguments⟩:
Does deliver that Kth argument with one level of braces removed.
(With macro-mechanisms that are driven by \romannumeral-expansion you need exactly two expansion-steps/two "hits" by \expandafter for obtaining the result.)
You can use the two "building bricks" for defining a generic macro-mechanism which processes an amount of L arguments, whereby L>9 is possible, the first ones of these L arguments being those whose defaults are to be changed for the non-generic macros/user-level-macros:
The generic macro-mechanism consists of a generic toplevel-macro which via \expandafter-trickery calls \CollectLArgs to deliver to a generic internal-macro, which processes exactly one argument, the L arguments turned into a single undelimited argument containing a list of L undelimited arguments.
This way the generic internal-macro's argument is a list of L undelimited arguments from which you can have extracted whichever you like/need by means of \ExtractKthArg.
In the example below \ExtractKthArg requires two expansion-steps/two "hits" by \expandafter to deliver the result.
Assume you want the 11th argument/element to be in typewriter-font.
You can do
\texttt{%
\ExtractKthArg{11}%
{}%
{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}{13}{14}{15}}%
}%
This way \texttt will start working and hereby also evaluate \ExtractKthArg..., which in turn delivers 11.
Alternatively you can do:
\expandafter\expandafter\expandafter\texttt
\expandafter\expandafter\expandafter{%
\ExtractKthArg{11}%
{}%
{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}{13}{14}{15}}%
}%
This way the \expandafter-chain triggers carrying out \ExtractKthArg before \texttt comes to start working and you get:
\texttt{11}%
With these two snippets the differences in the order in time of expansion/carrying things out doesn't matter—the result in the end is the same.
But often the order in time in which expansion takes place and tokens are obtained does matter.
Let's create an example where the two mentioned "building-bricks" are implemented and used for defining both a \PrototypeThing which processes 17 arguments, the first two arguments being defaults that are to be provided within the definition-text of the user-level-macros, the following 15 to be provided by the user when calling the user-level-macros—so that the user-level-macros take 15 arguments:
Syntax is:
\PrototypeThing{⟨environment-name⟩}%
{⟨caption-command⟩}%
{⟨fileNlabel⟩}%
{⟨caption-text⟩}%
{⟨width⟩}%
{⟨Dummy-Argument 1⟩}%
{⟨Dummy-Argument 2⟩}%
{⟨Dummy-Argument 3⟩}%
{⟨Dummy-Argument 4⟩}%
{⟨Dummy-Argument 5⟩}%
{⟨Dummy-Argument 6⟩}%
{⟨Dummy-Argument 7⟩}%
{⟨Dummy-Argument 8⟩}%
{⟨Dummy-Argument 9⟩}%
{⟨Dummy-Argument 10⟩}%
{⟨Dummy-Argument 11⟩}%
{⟨Dummy-Argument 12⟩}%
Based on \PrototypeThing you can easily define user-macros of definition-pattern
\newcommand⟨Cs-token⟩{%
\PrototypeThing{⟨environment-name⟩}%
{⟨caption-command⟩}%
}%
with syntax
⟨Cs-token⟩{⟨fileNlabel⟩}%
{⟨caption-text⟩}%
{⟨width⟩}%
{⟨Dummy-Argument 1⟩}%
{⟨Dummy-Argument 2⟩}%
{⟨Dummy-Argument 3⟩}%
{⟨Dummy-Argument 4⟩}%
{⟨Dummy-Argument 5⟩}%
{⟨Dummy-Argument 6⟩}%
{⟨Dummy-Argument 7⟩}%
{⟨Dummy-Argument 8⟩}%
{⟨Dummy-Argument 9⟩}%
{⟨Dummy-Argument 10⟩}%
{⟨Dummy-Argument 11⟩}%
{⟨Dummy-Argument 12⟩}%
Here we go:
\makeatletter
%%=============================================================================
%% PARAPHERNALIA:
%%\UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond, \UD@Exchange,
%%\UD@stopromannumeral, \UD@CheckWhetherNull
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%=============================================================================
%% BUILDING-BRICK 1: \CollectLArgs
%%=============================================================================
%% \CollectLArgs{<TeX-<number>-quantity of value L>}%
%% {<Argument 1>}{<Argument 2>}...{<Argument L>}%
%%
%% yields:
%%
%% {{<Argument 1>}{<Argument 2>}...{<Argument L>}}
%%
%% Due to \romannumeral-expansion the result is delivered after two
%% expansion-steps / by "hitting" \CollectLArgs with \expandafter twice.
%%
%%.............................................................................
\newcommand\CollectLArgs[1]{%
\romannumeral
\expandafter\UD@CollectLArgs\expandafter{\romannumeral\number\number#1 000}{}%
}%
\newcommand\UD@CollectLArgs[2]{%
\UD@CheckWhetherNull{#1}{\UD@stopromannumeral{#2}}{\UD@@CollectLArgs{#1}{#2}}%
}%
\newcommand\UD@@CollectLArgs[3]{%
\expandafter\UD@CollectLArgs\expandafter{\UD@firstoftwo{}#1}{#2{#3}}%
}%
%%=============================================================================
%% BUILDING-BRICK 2: \ExtractKthArg
%%=============================================================================
%% \ExtractKthArg{<TeX-<number>-quantity of value K>}%
%% {<tokens in case list of undelimited args doesn't have a k-th argumnent>}%
%% {<list of undelimited args>} %
%%
%% In case there is no K-th argument in <list of undelimited args> :
%% Does deliver <tokens in case list of undelimited args doesn't have a k-th argumnent.
%% In case there is a K-th argument in <list of undelimited args> :
%% Does deliver that K-th argument with one level of braces removed.
%%
%% Due to \romannumeral-expansion the result is delivered after two
%% expansion-steps / by "hitting" \ExtractKthArg with \expandafter twice.
%%
%% Examples:
%%
%% \ExtractKthArg{0}{not available}{ABCDE} yields: not available
%%
%% \ExtractKthArg{3}{not available}{ABCDE} yields: C
%%
%% \ExtractKthArg{3}{not available}{AB{CD}E} yields: CD
%%
%% \ExtractKthArg{4}{not available}{{001}{002}{003}{004}{005}} yields: 004
%%
%% \ExtractKthArg{6}{not available}{{001}{002}{003}} yields: not available
%%=============================================================================
\newcommand\ExtractKthArg[2]{%
\romannumeral
% #1: <integer number K>
% #2: <action if there is no K-th argument>
\expandafter\UD@ExtractKthArgCheck
\expandafter{\romannumeral\number\number#1 000}{#2}%
}%
\newcommand\UD@ExtractKthArgCheck[3]{%
\UD@CheckWhetherNull{#1}{\UD@stopromannumeral#2}{% empty
\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}%
}%
}%
\begingroup
\def\UD@ExtractFirstArgLoop#1{%
\endgroup
\@ifdefinable\UD@RemoveTillFrozenrelax{%
\long\def\UD@RemoveTillFrozenrelax##1##2#1{{##1}}%
}%
\newcommand\UD@ExtractKthArgLoop[3]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo##3{}.}{\UD@stopromannumeral##2}{%
\UD@CheckWhetherNull{##1}{%
\UD@ExtractFirstArgLoop{##3#1}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}##3}%
{\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}##1}{##2}}%
}%
}%
}%
}%
\expandafter\expandafter\expandafter\UD@ExtractFirstArgLoop
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\ifnum0=0\fi}%
%% Usage of frozen-\relax as delimiter is for speeding things up by reducing the
%% amount of iterations needed. I chose frozen-\relax because David Carlisle
%% pointed out in <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \UD@ExtractFirstArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\expandafter\UD@stopromannumeral\UD@firstoftwo#1{}}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}%
}%
%%=============================================================================
\makeatother
\documentclass{article}
\usepackage{graphicx}
\usepackage{capt-of}
\makeatletter
\newcommand{\PrototypeThing}{%
\expandafter\expandafter\expandafter\InternalPrototypeThing\CollectLArgs{17}%
}%
\newcommand{\InternalPrototypeThing}[1]{%
\begin{\ExtractKthArg{1}{}{#1}}%
\par\noindent\hrulefill
\par\noindent User macro's 1st argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{3}{}{#1}}%
\par\noindent User macro's 2nd argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{4}{}{#1}}%
\par\noindent User macro's 3rd argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{5}{}{#1}}%
\par\noindent User macro's 4th argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{6}{}{#1}}%
\par\noindent User macro's 5th argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{7}{}{#1}}%
\par\noindent User macro's 6th argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{8}{}{#1}}%
\par\noindent User macro's 7th argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{9}{}{#1}}%
\par\noindent User macro's 8th argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{10}{}{#1}}%
\par\noindent User macro's 9th argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{11}{}{#1}}%
\par\noindent User macro's 10th argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{12}{}{#1}}%
\par\noindent User macro's 11th argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{13}{}{#1}}%
\par\noindent User macro's 12th argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{14}{}{#1}}%
\par\noindent User macro's 13th argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{15}{}{#1}}%
\par\noindent User macro's 14th argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{16}{}{#1}}%
\par\noindent User macro's 15th argument: \detokenize\expandafter\expandafter\expandafter{\ExtractKthArg{17}{}{#1}}%
\par\noindent\hrulefill
\bigskip
\par
\centering
\expandafter\expandafter\expandafter\UD@CheckWhetherNull
\expandafter\expandafter\expandafter{\ExtractKthArg{5}{}{#1}}{%
\includegraphics
}{%
\includegraphics[{width=\ExtractKthArg{5}{}{#1}\textwidth}]%
}{\ExtractKthArg{3}{}{#1}}%
\ExtractKthArg{2}{}{#1}{\ExtractKthArg{4}{}{#1}}%
\label{fig:\ExtractKthArg{3}{}{#1}}%
\par\noindent\hrulefill
\end{\ExtractKthArg{1}{}{#1}}%
}%
\makeatother
\newcommand{\figureone}{\PrototypeThing{figure}{\caption}}
\newcommand*{\centerone}{\PrototypeThing{center}{\captionof{figure}}}
\begin{document}
\listoffigures
\newpage
\null
\figureone{example-image-a.jpg}{caption}{.3}{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}{K}{L}
\newpage
\centerone{example-image-b.jpg}{caption}{1}{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}{K}{L}
\end{document}
