Problem.
I'm trying to write a command similar to \newcommand
, but I'm having problems with illegal parameter numbers: for reasons that I can't fix out, it's asking me to use parameters of the type ##1
rather than #1
. I'm familiar with using the former in the case of nested definitions, e.g. in code-snippets such as \def\foo#1{\def\bar##1{#1-##1}}
; but I don't understand why the error is coming up in what I'm trying to do. I'm hoping that someone can show me how to make parameters of the style #1
suffice, in the example I describe below.
Details.
I'm trying to write a macro called \newmacro
, which emulates the syntax of \newcommand
, and which makes each macro write its own definition to an output file the first time it is used. Try out the minimal example below to see precisely what I mean:
\documentclass{article}
\makeatletter
\newwrite\macro@outfile
\immediate\openout\macro@outfile=\jobname.macros%
\typeout{Writing extra macros to file \jobname.macros}%
\def\macro@write#1#2{%
\immediate\write\macro@outfile{\string\renewcommand\string#1#2}%
\renewcommand#1#2%
#1%
}
\def\newmacro#1[#2]#3{%
\def#1{\macro@write{#1}{[#2]{#3}}}}
\makeatother
These macro definitions don't quite work properly: for instance, using the above pre-amble, the second invocation of \newmacro
below throws an error.
\begin{document}
\newmacro\testmacro[1]{foo(##1)}
\testmacro{1}
\testmacro{2}
\newmacro\testmacro[1]{foo --- #1 ---}
\testmacro{3}
\testmacro{4}
\end{document}
If this document is compiled with the preamble above as test.tex
, in addition to the usual output, a new file test.macros
is created which will have the following content:
\renewcommand\testmacro[1]{foo(##1)}
\renewcommand\testmacro[1]{foo --- ##1 ---}
This is almost correct, and it is what I mean by the macro writing "its own definition" to an output file. However, I would like the stored definitions to use the parameters #1
rather than ##1
, and for the invocation of \newmacro
with the parameter #1
not to throw errors. I'm hoping that someone can show me how to do this.
Best Answer
When you say:
You end up with:
So you try to define a macro without any arguments but still use
#1
in it. This leads to theIllegal parameter number
error message.In order to permit
#1
,#2
, etc. you would need to define the macro with the right number of arguments. You can define the macro first with nine arguments to avoid anyIllegal parameter number
error. Then you expand it with##1
etc. as arguments, so you basically replace#1
with##1
, etc., and define this to the same macro but without arguments. Because all this happens inside another macro\newmacro
all#
must be doubled:Then, however, you have the issues of
##
in your output file. This can be best avoided by again defining the macro with nine arguments and print the\meaning
with the prefix stripped, before defining it to its true definition:The
\jobname.macros
files then holds:However, this breaks when you now use
##1
inside the macro definition, e.g:So instead you would recommend the following approach: Define the macro to a different name and copy that definition to the real name inside
\macro@write
. You avoid thereby all issues with escaping of macro arguments. You can use\csname
with\string#1
to build a second macro name which is unique to the official macro name.I took the liberty to replace
\renewcommand
with\newcommand
because its seems to be more logically for a\newmacro
macro. If you really want\renewcommand
, i.e. allow\newmacro
to redefine its macro, then define\newmacro
like:The first line ensures that the macro exists in any case before
\renewcommand
is used.