1

Say I have a function with a complex argument specification like this:

\DeclareDocumentCommand\backend{m moomm}{
    Type: #1, arguments: #2 #3 #4 #5 #6
}

And I would like to declare a set of wrappers inserting something for the first argument but leaving the remaining arguments untouched (passing them on to the \backend command), like this:

\DeclareDocumentCommand\typeA{moomm}{%
    \backend{A}{#1}[#2][#3]{#4}{#5}%
}

This seems to work, but is it really a 'save' way to go for all types of arguments? Or is this better:

\def\typeB{\backend{B}} 

Or are both equivalent? What is best practice?

Here an MWE to play around with:

\documentclass{article}
\usepackage{xparse}

\DeclareDocumentCommand\backend{m moomm}{
    Type: #1, arguments: #2 #3 #4 #5 #6
}

\DeclareDocumentCommand\typeA{moomm}{%
    \backend{A}{#1}[#2][#3]{#4}{#5}%
}

\def\typeB{\backend{B}}


\begin{document}

\typeA{Foo}[opt.]{m2}{m3}

\typeB{Bar}[opt.]{m2}{m3}

\end{document}
  • 1
    Best practice is not using \DeclareDocumentCommand, but \NewDocumentCommand. The “longer form” is preferable. – egreg May 4 '17 at 23:00
  • 2
    best practice would have the backend command defined with all mandatory arguments (perhaps with expl3) and then you define two different document commands wrapping that internal, not that you wrap a document level command. – David Carlisle May 4 '17 at 23:02
  • 1
    For so many arguments you might want to prefer a key-value interface. – Henri Menke May 7 '17 at 6:40
2

Something like this?

\NewDocumentCommand is preferable to \DeclareDocumentCommand unless you are certain you want to overwrite any existing command, which isn't usually the case.

The basic idea is that the xparse functions are simply wrappers which process user input, invoke the appropriate lower level functions and pass any argument values along appropriately. So, in this case, we need 3 lower level functions: one each for 4, 5 and 6 arguments. Alternatively, if you really want default values used when options are not specified, you only need a single lower-level function and the code is simpler.

Assuming the more complex case, each xparse macro simply decides how many arguments are specified and passes the relevant values on to the appropriate lower-level command.

You can use \cs_new_protected_nopar if you are sure you want m rather than +m and o rather than +o for your argument specifications.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new_protected:Nn \latechneuse_backend:nnnnnn
{
  Type: ~ #1, ~ arguments: ~ #2 ~ #3 ~ #4 ~ #5 ~ #6
}
\cs_new_protected:Nn \latechneuse_backend:nnnnn
{
  Type: ~ #1, ~ arguments: ~ #2 ~ #3 ~ #4 ~ #5
}
\cs_new_protected:Nn \latechneuse_backend:nnnn
{
  Type: ~ #1, ~ arguments: ~ #2 ~ #3 ~ #4
}
\NewDocumentCommand \backend { mmoomm }
{
  \group_begin:
  \IfValueTF { #3 }
  {
    \IfValueTF { #4 }
    {
      \latechneuse_backend:nnnnnn { #1 } { #2 } { #3 } { #4 } { #5 } { #6 }
    }{
      \latechneuse_backend:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 }
    }
  }{
    \latechneuse_backend:nnnn { #1 } { #2 } { #3 } { #4 }
  }
  \group_end:
}
\NewDocumentCommand \altbackend { m m O {default} O {default} m m }
{
  \group_begin:
    \latechneuse_backend:nnnnnn { #1 } { #2 } { #3 } { #4 } { #5 } { #6 }
  \group_end:
}
\NewDocumentCommand \typeA { m o o m m }
{
  \group_begin:
  \IfValueTF { #2 }
  {
    \IfValueTF { #3 }
    {
      \latechneuse_backend:nnnnnn { A } { #1 } { #2 } { #3 } { #4 } { #5 }
    }{
      \latechneuse_backend:nnnnn { A } { #1 } { #2 } { #4 } { #5 }
    }
  }{
    \latechneuse_backend:nnnn { A } { #1 } { #4 } { #5 }
  }
  \group_end:
}
\NewDocumentCommand \typeB { m o o m m }
{
  \group_begin:
  \IfValueTF { #2 }
  {
    \IfValueTF { #3 }
    {
      \latechneuse_backend:nnnnnn { B } { #1 } { #2 } { #3 } { #4 } { #5 }
    }{
      \latechneuse_backend:nnnnn { B } { #1 } { #2 } { #4 } { #5 }
    }
  }{
    \latechneuse_backend:nnnn { B } { #1 } { #4 } { #5 }
  }
  \group_end:
}
\NewDocumentCommand \alttypeA { m O {default} O {default} m m }
{
  \group_begin:
  \latechneuse_backend:nnnnnn { A } { #1 } { #2 } { #3 } { #4 } { #5 }
  \group_end:
}
\NewDocumentCommand \alttypeB { m O {default} O {default} m m }
{
  \group_begin:
  \latechneuse_backend:nnnnnn { B } { #1 } { #2 } { #3 } { #4 } { #5 }
  \group_end:
}
\ExplSyntaxOff


\begin{document}

\typeA{Foo}[opt.]{m2}{m3}

\typeB{Bar}[opt.]{m2}{m3}

\end{document}

It is hard to think the interface is really a good design choice, but it is impossible to say how it might be done differently without more information about the real use case. (Possibly a key-value interface but, as I say, impossible to really say with this little information.)

  • Thank you very much. I didn't expect my question would open such an abyss of complexity :). As I'm just a humble end user, let's assume I drop caring about "best practice". Could we at least say that one of the two options I gave in my OP is save to do (e.g. in terms of expansion of the arguments)? For instance, if argument #2 contains other macros (such as \addcontentsline etc), will it be delivered to the \backend function in the same way as it is being delivered to the outer function \typeA if I use my original definition of \typeA, or does anything 'happen' to the args in between? – LaTechneuse May 5 '17 at 9:02
  • @LaTechneuse with xparse you always know if the arguments are going to be expanded (x for example) or passed straight through (m for example) but really this is a lot simpler and safer than wrapping one document command by another. Note if you do not want to use expl3 that is fine you can define your inner command with \def or \newcommand – David Carlisle May 5 '17 at 10:07
  • @LaTechneuse I would use \newcommand rather than \def if possible for the inner command, as that's safer generally - just as using \NewDocumentCommand is safer than \DeclareDocumentCommand. Unless you know you want to overwrite anything which is there, but don't care whether it is there or not. Then \NewDocumentCommand can parse the arguments and pass the relevant values off to \newcommand (or \def if necessary). – cfr May 5 '17 at 22:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.