\begin{advertisement}[package=expkv-cs, author=myself]
General
expkv-cs provides a very straight forward way to define such macros. Notice however that the arguments it defines are optional keys and if you want them to be mandatory you have to implement the required checks yourself.
This is fully expandable and works in all TeX engines with e-TeX and the \expanded primitive (so anything running contemporary LaTeX).
expkv-cs is part of the expkv-bundle. This answer won't be exhaustive, if you want to know all the features (and also the other packages of the bundle) pay the documentation a visit.
Brief explanation
The basic syntax to define a macro in expkv-cs is
\ekvcSplit⟨cs⟩{⟨key=value declarations⟩}{⟨definition⟩}
therein
⟨cs⟩ is the name of an undefined control sequence that you want to define
⟨key=value declarations⟩ is a list of ⟨key⟩ names and their default ⟨value⟩s (which get used if you omit those keys on use time). Each ⟨key⟩ can be prefixed by either long or short specifying whether it should be able to get an explicit \par (long) or not (short -- which is the default behaviour, but without this you couldn't define a short key named long). That prefix won't be part of the key name.
⟨definition⟩ is the replacement text of the defined macro (so to what it expands). Inside it you can refer to the individual ⟨value⟩ of each ⟨key⟩ using the numeric arguments #1 to #9 (the number being the position in the ⟨key=value declarations⟩ -- this limits the number of available keys to 9).
There is an alternative front end as well:
\ekvcHash⟨cs⟩{⟨key=value declarations⟩}{⟨definition⟩}
It works identical but instead of using #1 to #9 you use \ekvcValue{⟨key⟩}{#1} to get the values of the individual keys (this has some technical implications, but those are better to be ignored for beginners -- see the documentation if you're curious, might require advanced TeX-programming-knowledge).
So in order to define a simple macro with two keys, both having default values you'd use
\ekvcSplit\mycmd
{
arg1 = default1
,arg2={default2}
}
{1: #1, 2: #2.}
And if you want to define the very same macro, but this time both arguments might contain \par tokens:
\ekvcSplit\mycmd
{
long arg1 = default1
,long arg2={default2}
}
{1: #1, 2: #2.}
Both these definitions could then be used in the document as any of the following (results as comments)
\mycmd{arg1 = {value1}, arg2=value2} % -> 1: value1, 2: value2.
\mycmd{} % -> 1: default1, 2: default2.
\mycmd{arg2={{2}}, } % -> 1: default1, 2: {2}.
\mycmd{arg2= V , arg1=} % -> 1: , 2: V.
Turning the keys mandatory
As you can see in the example above, the arguments are now optional, omitting one leads to a default value being used. If you define a command with \newcommand\mycmd[7]{...} it will have 7 ordered mandatory arguments. If we want to mimic this behaviour, just with named unordered arguments, we'll have to set specific default values.
LaTeX has two handy builtins for this. The first one is the test \IfNoValueT, it'll test whether its argument is a special -NoValue- marker. The second is the command storing that marker, \c_novalue_tl. Don't be afraid of the strange name with underscores, it's part of the LaTeX3 programming layer. To access that marker we can use another feature of expkv and its key=value-parser, which is expansion control: Using a prefix v: in front of everything in a key=value list means "build a control sequence from the value's contents and retrieve that control sequences value".
So v: long arg1 = c_novalue_tl in our ⟨key=value declarations⟩ will define the key arg1 to have the -NoValue- marker as its default value. We can then check for that marker and throw an error if we find it. \ekverr allows us to throw an error while still being fully expandable. Our example macro becomes
\ekvcSplit\mycmd
{
v: long arg1 = c_novalue_tl
,v: long arg2=c_novalue_tl
}
{%
\IfNoValueT{#1}{\ekverr{user1724887}{missing key arg1}}%
\IfNoValueT{#2}{\ekverr{user1724887}{missing key arg2}}%
1: #1, 2: #2.%
}
With that new definition, if we use the same input as above we'll get different behaviour (again using comments)
\mycmd{arg1 = {value1}, arg2=value2} % -> 1: value1, 2: value2.
\mycmd{} % 2 ERRORS -> 1: -NoValue-, 2: -NoValue-.
\mycmd{arg2={{2}}, } % 1 ERROR -> 1: -NoValue-, 2: {2}.
\mycmd{arg2= V , arg1=} % -> 1: , 2: V.
MWE defining macros with 7 keys
This MWE uses the above approaches to define two macros, both taking 7 named arguments. One of which having all 7 as mandatory arguments (with a small helper macro).
\documentclass[]{article}
\usepackage{expkv-cs}
\ekvcSplit\myfunction
{
long arg1 = {} % #1
,long arg2 = {} % #2
,long arg3 = {} % #3
,long arg4 = {} % #4
,long arg5 = {} % #5
,long arg6 = {} % #6
,long arg7 = {} % #7
}
{%
\par
\noindent
arg1: #1 \\
arg2: #2 \\
arg3: #3 \\
arg4: #4 \\
arg5: #5 \\
arg6: #6 \\
arg7: #7%
}
\ekvcSplit\myfunctionMandatory
{
v: long arg1 = c_novalue_tl % #1
,v: long arg2 = c_novalue_tl % #2
,v: long arg3 = c_novalue_tl % #3
,v: long arg4 = c_novalue_tl % #4
,v: long arg5 = c_novalue_tl % #5
,v: long arg6 = c_novalue_tl % #6
,v: long arg7 = c_novalue_tl % #7
}
{%
\assertMandatoryArg{arg1}{#1}%
\assertMandatoryArg{arg2}{#2}%
\assertMandatoryArg{arg3}{#3}%
\assertMandatoryArg{arg4}{#4}%
\assertMandatoryArg{arg5}{#5}%
\assertMandatoryArg{arg6}{#6}%
\assertMandatoryArg{arg7}{#7}%
\par
\noindent
arg1: #1 \\
arg2: #2 \\
arg3: #3 \\
arg4: #4 \\
arg5: #5 \\
arg6: #6 \\
arg7: #7%
}
\newcommand\assertMandatoryArg[2]
{\IfNoValueT{#2}{\ekverr{user1724887}{missing #1}}}
\begin{document}
\myfunction {
arg1 = {value1},
arg2 = {value2},
arg3 = {value3},
arg4 = {value4},
arg5 = {value5},
arg6 = {value6},
arg7 = {value7}
}
\myfunctionMandatory {
arg1 = {value1},
arg2 = {value2},
arg3 = {value3},
arg4 = {value4},
arg5 = {value5},
arg6 = {value6},
arg7 = {value7}
}
\end{document}

Example macros using \ekvcHash
Keep in mind that if you need more than 9 arguments you can't use \ekvcSplit and have to use \ekvcHash instead. In that case you can't use \IfNoValueT{\ekvcValue{arg1}{#1}} because the test won't understand the \ekvcValue-nesting. The following defines two small example macros using the hashing variant and makes some adjustments for the auxiliary \assertMandatoryArg.
\documentclass[]{article}
\usepackage{expkv-cs}
\ekvcHash\myfunction
{
long arg1 = {}
,long arg2 = {}
}
{1: \ekvcValue{arg1}{#1}, 2: \ekvcValue{arg2}{#1}.}
\ekvcHash\myfunctionMandatory
{
v: long arg1 = c_novalue_tl
,v: long arg2 = c_novalue_tl
}
{%
\assertMandatoryArg{arg1}{#1}%
\assertMandatoryArg{arg2}{#1}%
1: \ekvcValue{arg1}{#1}, 2: \ekvcValue{arg2}{#1}.%
}
\newcommand\assertMandatoryArg[2]
{\ekvcValueSplit{#1}{#2}\IfNoValueT{\ekverr{user1724887}{missing #1}}}
\begin{document}
\myfunction {
arg1 = {value1},
arg2 = {value2},
}
\myfunctionMandatory {
arg1 = {value1},
arg2 = {value2},
}
\end{document}

\end{advertisement}