4

When declaring a new command with multiple optional arguments next to each other, e.g.

\NewDocumentCommand{\mycmd}{ o o o }{}

one is implicitly excluding the option of passing a value for #2 without having one for #1, i.e. \mycmd[][2] will fail \IfNoValueT{#1}. This can be remediated by using \tl_if_blank:nTF, see \IfNoValueTF - How to force "no value" if there (are brackets but there) is no value for an argument. However, sometimes this is not an option (in my particular case, the xparse support of tcolorbox is limited to checking -NoValue-).

This, among other things, made me want to reprogram some commands (or, in my case, tcolorboxes) into a key-value system. My approach was to give the original command \mycmd extra mandatory arguments that would remain empty, as to ensure that any combination of empty or nonempty optional arguments is possible. E.g. \mycmd from above would be set with

\NewDocumentCommand{\mycmd}{ o m o m o }{}

With this version, I can pass a value to any of my three "real" arguments (the optional ones) without needing any of the others, while also being able to call \IfValueTF on any of them.

Next, I define keys for each of the optional arguments, e.g.

\keys_define:nn { mykeys } {
    a .tl_set:N = \l_mykeys_a_tl,
    b .tl_set:N = \l_mykeys_b_tl,
    c .tl_set:N = \l_mykeys_c_tl
}

Lastly, I define a wrapper command

\NewDocumentCommand{\wrapper}{ O{} }{
    \keys_set:nn{mykeys}{#1}
    \mycmd
    \tl_if_empty:NTF \l_mykeys_a_tl {} { [\l_mykeys_a_tl] }
    {}
    \tl_if_empty:NTF \l_mykeys_b_tl {} { [\l_mykeys_b_tl] }
    {}
    \tl_if_empty:NTF \l_mykeys_c_tl {} { [\l_mykeys_c_tl] }
}

with the intention that e.g. \wrapper[a=13, c=2] would be equivalent to \mycmd[13]{}{}[2].

This last point is where my approach fails completely. I tried to play around with \exp_not:n or \use:n, explained in The LaTeX3 Interfaces, but to no avail. Hence, any help or guidance would be much appreciated. Thank you!


Full MWE:

\documentclass{article}

\usepackage{xparse}

\NewDocumentCommand{\mycmd}{ o m o m o }{
    First ``real'' argument is \IfValueTF{#1}{#1}{not provided}.\par
    Second ``real'' argument is \IfValueTF{#3}{#3}{not provided}.\par
    Third ``real'' argument is \IfValueTF{#5}{#5}{not provided}.\par
}
\ExplSyntaxOn
\keys_define:nn { mykeys } {
    a .tl_set:N = \l_mykeys_a_tl,
    b .tl_set:N = \l_mykeys_b_tl,
    c .tl_set:N = \l_mykeys_c_tl
}
\NewDocumentCommand{\wrapper}{ O{} }{
    \keys_set:nn{mykeys}{#1}
    \mycmd
    \tl_if_empty:NTF \l_mykeys_a_tl {} { [\l_mykeys_a_tl] }
    {}
    \tl_if_empty:NTF \l_mykeys_b_tl {} { [\l_mykeys_b_tl] }
    {}
    \tl_if_empty:NTF \l_mykeys_c_tl {} { [\l_mykeys_c_tl] }
}
\ExplSyntaxOff

\begin{document}

\mycmd[13]{}{}[2]

\noindent\hrulefill

\wrapper[a=13, c=2]

\end{document}

Output of MWE

6
  • Then \mycmd would have a very weird usage requirements; do you really need it? Commented Mar 15, 2021 at 8:21
  • it would be far simpler (and conventional) to have the inner command just have three mandatory arguments. The optional arguments server no purpose if you are using a keyval interface at the top. Commented Mar 15, 2021 at 9:39
  • as written when called from \wrapper , \mycmd is never called with an optional argument just two mandatory arguments \mycmd{\tl_if_empty:NTF}{\l_mykeys_a_tl} Commented Mar 15, 2021 at 9:42
  • @DavidCarlisle The advantage of having the arguments be optional is that I can check if the value is empty without requiring \tl_if_blank:NTF, which isn't supported by tcolorbox. However, your second comment is really the crux of the question; how can I pass the (nonempty) keys to \mycmd? Commented Mar 15, 2021 at 12:16
  • @steve no really there is no advantage at all you are making things weirdly complicated and doubling the number of arguments used (so you are lucky to stay below 9) your inner command doesn't need to test for if_blank because your key definition can specify a default so you can always pass three real arguments to the inner call. That is the point of the xparse parsing (actually xparse package not needed with current latex, it is built in) that sorting out defaults is done at that level and then call a standard command which does not deal with user-interface issues like default values Commented Mar 15, 2021 at 12:37

1 Answer 1

2

Like David says in the comments, you should make all the arguments of your underlying command mandatory. The definition of \mykeys_base_command:nnnnn below can be anything, including tcolorbox environments or commands. Just check if the arguments are empty and initialize the keys to be empty.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn
\cs_new_protected:Nn \mykeys_base_command:nnnnn
  {
    First~``real''~argument~is~\tl_if_empty:oTF{#1}{not provided}{#1}.\par
    Second~``real''~argument~is~\tl_if_empty:oTF{#3}{not provided}{#3}.\par
    Third~``real''~argument~is~\tl_if_empty:oTF{#5}{not provided}{#5}.\par
  }
\keys_define:nn { mykeys } {
    a .tl_set:N = \l_mykeys_a_tl,
    a .initial:n = {},
    b .tl_set:N = \l_mykeys_b_tl,
    b .initial:n = {},
    c .tl_set:N = \l_mykeys_c_tl,
    c .initial:n = {},
}
\NewDocumentCommand{\wrapper}{ O{} }{
    \group_begin:
    \keys_set:nn { mykeys } { #1 }
    \mykeys_base_command:nnnnn { \l_mykeys_a_tl } { } { \l_mykeys_b_tl } { } { \l_mykeys_c_tl }
    \group_end:
}
\ExplSyntaxOff

\begin{document}

\wrapper

\bigskip

\wrapper[b=blub]

\bigskip

\wrapper[a=13, c=2]

\end{document}

wrapper with keys

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.