Macro defining macro defining global macro (similar to `author` or `title` fields)
I have already seen these posts Macro defining macro and
Macros that define other macros, which also define other macros but unfortunately I didn't manege to figure it out how to correct my attempt to the problem.
I want to create a macro which will produce macro + global macro with @
in front of macro name, like in the following example:
defBob#1{gdef@Bob{#1}}
Bob{My name is Bob}
makeatletter
@Bob % it will display `My name is Bob'
Bob{Name: Bob}
@Bob % it will display `Name: Bob'
makeatother
So this works. However:
defDefineField#1{%
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1 endcsname{##1}%
}%
}
DefineField{Alice}
Alice{My name is Alice}
doesn't work. I got Undefined control sequence.
with @Alice
. I have checked Alice
and Bob
with show
command:
> Bob=macro:
#1->gdef @Bob {#1}.
l.20 showBob
> Alice=macro:
#1->gdef @Alice {#1}.
l.21 showAlice
They have exactly the same structure. What did I miss?
MWE:
documentclass[preview]{standalone}
makeatletter
defDefineField#1{%
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1 endcsname{##1}%
}%
}
DefineField{Alice}
Alice{My name is Alice}
defBob#1{gdef@Bob{#1}}
Bob{My name is Bob}
begin{document}
%@Alice % Undefined control sequence.
@Bob
Bob{No my name is Alice}
@Bob
showBob
showAlice
makeatother
end{document}
macros undefined
add a comment |
I have already seen these posts Macro defining macro and
Macros that define other macros, which also define other macros but unfortunately I didn't manege to figure it out how to correct my attempt to the problem.
I want to create a macro which will produce macro + global macro with @
in front of macro name, like in the following example:
defBob#1{gdef@Bob{#1}}
Bob{My name is Bob}
makeatletter
@Bob % it will display `My name is Bob'
Bob{Name: Bob}
@Bob % it will display `Name: Bob'
makeatother
So this works. However:
defDefineField#1{%
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1 endcsname{##1}%
}%
}
DefineField{Alice}
Alice{My name is Alice}
doesn't work. I got Undefined control sequence.
with @Alice
. I have checked Alice
and Bob
with show
command:
> Bob=macro:
#1->gdef @Bob {#1}.
l.20 showBob
> Alice=macro:
#1->gdef @Alice {#1}.
l.21 showAlice
They have exactly the same structure. What did I miss?
MWE:
documentclass[preview]{standalone}
makeatletter
defDefineField#1{%
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1 endcsname{##1}%
}%
}
DefineField{Alice}
Alice{My name is Alice}
defBob#1{gdef@Bob{#1}}
Bob{My name is Bob}
begin{document}
%@Alice % Undefined control sequence.
@Bob
Bob{No my name is Alice}
@Bob
showBob
showAlice
makeatother
end{document}
macros undefined
1
Remove the space after the#1
ingdefcsname @#1 endcsname{##1}%
, it should begdefcsname @#1endcsname{##1}%
– moewe
Jan 27 at 11:00
Thank you very much, I don't know why I missed it.
– andywiecko
Jan 27 at 11:05
add a comment |
I have already seen these posts Macro defining macro and
Macros that define other macros, which also define other macros but unfortunately I didn't manege to figure it out how to correct my attempt to the problem.
I want to create a macro which will produce macro + global macro with @
in front of macro name, like in the following example:
defBob#1{gdef@Bob{#1}}
Bob{My name is Bob}
makeatletter
@Bob % it will display `My name is Bob'
Bob{Name: Bob}
@Bob % it will display `Name: Bob'
makeatother
So this works. However:
defDefineField#1{%
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1 endcsname{##1}%
}%
}
DefineField{Alice}
Alice{My name is Alice}
doesn't work. I got Undefined control sequence.
with @Alice
. I have checked Alice
and Bob
with show
command:
> Bob=macro:
#1->gdef @Bob {#1}.
l.20 showBob
> Alice=macro:
#1->gdef @Alice {#1}.
l.21 showAlice
They have exactly the same structure. What did I miss?
MWE:
documentclass[preview]{standalone}
makeatletter
defDefineField#1{%
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1 endcsname{##1}%
}%
}
DefineField{Alice}
Alice{My name is Alice}
defBob#1{gdef@Bob{#1}}
Bob{My name is Bob}
begin{document}
%@Alice % Undefined control sequence.
@Bob
Bob{No my name is Alice}
@Bob
showBob
showAlice
makeatother
end{document}
macros undefined
I have already seen these posts Macro defining macro and
Macros that define other macros, which also define other macros but unfortunately I didn't manege to figure it out how to correct my attempt to the problem.
I want to create a macro which will produce macro + global macro with @
in front of macro name, like in the following example:
defBob#1{gdef@Bob{#1}}
Bob{My name is Bob}
makeatletter
@Bob % it will display `My name is Bob'
Bob{Name: Bob}
@Bob % it will display `Name: Bob'
makeatother
So this works. However:
defDefineField#1{%
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1 endcsname{##1}%
}%
}
DefineField{Alice}
Alice{My name is Alice}
doesn't work. I got Undefined control sequence.
with @Alice
. I have checked Alice
and Bob
with show
command:
> Bob=macro:
#1->gdef @Bob {#1}.
l.20 showBob
> Alice=macro:
#1->gdef @Alice {#1}.
l.21 showAlice
They have exactly the same structure. What did I miss?
MWE:
documentclass[preview]{standalone}
makeatletter
defDefineField#1{%
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1 endcsname{##1}%
}%
}
DefineField{Alice}
Alice{My name is Alice}
defBob#1{gdef@Bob{#1}}
Bob{My name is Bob}
begin{document}
%@Alice % Undefined control sequence.
@Bob
Bob{No my name is Alice}
@Bob
showBob
showAlice
makeatother
end{document}
macros undefined
macros undefined
edited Jan 27 at 16:36
Martin Scharrer♦
202k46643820
202k46643820
asked Jan 27 at 10:31
andywieckoandywiecko
628
628
1
Remove the space after the#1
ingdefcsname @#1 endcsname{##1}%
, it should begdefcsname @#1endcsname{##1}%
– moewe
Jan 27 at 11:00
Thank you very much, I don't know why I missed it.
– andywiecko
Jan 27 at 11:05
add a comment |
1
Remove the space after the#1
ingdefcsname @#1 endcsname{##1}%
, it should begdefcsname @#1endcsname{##1}%
– moewe
Jan 27 at 11:00
Thank you very much, I don't know why I missed it.
– andywiecko
Jan 27 at 11:05
1
1
Remove the space after the
#1
in gdefcsname @#1 endcsname{##1}%
, it should be gdefcsname @#1endcsname{##1}%
– moewe
Jan 27 at 11:00
Remove the space after the
#1
in gdefcsname @#1 endcsname{##1}%
, it should be gdefcsname @#1endcsname{##1}%
– moewe
Jan 27 at 11:00
Thank you very much, I don't know why I missed it.
– andywiecko
Jan 27 at 11:05
Thank you very much, I don't know why I missed it.
– andywiecko
Jan 27 at 11:05
add a comment |
4 Answers
4
active
oldest
votes
The definition of DefineField
has a superfluous space in gdefcsname @#1 endcsname{##1}%
, it should be
defDefineField#1{%
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1endcsname{##1}%
}%
}
With the space you would define the command @Alice␣
(where ␣
represents a space) as can be verified by checking @Alice␣
's definition with
expandaftershowcsname @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.
add a comment |
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:
defDefineField#1{%
expandafterdefcsname #1endcsname##1{%
expandaftergdefcsname @#1endcsname{##1}%
}%
}
add a comment |
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}
showAlice
DefineField{Alice}
showAlice
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:
defDefineField#1{%
@ifundefined{#1}
{% go on, it's undefined
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1endcsname{##1}%
}%
}
{% ignore the redefinition
PackageWarning{andywiecko}{%
expandafternoexpandcsname #1endcsname 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}
expandaftershowcsname @Aliceendcsname
This will produce a standard error if DefineField{Alice}
is used twice or if you try doing DefineField{box}
; the code will show
> @Alice=protectedlong 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
Yours comments, especially about double usage ofDefineField{Alice}
, are very useful, thanks!
– andywiecko
Jan 27 at 11:49
add a comment |
In the other answers the spurious space within the csname..endcsname
-expression is already pointed out.
In case you wish to avoid typing so many csname..endcsname
-expressions, I can offer a macro name
.
name
takes following tokens that are not nested in braces for its first argument and following tokens nested in braces for its second argument and applies csname..endcsname
to its second argument.
The tokens that form the first argument can, e.g., be: globallongouterdef
or newcommand
or string
or meaning
or whatever.
You can also leave the first argument empty.
Examples:
nameshow{foo}
yields: showfoo
namedef{foo}...
yields: deffoo...
namegloballongouterdef{foo}...
yields: globallongouterdeffoo...
namenewcommand{foo}...
yields: newcommandfoo...
name{foo}...
yields: foo
namenamegloballet{foo}={bar}
yields:namegloballetfoo={bar}
yields:globalletfoo=bar
documentclass{article}
newcommandUDExchange[2]{#2#1}
newcommandname{}longdefname#1#{romannumeral0innername{#1}}%
newcommandinnername[2]{%
expandafterUDExchangeexpandafter{csname#2endcsname}{ #1}%
}%
makeatletter
newcommandnovalueprovided{nfss@text{reset@fontbfseries??}}
makeatother
newcommandDefineField[2][novalueprovided]{%
namenewcommand{#2}[1]{namegdef{@#2}{##1}}%
namenewcommand{@#2}{}%
name{#2}{#1}%
}%
newcommandUseField[1]{name{@#1}}%
DefineField[initial value of field Alice]{Alice}
DefineField{Bob}
begin{document}
{ttfrenchspacing
namestring{Alice}: namemeaning{Alice}
namestring{@Alice}: namemeaning{@Alice}
namestring{Bob}: namemeaning{Bob}
namestring{@Bob}: namemeaning{@Bob}
verb|UseField{Alice}:| UseField{Alice}
verb|UseField{Bob}:| UseField{Bob}
}
hrulefill
Now the fields are changed:
Alice{My name is Alice}
Bob{My name is Bob}
{ttfrenchspacing
namestring{Alice}: namemeaning{Alice}
namestring{@Alice}: namemeaning{@Alice}
namestring{Bob}: namemeaning{Bob}
namestring{@Bob}: namemeaning{@Bob}
verb|UseField{Alice}:| UseField{Alice}
verb|UseField{Bob}:| UseField{Bob}
}
end{document}
If you provide more info about what you try to achieve in practice, an interface could be developed which also does better error-management.
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "85"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f472068%2fmacro-defining-macro-defining-global-macro-similar-to-author-or-title-fie%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
The definition of DefineField
has a superfluous space in gdefcsname @#1 endcsname{##1}%
, it should be
defDefineField#1{%
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1endcsname{##1}%
}%
}
With the space you would define the command @Alice␣
(where ␣
represents a space) as can be verified by checking @Alice␣
's definition with
expandaftershowcsname @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.
add a comment |
The definition of DefineField
has a superfluous space in gdefcsname @#1 endcsname{##1}%
, it should be
defDefineField#1{%
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1endcsname{##1}%
}%
}
With the space you would define the command @Alice␣
(where ␣
represents a space) as can be verified by checking @Alice␣
's definition with
expandaftershowcsname @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.
add a comment |
The definition of DefineField
has a superfluous space in gdefcsname @#1 endcsname{##1}%
, it should be
defDefineField#1{%
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1endcsname{##1}%
}%
}
With the space you would define the command @Alice␣
(where ␣
represents a space) as can be verified by checking @Alice␣
's definition with
expandaftershowcsname @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.
The definition of DefineField
has a superfluous space in gdefcsname @#1 endcsname{##1}%
, it should be
defDefineField#1{%
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1endcsname{##1}%
}%
}
With the space you would define the command @Alice␣
(where ␣
represents a space) as can be verified by checking @Alice␣
's definition with
expandaftershowcsname @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.
edited Jan 27 at 11:56
answered Jan 27 at 11:04
moewemoewe
91.3k10114345
91.3k10114345
add a comment |
add a comment |
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:
defDefineField#1{%
expandafterdefcsname #1endcsname##1{%
expandaftergdefcsname @#1endcsname{##1}%
}%
}
add a comment |
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:
defDefineField#1{%
expandafterdefcsname #1endcsname##1{%
expandaftergdefcsname @#1endcsname{##1}%
}%
}
add a comment |
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:
defDefineField#1{%
expandafterdefcsname #1endcsname##1{%
expandaftergdefcsname @#1endcsname{##1}%
}%
}
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:
defDefineField#1{%
expandafterdefcsname #1endcsname##1{%
expandaftergdefcsname @#1endcsname{##1}%
}%
}
answered Jan 27 at 11:05
Martin Scharrer♦Martin Scharrer
202k46643820
202k46643820
add a comment |
add a comment |
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}
showAlice
DefineField{Alice}
showAlice
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:
defDefineField#1{%
@ifundefined{#1}
{% go on, it's undefined
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1endcsname{##1}%
}%
}
{% ignore the redefinition
PackageWarning{andywiecko}{%
expandafternoexpandcsname #1endcsname 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}
expandaftershowcsname @Aliceendcsname
This will produce a standard error if DefineField{Alice}
is used twice or if you try doing DefineField{box}
; the code will show
> @Alice=protectedlong 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
Yours comments, especially about double usage ofDefineField{Alice}
, are very useful, thanks!
– andywiecko
Jan 27 at 11:49
add a comment |
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}
showAlice
DefineField{Alice}
showAlice
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:
defDefineField#1{%
@ifundefined{#1}
{% go on, it's undefined
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1endcsname{##1}%
}%
}
{% ignore the redefinition
PackageWarning{andywiecko}{%
expandafternoexpandcsname #1endcsname 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}
expandaftershowcsname @Aliceendcsname
This will produce a standard error if DefineField{Alice}
is used twice or if you try doing DefineField{box}
; the code will show
> @Alice=protectedlong 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
Yours comments, especially about double usage ofDefineField{Alice}
, are very useful, thanks!
– andywiecko
Jan 27 at 11:49
add a comment |
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}
showAlice
DefineField{Alice}
showAlice
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:
defDefineField#1{%
@ifundefined{#1}
{% go on, it's undefined
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1endcsname{##1}%
}%
}
{% ignore the redefinition
PackageWarning{andywiecko}{%
expandafternoexpandcsname #1endcsname 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}
expandaftershowcsname @Aliceendcsname
This will produce a standard error if DefineField{Alice}
is used twice or if you try doing DefineField{box}
; the code will show
> @Alice=protectedlong 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
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}
showAlice
DefineField{Alice}
showAlice
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:
defDefineField#1{%
@ifundefined{#1}
{% go on, it's undefined
expandafteredefcsname #1endcsname##1{%
gdefcsname @#1endcsname{##1}%
}%
}
{% ignore the redefinition
PackageWarning{andywiecko}{%
expandafternoexpandcsname #1endcsname 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}
expandaftershowcsname @Aliceendcsname
This will produce a standard error if DefineField{Alice}
is used twice or if you try doing DefineField{box}
; the code will show
> @Alice=protectedlong 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
edited Jan 27 at 12:22
answered Jan 27 at 11:40
egregegreg
720k8719093208
720k8719093208
Yours comments, especially about double usage ofDefineField{Alice}
, are very useful, thanks!
– andywiecko
Jan 27 at 11:49
add a comment |
Yours comments, especially about double usage ofDefineField{Alice}
, are very useful, thanks!
– andywiecko
Jan 27 at 11:49
Yours comments, especially about double usage of
DefineField{Alice}
, are very useful, thanks!– andywiecko
Jan 27 at 11:49
Yours comments, especially about double usage of
DefineField{Alice}
, are very useful, thanks!– andywiecko
Jan 27 at 11:49
add a comment |
In the other answers the spurious space within the csname..endcsname
-expression is already pointed out.
In case you wish to avoid typing so many csname..endcsname
-expressions, I can offer a macro name
.
name
takes following tokens that are not nested in braces for its first argument and following tokens nested in braces for its second argument and applies csname..endcsname
to its second argument.
The tokens that form the first argument can, e.g., be: globallongouterdef
or newcommand
or string
or meaning
or whatever.
You can also leave the first argument empty.
Examples:
nameshow{foo}
yields: showfoo
namedef{foo}...
yields: deffoo...
namegloballongouterdef{foo}...
yields: globallongouterdeffoo...
namenewcommand{foo}...
yields: newcommandfoo...
name{foo}...
yields: foo
namenamegloballet{foo}={bar}
yields:namegloballetfoo={bar}
yields:globalletfoo=bar
documentclass{article}
newcommandUDExchange[2]{#2#1}
newcommandname{}longdefname#1#{romannumeral0innername{#1}}%
newcommandinnername[2]{%
expandafterUDExchangeexpandafter{csname#2endcsname}{ #1}%
}%
makeatletter
newcommandnovalueprovided{nfss@text{reset@fontbfseries??}}
makeatother
newcommandDefineField[2][novalueprovided]{%
namenewcommand{#2}[1]{namegdef{@#2}{##1}}%
namenewcommand{@#2}{}%
name{#2}{#1}%
}%
newcommandUseField[1]{name{@#1}}%
DefineField[initial value of field Alice]{Alice}
DefineField{Bob}
begin{document}
{ttfrenchspacing
namestring{Alice}: namemeaning{Alice}
namestring{@Alice}: namemeaning{@Alice}
namestring{Bob}: namemeaning{Bob}
namestring{@Bob}: namemeaning{@Bob}
verb|UseField{Alice}:| UseField{Alice}
verb|UseField{Bob}:| UseField{Bob}
}
hrulefill
Now the fields are changed:
Alice{My name is Alice}
Bob{My name is Bob}
{ttfrenchspacing
namestring{Alice}: namemeaning{Alice}
namestring{@Alice}: namemeaning{@Alice}
namestring{Bob}: namemeaning{Bob}
namestring{@Bob}: namemeaning{@Bob}
verb|UseField{Alice}:| UseField{Alice}
verb|UseField{Bob}:| UseField{Bob}
}
end{document}
If you provide more info about what you try to achieve in practice, an interface could be developed which also does better error-management.
add a comment |
In the other answers the spurious space within the csname..endcsname
-expression is already pointed out.
In case you wish to avoid typing so many csname..endcsname
-expressions, I can offer a macro name
.
name
takes following tokens that are not nested in braces for its first argument and following tokens nested in braces for its second argument and applies csname..endcsname
to its second argument.
The tokens that form the first argument can, e.g., be: globallongouterdef
or newcommand
or string
or meaning
or whatever.
You can also leave the first argument empty.
Examples:
nameshow{foo}
yields: showfoo
namedef{foo}...
yields: deffoo...
namegloballongouterdef{foo}...
yields: globallongouterdeffoo...
namenewcommand{foo}...
yields: newcommandfoo...
name{foo}...
yields: foo
namenamegloballet{foo}={bar}
yields:namegloballetfoo={bar}
yields:globalletfoo=bar
documentclass{article}
newcommandUDExchange[2]{#2#1}
newcommandname{}longdefname#1#{romannumeral0innername{#1}}%
newcommandinnername[2]{%
expandafterUDExchangeexpandafter{csname#2endcsname}{ #1}%
}%
makeatletter
newcommandnovalueprovided{nfss@text{reset@fontbfseries??}}
makeatother
newcommandDefineField[2][novalueprovided]{%
namenewcommand{#2}[1]{namegdef{@#2}{##1}}%
namenewcommand{@#2}{}%
name{#2}{#1}%
}%
newcommandUseField[1]{name{@#1}}%
DefineField[initial value of field Alice]{Alice}
DefineField{Bob}
begin{document}
{ttfrenchspacing
namestring{Alice}: namemeaning{Alice}
namestring{@Alice}: namemeaning{@Alice}
namestring{Bob}: namemeaning{Bob}
namestring{@Bob}: namemeaning{@Bob}
verb|UseField{Alice}:| UseField{Alice}
verb|UseField{Bob}:| UseField{Bob}
}
hrulefill
Now the fields are changed:
Alice{My name is Alice}
Bob{My name is Bob}
{ttfrenchspacing
namestring{Alice}: namemeaning{Alice}
namestring{@Alice}: namemeaning{@Alice}
namestring{Bob}: namemeaning{Bob}
namestring{@Bob}: namemeaning{@Bob}
verb|UseField{Alice}:| UseField{Alice}
verb|UseField{Bob}:| UseField{Bob}
}
end{document}
If you provide more info about what you try to achieve in practice, an interface could be developed which also does better error-management.
add a comment |
In the other answers the spurious space within the csname..endcsname
-expression is already pointed out.
In case you wish to avoid typing so many csname..endcsname
-expressions, I can offer a macro name
.
name
takes following tokens that are not nested in braces for its first argument and following tokens nested in braces for its second argument and applies csname..endcsname
to its second argument.
The tokens that form the first argument can, e.g., be: globallongouterdef
or newcommand
or string
or meaning
or whatever.
You can also leave the first argument empty.
Examples:
nameshow{foo}
yields: showfoo
namedef{foo}...
yields: deffoo...
namegloballongouterdef{foo}...
yields: globallongouterdeffoo...
namenewcommand{foo}...
yields: newcommandfoo...
name{foo}...
yields: foo
namenamegloballet{foo}={bar}
yields:namegloballetfoo={bar}
yields:globalletfoo=bar
documentclass{article}
newcommandUDExchange[2]{#2#1}
newcommandname{}longdefname#1#{romannumeral0innername{#1}}%
newcommandinnername[2]{%
expandafterUDExchangeexpandafter{csname#2endcsname}{ #1}%
}%
makeatletter
newcommandnovalueprovided{nfss@text{reset@fontbfseries??}}
makeatother
newcommandDefineField[2][novalueprovided]{%
namenewcommand{#2}[1]{namegdef{@#2}{##1}}%
namenewcommand{@#2}{}%
name{#2}{#1}%
}%
newcommandUseField[1]{name{@#1}}%
DefineField[initial value of field Alice]{Alice}
DefineField{Bob}
begin{document}
{ttfrenchspacing
namestring{Alice}: namemeaning{Alice}
namestring{@Alice}: namemeaning{@Alice}
namestring{Bob}: namemeaning{Bob}
namestring{@Bob}: namemeaning{@Bob}
verb|UseField{Alice}:| UseField{Alice}
verb|UseField{Bob}:| UseField{Bob}
}
hrulefill
Now the fields are changed:
Alice{My name is Alice}
Bob{My name is Bob}
{ttfrenchspacing
namestring{Alice}: namemeaning{Alice}
namestring{@Alice}: namemeaning{@Alice}
namestring{Bob}: namemeaning{Bob}
namestring{@Bob}: namemeaning{@Bob}
verb|UseField{Alice}:| UseField{Alice}
verb|UseField{Bob}:| UseField{Bob}
}
end{document}
If you provide more info about what you try to achieve in practice, an interface could be developed which also does better error-management.
In the other answers the spurious space within the csname..endcsname
-expression is already pointed out.
In case you wish to avoid typing so many csname..endcsname
-expressions, I can offer a macro name
.
name
takes following tokens that are not nested in braces for its first argument and following tokens nested in braces for its second argument and applies csname..endcsname
to its second argument.
The tokens that form the first argument can, e.g., be: globallongouterdef
or newcommand
or string
or meaning
or whatever.
You can also leave the first argument empty.
Examples:
nameshow{foo}
yields: showfoo
namedef{foo}...
yields: deffoo...
namegloballongouterdef{foo}...
yields: globallongouterdeffoo...
namenewcommand{foo}...
yields: newcommandfoo...
name{foo}...
yields: foo
namenamegloballet{foo}={bar}
yields:namegloballetfoo={bar}
yields:globalletfoo=bar
documentclass{article}
newcommandUDExchange[2]{#2#1}
newcommandname{}longdefname#1#{romannumeral0innername{#1}}%
newcommandinnername[2]{%
expandafterUDExchangeexpandafter{csname#2endcsname}{ #1}%
}%
makeatletter
newcommandnovalueprovided{nfss@text{reset@fontbfseries??}}
makeatother
newcommandDefineField[2][novalueprovided]{%
namenewcommand{#2}[1]{namegdef{@#2}{##1}}%
namenewcommand{@#2}{}%
name{#2}{#1}%
}%
newcommandUseField[1]{name{@#1}}%
DefineField[initial value of field Alice]{Alice}
DefineField{Bob}
begin{document}
{ttfrenchspacing
namestring{Alice}: namemeaning{Alice}
namestring{@Alice}: namemeaning{@Alice}
namestring{Bob}: namemeaning{Bob}
namestring{@Bob}: namemeaning{@Bob}
verb|UseField{Alice}:| UseField{Alice}
verb|UseField{Bob}:| UseField{Bob}
}
hrulefill
Now the fields are changed:
Alice{My name is Alice}
Bob{My name is Bob}
{ttfrenchspacing
namestring{Alice}: namemeaning{Alice}
namestring{@Alice}: namemeaning{@Alice}
namestring{Bob}: namemeaning{Bob}
namestring{@Bob}: namemeaning{@Bob}
verb|UseField{Alice}:| UseField{Alice}
verb|UseField{Bob}:| UseField{Bob}
}
end{document}
If you provide more info about what you try to achieve in practice, an interface could be developed which also does better error-management.
edited Jan 27 at 14:42
answered Jan 27 at 14:14
Ulrich DiezUlrich Diez
4,865618
4,865618
add a comment |
add a comment |
Thanks for contributing an answer to TeX - LaTeX Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f472068%2fmacro-defining-macro-defining-global-macro-similar-to-author-or-title-fie%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Remove the space after the
#1
ingdefcsname @#1 endcsname{##1}%
, it should begdefcsname @#1endcsname{##1}%
– moewe
Jan 27 at 11:00
Thank you very much, I don't know why I missed it.
– andywiecko
Jan 27 at 11:05