3

I try to write macro(s), say \foo, which automatically provides counters and \listofsomething like commands for some generated macros, using a xkeyval package approach for fine tuning of the generated commands.

I use the tocloft package for the counter and \listofsomething generation (although I could define some \@starttoc like approach by myself, I think)

However, I found expansion problems and no combination of \expandafter for \newlistof works (I omitted the \expandafter statements in the MWE) below.

The code does not complain, but the output is wrong, since using the generator macro \foo for another macro definition will provide the 'correct' \listofsomething command, but the \listofname is used from the last one defined, as well all entries go to the same list, which is not wanted.

(Of course, the real code has a lot of features (or will have)), but I boiled it down to a minimum. For example, the macro which contains the \listofname will be autogenerated from the \foo 2nd arg, if not explicitly given by a key-value, but I've omitted this here)

  • I don't suspect xkeyval is to blame here (since usage of the key macros outside of it and redefining them to other content will just provide the newest content, not previous ones.
  • \begingroup...\endgroup would help to keep the namespace clean within in \foo, but then the \listofsomething macros defined by \newlistof are not usable outside of \foo
  • The formatting of the output of the macro defined by \foo is not important here. It's just a small 'header' for testing only at the moment.
  • The \foo command is designed for \@onlypreamble usage, but I omitted this feature here for simplicity.

What's the possible solution of this problem? Or is there a way to \global the \listofsomething macro afterwards, such that grouping can be used inside \foo (perhaps some \let etc.?)


\documentclass{article}


\usepackage{tocloft}
\usepackage{xkeyval}

\makeatletter
\define@key{foo}{listofname}{%
  \def\foolistofname{#1}%
}

\define@key{foo}{countername}{%
  \def\foocountername{#1}%
}

\define@key{foo}{listext}{%
  \def\foolistext{#1}%
}


\makeatother

\newcommand{\foo}[2][]{%
\setkeys{foo}{#1}
\newlistof{\foocountername}{\foolistext}{\foolistofname}
\expandafter\newcommand\csname #2\endcsname[1]{%
  \refstepcounter{\foocountername}%
  \addcontentsline{\foolistext}{section}{\csname the\foocountername\endcsname~ ##1}
  {\vspace*{50pt}\noindent\bfseries \large \csname the\foocountername\endcsname~ ##1\normalsize\normalfont}
}
}

\newcommand{\listofmarxbrothersname}{List of Marx Brothers}
\newcommand{\listofstoogesname}{List of the Stooges}

% Define the command \marxbrothers and its \listof...
\foo[listofname={\listofmarxbrothersname},listext=mb,countername={mb}]{marxbrothers}
% Define the command \stooges and its \listof...

\foo[listofname=\expandafter{\listofstoogesname},listext=st,countername={stooges}]{stooges}


\def\foocountername{monty}
\def\foolistext{monty}
\def\foolistofname{List of Monty Pythons}


\newlistof{\foocountername}{\foolistext}{\foolistofname}


\begin{document}
\listofstooges
\listofmb%
\marxbrothers{Groucho}
\marxbrothers{Zeppo}
\marxbrothers{Harpo}
\marxbrothers{Chico}
\marxbrothers{Gummo}


\stooges{Curly}
\stooges{Moe}
\stooges{Larry}



\end{document}

The snapshot shows that all entries of go to the same list, that the wrong counter is used and the wrong List of .... title. There should be two lists.

enter image description here

  • You have to pass the expansion of the \foo... macros to the \newcommand you do. – egreg May 15 '15 at 20:58
  • @egreg: You mean to \marxbrothers etc.? This would explain the wrong counters etc/wrong list usage, but not the wrong listof title – user31729 May 15 '15 at 21:01
3

What you do with

\foo[listofname={\listofmarxbrothersname},listext=mb,countername={mb}]{marxbrothers}

is essentially

\newlistof{\foocountername}{\foolistext}{\foolistofname}
\newcommand\marxbrothers[1]{%
   \addcontentsline{\foolistext}{section}{\csname the\foocountername\endcsname~##1}%
  ...%
}

which is certainly not what you want, because \marxbrothers will use the current value of \foocountername and so on.

You'd need a gigantic \begingroup\edef\x{\endgroup...}\x trick, which is very cumbersome to write, with many \expandafter and \noexpand in strategic places. Not something I'd want to do.

Here's a much simpler implementation with expl3.

\documentclass{article}

\usepackage{tocloft}
\usepackage{xparse}

\ExplSyntaxOn
\keys_define:nn { christian/foo }
 {
  listofname .tl_set:N = \l_foo_listofname_tl,
  countername .tl_set:N = \l_foo_countername_tl,
  listext .tl_set:N = \l_foo_listext_tl,
 }

\NewDocumentCommand{\foo}{O{}m}
 {
  \keys_set:nn { christian/foo } { #1 }
  \__foo_create_commands:nVVV {#2} \l_foo_countername_tl \l_foo_listext_tl \l_foo_listofname_tl
 }

\cs_new_protected:Nn \__foo_create_commands:nnnn
 {
  \newlistof{#2}{#3}{#4}
  \cs_new_protected:cpn { #1 } ##1
   {
    \refstepcounter{#2}
    \addcontentsline{#3}{section}{\use:c{the#2}~##1}
    \group_begin:
    \par
    \vspace*{20pt}
    \noindent\large\bfseries\use:c{the#2}~##1\par
    \group_end:
   }
 }
\cs_generate_variant:Nn \__foo_create_commands:nnnn { nVVV }
\ExplSyntaxOff

\newcommand{\listofmarxbrothersname}{List of Marx Brothers}
\newcommand{\listofstoogesname}{List of the Stooges}

% Define the command \marxbrothers and its \listof...
\foo[listofname={\listofmarxbrothersname},listext=mb,countername={mb}]{marxbrothers}
% Define the command \stooges and its \listof...
\foo[listofname=\listofstoogesname,listext=st,countername={stooges}]{stooges}

\begin{document}

\listofstooges
\listofmb

\marxbrothers{Groucho}
\marxbrothers{Zeppo}
\marxbrothers{Harpo}
\marxbrothers{Chico}
\marxbrothers{Gummo}

\stooges{Curly}
\stooges{Moe}
\stooges{Larry}

\end{document}

enter image description here

In my opinion, \foo should have two mandatory arguments and possibly the keys should be marked as required, because you need all three of them when you define a new list with a new command.

  • I think, I should really learn LaTeX3 now ;-) The actual \foo command would have two mandatory commands is actually a \NewDocumentCommand already, but I reduced it for the MWE. I omitted the explicit setting of keys to provoke errors if not specified – user31729 May 15 '15 at 21:23

Your Answer

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