1

I have the following setup to apply macros to certain data of a command:

\makeatletter
\def\myqu@rck{\myqu@rck}
\pgfkeys{/mykeys/macro/.code=\mykeys@defmacro{#1}}
\def\mykeys@defmacro#1{\mykeys@@defmacro#1\myqu@rck}
%the user macros are maintained in a separate folder
\def\mykeys@@defmacro#1=#2\myqu@rck{\pgfkeys{/mykeys/user macros/#1/.code=#2}}
\makeatother

The usage then is:

\mycomplicatedcmd[macro={bold=\textbf {#1}},temperature=bold,pressure,density]{<data>}

and when temperatures are found in <data>, these are printed in bold.

To centralize the definition of the macros I want another command were I can define all these macros.

Working set up

\def\mymacros#1{\pgfkeys{/mykeys/.cd,#1}}

\mymacros{macro={bold=\textbf{#1}}}

yields: \pgfkeys{mykeys/macro={bold=\textbf{#1}}} and then: \pgfkeys{mykeys/user macros/bold=\textbf{#1}}}

and it works:

\pgfkeys{/mykeys/user macros/bold=test}

prints test

Multiple macros can be defined at once:

\mymacros{%
  macro={italic=\textit{#1}},
  macro={math=$#1$},
  macro={small caps=\textsc{#1}}
}

\pgfkeys{/mykeys/user macros/italic=test};
\pgfkeys{/mykeys/user macros/math=\tau};
\pgfkeys{/mykeys/user macros/small caps=test}

Modified setup (non working)

\def\mymacros#1{\pgfkeys{/mykeys/.cd,macro={#1}}}
\mymacros{upper case=\MakeUppercase{#1}}

\pgfkeys{/mykeys/user macros/upper case=test}

Works, but now I can not insert a list:

\mymacros{%
  italic=\textit{#1},
%  math=$#1$,
%  small caps=\textsc{#1}
}
% yields:   \pgfkeys{mykeys/macro=<list>}} which is wrong

So, \mymacros should loop over the list. With pgfkeys it does not work:

\pgfkeys{mykeys/macro/.list={upper case=\MakeUppercase{#1}}}
%! Illegal parameter number in definition of \pgffor@values

With a \@for loop it dose not work either.

\makeatletter
\def\mymacros#1{%
  \@for\my@temp:=#1\do{%
    \pgfkeys{/mykeys/macro=\my@temp}
}}
\makeatother

\mymacros{upper case=\MakeUppercase{#1}}}  
%! Illegal parameter number in definition of \@fortmp

The complete MWE:

\documentclass{article}

\usepackage{pgfkeys}
\setlength{\parindent}{0pt}

\begin{document}

\makeatletter
\def\myqu@rck{\myqu@rck}
\pgfkeys{/mykeys/macro/.code=\mykeys@defmacro{#1}}
\def\mykeys@defmacro#1{\mykeys@@defmacro#1\myqu@rck}
% the user macros are maintained in a separate folder
\def\mykeys@@defmacro#1=#2\myqu@rck{\pgfkeys{/mykeys/user macros/#1/.code=#2}}
\makeatother


\subsection*{working setup}

\def\mymacros#1{\pgfkeys{/mykeys/.cd,#1}}

\mymacros{macro={bold=\textbf{#1}}}
% yields:   \pgfkeys{mykeys/macro={bold=\textbf{#1}}}
% and then: \pgfkeys{mykeys/user macros/bold=\textbf{#1}}}
% and it works:
\pgfkeys{/mykeys/user macros/bold=test}

multiple macros can be defined at once:
\mymacros{%
  macro={italic=\textit{#1}},
  macro={math=$#1$},
  macro={small caps=\textsc{#1}}
}
\pgfkeys{/mykeys/user macros/italic=test};
\pgfkeys{/mykeys/user macros/math=\tau};
\pgfkeys{/mykeys/user macros/small caps=test}


\subsection*{modified setup (non working)}

%I would like to avoid to write \verb|{macro={<macro>=<code>}}| every time.

It works for one:
\def\mymacros#1{\pgfkeys{/mykeys/.cd,macro={#1}}}
\mymacros{upper case=\MakeUppercase{#1}}
%
\pgfkeys{/mykeys/user macros/upper case=test}

but now I can not insert a list.

%%%pgfkeys does not work
%\pgfkeys{mykeys/macro/.list={upper case=\MakeUppercase{#1}}}
%%%! Illegal parameter number in definition of \pgffor@values

%%% this does not work either

\makeatletter
\def\mymacros#1{%
  \@for\my@temp:=#1\do{%
    \pgfkeys{/mykeys/macro=\my@temp}
}}
\makeatother

%\mymacros{bold=\textbf{#1}}  %! Illegal parameter number in definition of \@fortmp


\end{document}

2 Answers 2

3

You could use another macro to loop over the comma separated list. The following uses the expl3 function \clist_map_inline:nn instead of LaTeX2e's \@for.

\documentclass{article}

\usepackage{pgfkeys}
\setlength{\parindent}{0pt}

\begin{document}

\makeatletter
\def\myqu@rck{\myqu@rck}
\pgfkeys{/mykeys/macro/.code=\mykeys@defmacro{#1}}
\def\mykeys@defmacro#1{\mykeys@@defmacro#1\myqu@rck}
% the user macros are maintained in a separate folder
\def\mykeys@@defmacro#1=#2\myqu@rck{\pgfkeys{/mykeys/user macros/#1/.code=#2}}

\ExplSyntaxOn
\cs_new_eq:NN \clist@map@inline@@nn \clist_map_inline:nn
\ExplSyntaxOff
\def\mymacros#1{\clist@map@inline@@nn{#1}{\pgfkeys{/mykeys/macro={##1}}}}
\makeatother

\subsection*{working setup}

Multiple macros can be defined at once:
\mymacros{%
  italic=\textit{#1},
  math=$#1$,
  small caps=\textsc{#1}
}
\pgfkeys{/mykeys/user macros/italic=test};
\pgfkeys{/mykeys/user macros/math=\tau};
\pgfkeys{/mykeys/user macros/small caps=test}
\end{document}

enter image description here

1
  • 1
    @RaoulKessels I don't agree with this view. The needed expansion of the macro used in \@for makes code much less readable and (especially for not that experienced people) often leads to bugs in implementations. The hash doubling needed in the code above on the other hand is a regular thing that's easy to grasp, imho. If you refer to the macro's argument you use #1, if you refer to the argument of a nested definition you use ##1. Easy as that.
    – Skillmon
    Sep 25, 2021 at 9:14
1

Searching for \clist_map_inline:nn, I found an article by Enrico Gregorio, saying:

The given list is mapped by passing each item to the second argument, where the current item is referred to as #1; here the hash mark needs to be doubled because we’re in the body of a definition. Compare this with the standard \@for cycle, where the current item is stored in a macro, which typically needs to be expanded, often in an awkward way (TUGboat, Volume 39 (2018), No. 1, p. 51--59).

So, to do it without expl3, it is just a matter of finding the right awkward way and doubling the hashes. It must be noted that the author of the article encourages the use of expl3 to avoid, precisely, the complicated use of \expandafter's.

\makeatletter
\def\mykeysmacro{/mykeys/macro=}
\def\mymacros#1{%

\@for\my@temp:=#1\do{%
\expandafter\expandafter\expandafter
\pgfkeys
\expandafter\expandafter\expandafter
{\expandafter\mykeysmacro\expandafter{\my@temp}}
}}
\makeatother

Careful: double hashes!

\mymacros{
  italic=\textit{##1},
  math=$##1$,
  small caps=\textsc{##1}
}
\pgfkeys{/mykeys/user macros/italic=test};
\pgfkeys{/mykeys/user macros/math=\tau};
\pgfkeys{/mykeys/user macros/small caps=test}

This works, but having to use double hashes will not be very comfortable for the user. I also found a way of doubling them automatically in this answer by @wipet using \scantokes:

\documentclass{article}

\usepackage{pgfkeys} 
\usepackage{verbatim} 
\setlength{\parindent}{0pt}

\begin{document}

\makeatletter
\def\myqu@rck{\myqu@rck}
\pgfkeys{/mykeys/macro/.code=\mykeys@defmacro{#1}}
\def\mykeys@defmacro#1{\mykeys@@defmacro#1\myqu@rck}
% the user macros are maintained in a separate folder
\def\mykeys@@defmacro#1=#2\myqu@rck{\pgfkeys{/.     
mykeys/user macros/#1/.code=#2}}

\def\my@keysmacro{/mykeys/macro=} 
\def\mymacros#1{% 
%%% 
%%% https://tex.stackexchange.com/a/553015 
%%%    
\scantokens{\toks0{#1}}    
\edef\my@tempb{\the\toks0}  
%%%   
\@for\my@tempa:=\my@tempb\do{%
    \expandafter\expandafter\expandafter
    \pgfkeys
    \expandafter\expandafter\expandafter
    {\expandafter\my@keysmacro\expandafter{\my@tempa}} 
}} 
\makeatother

\subsection*{this works}

\mymacros{
  italic=\textit{#1},
  math=$#1$,
  small caps=\textsc{#1} }

\pgfkeys{/mykeys/user macros/italic=test}; 
\pgfkeys{/mykeys/user macros/math=\tau}; 
\pgfkeys{/mykeys/user macros/small caps=test}

\end{document}

enter image description here

2
  • The article surely doesn't endorse \expandafter frenzy; it's meant to avoid them.
    – egreg
    Dec 22, 2021 at 9:42
  • @egreg Yes, sorry. Context was in a comment that I deleted. I have edited. Buon Natale Dec 23, 2021 at 7:33

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.