4

I have created a command which creates another command. The parameter of the first command is a variable which is used in the second command. I would like the second command returns the value of the variable at the creation of the command.

\newcommand\testcom[2]{
    \texttt{newcommand #1 : #2}
    \expandafter\newcommand\csname#1\endcsname{#2}
}

\def \test{1} % set 1 to variable 'test'
\testcom{foofoo}{\test} % create command 'foofoo' and display "newcommand foofoo:1" 

\def \test{2} % set 2 to variable 'test'
\testcom{barfoo}{\test} % create command 'barfoo' and display "newcommand barfoo:2"

\def \test{3} % set 3 to variable 'test'

\foofoo % display '3', excepted '1'

\barfoo % display '3', excepted '2'

\barfoo % display '3', excepted '2'

I understand the command \testcom{barfoo}{\test} execute \newcommand\barfoo{\test} but I has excepted \newcommand\barfoo{3}.

Next, I have modified the command to set an intermediate variable with \edef.

\newcommand\testcom[2]{
    \texttt{newcommand #1 : #2}
    \expandafter\edef\csname test@#1 \endcsname{#2}
    \expandafter\newcommand\csname#1\endcsname{\csname test@#1 \endcsname}
}

Now, it creates a variable \test@barfoo with the value 2 and the command \barfoo returns this variable.

I would like know if a smartest method exists. In my context, the second newcommand is more complex and \expandafter\edef\csname#1\endcsname{#2} is not sufficient to replace the newcommand.

2
  • if the argument is \test defined to be 3 you want to define your command to 3, but what do you want if the argument is not expandable \sqrt[2]{4} for example? Jun 1 at 14:57
  • The \def, \edef, etc. are always sufficient to replace \newcommand because \newcommand is nothing more than \def. \newcommand is only a macro with more obscure syntax without possibility to declare separated parameters and with checking if the defined control sequence is unknown.
    – wipet
    Jun 2 at 3:35
4

Since both \foofoo and \barfoo are just defined to be \test, they will change their output if you change \test (as you've demonstrated). Also, there's no need to create an intermediate \test@<cmd>. You can just expand the argument using \edef:

enter image description here

\documentclass{article}

\newcommand\testcom[2]{%
  \texttt{newcommand #1 : #2}%
  \expandafter\edef\csname#1\endcsname{#2}%
}

\begin{document}

\def\test{1} % set 1 to variable 'test'
\testcom{foofoo}{\test} % create command 'foofoo' and display "newcommand foofoo:1" 

\def\test{2} % set 2 to variable 'test'
\testcom{barfoo}{\test} % create command 'barfoo' and display "newcommand barfoo:2"

\def\test{3} % set 3 to variable 'test'

\ttfamily
\string\foofoo: \foofoo % display '1', excepted '1'

\string\barfoo: \barfoo % display '2', excepted '2'

\end{document}
1
  • Ulrich Diez tends to increase confusion of beginners by mentioning too many subtle details at once.Despite this things might be clearer by mentioning that Werner's approach with \edef is focused on obtaining the "total expansion" of \test as definition-text of the macro to be defined via \testcom while U's approach is focused on "toplevel expansion" only. I.e., U's approach just expands \test and from the resulting tokens defines the macro while with Werner's approach expandable tokens coming from expanding (expandable tokens coming from expanding [etc]) \test get expanded also. Jun 3 at 13:21
4

As you did not yet answer the question in David Carlisle's comment I assume that as \testcom's second argument you always provide a single expandable token (an argumentless macro serving as "variable") and you always wish a macro, whose name is provided in \testcom's first argument, to be defined from the toplevel-expansion of that token:

\documentclass{article}
\newcommand\testcom[2]{%
  \texttt{newcommand #1 : \detokenize\expandafter{#2}}%
  \expandafter\newcommand\expandafter*\csname#1\expandafter\endcsname\expandafter{#2}%
}
\begin{document}
\def \test{1} % set 1 to variable 'test'
\testcom{foofoo}{\test} % create command 'foofoo' and display "newcommand foofoo : 1" 

\def \test{2} % set 2 to variable 'test'
\testcom{barfoo}{\test} % create command 'barfoo' and display "newcommand barfoo : 2"

\def\woozle{X}%
\def \test{\woozle}
\testcom{barbar}{\test} % create command 'barbar' and display "newcommand barbar : \woozle"

\def \test{3} % set 3 to variable 'test'

\ttfamily

\string\foofoo=\meaning\foofoo % display '1', excepted '1'

\string\barfoo=\meaning\barfoo % display '2', excepted '2'

\string\barbar=\meaning\barbar % display '\woozle', excepted '\woozle'
\end{document}

enter image description here

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.