6

Consider the following code

\documentclass{article}
\usepackage{xstring,etoolbox}
\usepackage[shortlabels]{enumitem}

\def\lists{%
    parts-a),
    items-1)
}
\renewcommand*{\do}[1]{%
    \StrCut{#1}{-}\csA\csB
    % the next line is the "crucial one"
    \expandafter\providecommand\csname my\csA\endcsname[1][\csB]{%
        \begin{enumerate}[##1]
            \item one
            \item two
        \end{enumerate}
    }
}
\expandafter\docsvlist\expandafter{\lists}

\begin{document}
\myparts[i)]
\myparts
\end{document}

It produces

i) one
ii) two
1) one
1) two

How should I modify the "crucial line" so that it works as expected, i.e. produces

i) one
ii) two
a) one
b) two

?

As far as I understand I need to pass \csB "expanded". But I played with various edef's and expandafter's for a few hours, I am on the verge of madness, and I ran out of ideas.

(The above is a distilled minimal example - in reality it's a more useful code, but I think the above captures my problem well)

EDIT: siracusa's answer below seems to be working, unfortunately I don't know how to do a notch more difficult example with the following modification

\begin{enumerate}[##1]
    \item one
    \item two
\end{enumerate}
The above list is of type \csB

As above it will print "The above list is of type 1)" all the time, and it's not clear to me how to modify siracusa's answer to correctly expand \csB which is in the definition of \myparts...

4

The following seems to be what you want:

\renewcommand*{\do}[1]{%
    \StrCut{#1}{-}\csA\csB
    % the next line is the "crucial one"
    \edef\temp{\noexpand\providecommand\expandafter\noexpand\csname my\csA\endcsname[1][\csB]}%
    \temp{%
        \begin{enumerate}[##1]
            \item one
            \item two
        \end{enumerate}
    }
}

We first define a temporary command \temp that fully expands the \providecommand parameters, except for the \providecommand macro itself which is prevented by \noexpand. Then \temp is executed to actually perform the command definition.


Here's a new version wrt. the extended question:

\renewcommand*{\do}[1]{%
    \StrCut{#1}{-}\csA\csB
    % the next line is the "crucial one"
    \edef\temp{\noexpand\providecommand\expandafter\noexpand\csname my\csA\endcsname[1][\csB]{%
        \noexpand\begin{enumerate}[####1]
            \noexpand\item one
            \noexpand\item two
        \noexpand\end{enumerate}
        The above list is of type \csB
    }%
    }\temp
}

The \edef now spans the whole defintion, not only the parameters. This makes it necessary to prefix all used commands that should not be expanded by \noexpand, though. Not sure if this is practical in your real code. The xparse version suggested by egreg is more flexible here.

  • Won't the \edef expand \csname my\csA\endcsname? If the command is not already defined, it'll be set to \relax so it won't expand further, but if it's already defined, it'll keep expanding. – TH. Jun 9 '17 at 18:59
  • @TH. This is intended, isn't it? The \csA is part of the name for the command, the \csB is the parameter. In the original definition, expansion of \csA was triggered by \expandafter. – siracusa Jun 9 '17 at 19:37
  • \newcommand\problem{ \def\csA{foo} \def\csB{bar} \edef\temp{\noexpand\providecommand\csname my\csA\endcsname[1][\bar]} \temp{[##1]} } is a simplified version of \do. If you use \problem once, everything works. If you use \problem a second time, it fails with an error. The problem is expanding \csname my\csA\endcsname expands to \myfoo which then expands because the first execution of \problem defined it. – TH. Jun 9 '17 at 19:52
  • @siracusa I did some limited testing and so far it works ok. I have a question relevant to my real life code: could you modify the code so that after \end{enumerate} there is "The above list is of type \csB"? I'll add it to the question. – Łukasz Grabowski Jun 9 '17 at 20:37
  • @TH. Ah, it's clear now what you mean. I modified the code above to fix that. – siracusa Jun 10 '17 at 3:54
3

You should not use \providecommand for this. Moreover, redefining \do is quite dangerous, because several packages can use it. With etoolbox you have \forcsvlist, which accepts a different macro.

The main problem is passing \csB expanded.

\documentclass{article}
\usepackage{xstring,etoolbox}
\usepackage[shortlabels]{enumitem}

\newcommand\lists{%
    parts-a),
    items-1)
}

\newcommand*{\mydo}[1]{%
    \StrCut{#1}{-}\csA\csB
    % build \newcommand one step at a time
    \toks0=\expandafter{\expandafter\newcommand\csname my\csA\endcsname}%
    \toks2=\expandafter{\csB}%
    \begingroup\edef\x{\endgroup\the\toks0[1][\the\toks2]}\x{%
        \begin{enumerate}[##1]
            \item one
            \item two
        \end{enumerate}
    }%
}
\expandafter\forcsvlist\expandafter\mydo\expandafter{\lists}

\begin{document}
\myparts[i)]
\myparts
\end{document}

enter image description here

With a different approach, based on xparse:

\documentclass{article}
\usepackage{xparse}
\usepackage[shortlabels]{enumitem}

\NewDocumentCommand{\parselists}{>{\SplitList{,}}m}{%
  \ProcessList{#1}{\makelist}%
}
\NewDocumentCommand{\makelist}{>{\SplitArgument{1}{-}}{m}}{%
  \makelistsdo#1%
}
\NewDocumentCommand{\makelistsdo}{mm}{%
  \NewDocumentNamedCommand{my#1}{O{#2}}{%
        \begin{enumerate}[##1]
            \item one
            \item two
        \end{enumerate}
  }%
}
\ExplSyntaxOn
\cs_new_protected:Npn \NewDocumentNamedCommand #1
 {
  \exp_args:Nc \NewDocumentCommand { #1 }
 }
\ExplSyntaxOff

\parselists{
  parts-a),
  items-1),
}

\begin{document}
\myparts[i)]
\myparts
\myitems[i)]
\myitems
\end{document}

enter image description here

  • Thanks, I added an edit to the question to make it a notch more difficult - would your approach solve also the new question? (I'm more interested in the etoolbox version because it looks more readable to my latex2e-trained eyes) Also what's wrong with \providecommand? In my case the command will be both in a cls file and in a sty file, and sometimes I need to use only cls file without sty, or only sty file without cls, or both at the same time, so providecommand seemed natural – Łukasz Grabowski Jun 9 '17 at 20:49

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.