Macro defining macro defining global macro (similar to `\author` or `\title` fields)
The definition of \DefineField
has a superfluous space in \gdef\csname @#1 \endcsname{##1}%
, it should be
\def\DefineField#1{%
\expandafter\edef\csname #1\endcsname##1{%
\gdef\csname @#1\endcsname{##1}%
}%
}
With the space you would define the command \@Alice␣
(where ␣
represents a space) as can be verified by checking \@Alice␣
's definition with
\expandafter\show\csname @Alice \endcsname
If we want to define \@Alice
we need to get rid of the space.
Note that the use of \edef
in \DefineField
can have unintended consequences, so you may want to consider going for the solution presented in Martin Scharrer's answer or egreg's answer.
I would not use \edef
here but work with a second \expandafter
.
The main problem here is that you have a space between #1
and \endcsname
, so you actually define \@Alice<space>
, which is possible with \csname.
Correct code is:
\def\DefineField#1{%
\expandafter\def\csname #1\endcsname##1{%
\expandafter\gdef\csname @#1\endcsname{##1}%
}%
}
You're defining \csname @Alice \endcsname
which results in a control sequence token having a trailing space in its name. The space after \csname
doesn't find its way during tokenization, because it follows a control word, but the space between #1
and \endcsname
will not be removed.
Your usage of \edef
is quite problematic as well: if you mistakenly use \DefineField{Alice}
twice,
\DefineField{Alice}
\Alice{My name is Alice}
\show\Alice
\DefineField{Alice}
\show\Alice
you'll get
> \Alice=macro:
#1->\gdef \@Alice {#1}.
> \Alice=macro:
#1->\gdef My name is Alice{#1}.
This can be cured by checking first if \Alice
is defined:
\def\DefineField#1{%
\@ifundefined{#1}
{% go on, it's undefined
\expandafter\edef\csname #1\endcsname##1{%
\gdef\csname @#1\endcsname{##1}%
}%
}
{% ignore the redefinition
\PackageWarning{andywiecko}{%
\expandafter\noexpand\csname #1\endcsname already defined%
}%
}%
}
A simpler approach with xparse
and expl3
:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\DefineField}{m}
{
\cs_new_protected:cpn { #1 } ##1
{
\cs_new_protected:cpn { @#1 } { ##1 } % or `\cs_new:cpn
}
}
\ExplSyntaxOff
\DefineField{Alice}
\Alice{My name is Alice}
\expandafter\show\csname @Alice\endcsname
This will produce a standard error if \DefineField{Alice}
is used twice or if you try doing \DefineField{box}
; the code will show
> \@Alice=\protected\long macro:
->My name is Alice.
You may want to use \cs_new:cpn
in the second instance, depending on what expansion context you will use the \@Alice
macro.
A more proper expl3
interface, where instead of defining \@foo
macros you do \UseField{foo}
when needed. A way to override already existing fields is provided.
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\prop_new:N \g_andywiecko_fields_prop
\cs_new:Nn \andywiecko_define_field:nn
{
\prop_gput:Nnn \g_andywiecko_fields_prop { #1 } { #2 }
}
\NewDocumentCommand{\DefineField}{sm}
{
\cs_set_protected:Nn \__andywiecko:n
{
\andywiecko_define_field:nn { #2 } { ##1 }
}
\IfBooleanTF { #1 }
{% override an existing definition
\cs_gset_eq:cN { #2 } \__andywiecko:n
}
{% new field
\cs_new_eq:cN { #2 } \__andywiecko:n
}
}
\NewExpandableDocumentCommand{\UseField}{m}
{
\prop_item:Nn \g_andywiecko_fields_prop { #1 }
}
\ExplSyntaxOff
\DefineField{author} % gives error
\DefineField*{author} % no error, use new interface
\DefineField{Alice}
\Alice{My name is Alice}
\DefineField{Alice} % gives error