I am trying to set up a macro that defines a macro that is to be executed only once.
With what I learned from Define a Macro via Macro if Given Macro is not defined I can do that via the \expandafter\newcommand
syntax. And indeed the MWE as below works and produces the desired output:
where the subsequent \HelloWorld
and \HelloDude
are ignored.
What I would like to do, is to learn how to use \NewDocumentCommand
(instead of \newcommand
) to define this new macro via a macro.
My attempt (requires you to uncomment the \def
at the top of the file) fails with
Illegal parameter number in definition of \reserved@a
Furthermore, am wondering if there is a way to define repeated parameters. That is instead of {m m}
can I use something similar to with tabular
where one can say \begin{tabular}{r*{3}{l}r}
which is the identical to \begin{tabular}{rlllr}
.
This would save me from having to use \IfEqCase
for each possible number of parameters. I personally only need up to 3 so am ok with the slight duplication of code below, but though I'd ask in case there was some other syntax that I am not aware of.
Notes:
- You need to uncomment the
\def
at the top to get the MWE below to fail.
Refernces
Code:
%% Uncomment the following \def to get the failing test case.
%\def\UseXparseForDefiningMacro{}% Works if commented out (in which case \newcommand{}{} is used to define macro)
\documentclass{article}
\usepackage{etoolbox}
\usepackage{xstring}
\usepackage{xparse}
\usepackage{xcolor}
\makeatletter
\NewDocumentCommand{\DefineMeAMacroThatExecutesOnlyOnce}{m m m}{%
% #1 = csname to use
% #2 = number of parameters
% #3 = code to execute
\newtoggle{AlreadyIssued#1}%
\togglefalse{AlreadyIssued#1}%
\ifdefined\UseXparseForDefiningMacro
\IfEqCase{#2}{%
{0}{\expandafter\NewDocumentCommand\csname#1\endcsname{ }{% No paramater version
\iftoggle{AlreadyIssued#1}{}{%
#3% Never executed this macro so go ahead an execute it,
\toggletrue{AlreadyIssued#1}% and remember that we did (so we don't do it again).
}%
}}%
{1}{\expandafter\NewDocumentCommand\csname#1\endcsname{ m }{% 1 paramater version
\iftoggle{AlreadyIssued#1}{}{%
#3% Never executed this macro so go ahead an execute it,
\toggletrue{AlreadyIssued#1}% and remember that we did (so we don't do it again).
}%
}}%
{2}{\expandafter\NewDocumentCommand\csname#1\endcsname{ m m }{% 2 paramater version
\iftoggle{AlreadyIssued#1}{}{%
#3% Never executed this macro so go ahead an execute it,
\toggletrue{AlreadyIssued#1}% and remember that we did (so we don't do it again).
}%
}}%
}%
\else% --------------------------------------------- This works!!
\expandafter\newcommand\csname#1\endcsname[#2]{%
\iftoggle{AlreadyIssued#1}{}{%
#3% Never executed this macro so go ahead an execute it,
\toggletrue{AlreadyIssued#1}% and remember that we did (so we don't do it again).
}%
}%
\fi
}
\makeatother
\DefineMeAMacroThatExecutesOnlyOnce{HelloWorld}{0}{% Does not take any parameters
Hello World!%
}
\DefineMeAMacroThatExecutesOnlyOnce{HelloDude}{1}{% Take 1 parameter
Hello \textcolor{red}{#1}.%
}
\begin{document}
%% Section 1: Works if \UseXparseForDefiningMacro is NOT defined
\HelloWorld
\HelloWorld
%% Section 2: Works if \UseXparseForDefiningMacro is NOT defined
\HelloDude{Peter}
\HelloDude{John}
\end{document}
expl3
syntax? It makes much more sense when usingxparse
, in my opinion. I would imagine the solution would also be more concise.expl3
syntax yet.