3

When using normal macro names I can set the value of a macro as

\newcommand*{\SetMacro}[1]{\def#1{42}}

\SetMacro{\Answer}

and \Answer will have the value of 42. Is there an etoolbox way of invoking \SetMacro. Something like

\SetMacro{\csuse{Answer for Section Y}}%% <--- This is incorrect

without changing the definition of \SetMacro?

Code below produces:

enter image description here

If there is not an etoolbox way, what is the equivalent \expandafter magic that needs to be used to invoke \SetMacro

Notes:

  • One way to solve this is to define \SetMacro* as shown below. Was wondering if there was a way to not require a modification of \SetMacro.

Code:

\documentclass{article}
\usepackage{etoolbox}

\newcommand*{\SetMacro}[1]{%
    %% #1 macro name to be set
    \def#1{42}% After much calculations in this macro
}

\begin{document}
\textbf{Using macro}:
\SetMacro{\Answer}
Answer is \Answer.

\par\medskip
\textbf{Not using macro (with csname)}:
\csdef{Answer for Section X}{42}
Answer is \csuse{Answer for Section X}.

\par\medskip
\textbf{Using macro (with csname)}:
%\SetMacro{\??????{Answer for Section Y}}%% <--- How to do this???
Answer is \csuse{Answer for Section Y}.
\end{document}

Solution (using modified \SetMacro)

\documentclass{article}
\usepackage{etoolbox}
\usepackage{xparse}

\NewDocumentCommand{\SetMacro}{%
    s% 
    m% macro or control sequence name
}{%
    \IfBooleanTF{#1}{%
        \csdef{#2}{42}% After much calculations in this macro
    }{%
        \def#2{42}% After much calculations in this macro
    }%
}%

\begin{document}
\textbf{Using macro}:
\SetMacro{\Answer}
Answer is \Answer.

\par\medskip
\textbf{Not using macro (with csname)}:
\csdef{Answer for Section X}{42}
Answer is \csuse{Answer for Section X}.

\par\medskip
\textbf{Using macro (with csname)}:
\SetMacro*{Answer for Section Y}%% <--- One possible way
Answer is \csuse{Answer for Section Y}.
\end{document}
  • But I think you should clarify what you want, why can't you change \SetMacro or may be if it's possible to use another interface. – Manuel May 22 '18 at 20:01
  • \newcommand\SetMacroName[1]{\csdef{#1}{42}}, but I'm not certain what you have in mind. – egreg May 22 '18 at 20:03
  • @Manuel: Yes, I know how to define \SetMacro* to accommodate the control sequence version. Was wondering if there was some other way. – Peter Grill May 22 '18 at 20:04
  • @egreg: Added possible solution that required definition of \SetMacro*. Was trying to see if there was a way to not rehire that modificatuion. Hope that clarifies. I think perhaps that is what you meant anyway, – Peter Grill May 22 '18 at 20:12
  • In your last edit, you can leave the {42} out of the \IfBooleanTF so that you avoid repetition. – Manuel May 22 '18 at 20:13
2

For that exact situation you asked you can do \SetMacro{\x{}\csdef{Answer for Section Y}}.

Since \SetMacro does \def#1{42} we put #1 = \x{} which ends that \def and then add \csdef{Answer for Section Y} which is followed by {42}. That would leave \def\x{}\csdef{Answer for Section Y}{42}. So you are making the first \def to do something harmless and then call your own \csdef.

And if you allow \expandafter you can do

\def\usecsinsteadofthetokenitself#1{\x{}\csdef{#1}}

and then \expandafter\SetMacro\expandafter{\usecsinsteadofthetokenitself{Answer for Section Y}} to output the same as before.

But this is for “recreational purposes”, for sure there must be another solution to whatever your problem is.

  • This works pretty well and does not require modification to \SetMacro. – Peter Grill May 22 '18 at 20:18
1

Perhaps this, but there is something mysterious about what your aim is.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\SetMacro}{m}
 {
  \bool_lazy_and:nnTF
   {
    \tl_if_single_p:n { #1 }
   }
   {
    \token_if_cs_p:N #1
   }
   { \cs_new:Npn #1 } { \cs_new:cpn { #1 } } { 42 }
 }

\NewExpandableDocumentCommand{\use}{m}{\use:c { #1 }}

\ExplSyntaxOff

\begin{document}

\SetMacro{\test}
\SetMacro{This is another test}
\SetMacro{z}

\test

\use{This is another test}

\use{z}

\end{document}

This prints 42 three times.

The test returns true if the argument is a single token which is a control sequence. Of course, silly input such as \SetMacro{a\bc} will fail.

  • \SetMacro{a\bc} won't fail if \bc is defined, I don't think that's a problem. May be what you wanted to mention is that if one has \def\bc{Answer for whatever} and wants to do \SetMacro{Answer for whatever} he can't expect \SetMacro{\bc} to work as intended. – Manuel May 22 '18 at 20:48
1
\documentclass{article}
\usepackage{etoolbox}
\newcommand*{\SetMacro}[1]{\testtype#1\endtt\expandafter\def\argname{42}}
\def\testtype#1#2\endtt{%
  \ifx\csname#1%
    \expandafter\def\expandafter\argname\expandafter{#1#2}\else%
    \ifx\csuse#1%
      \expandafter\def\expandafter\argname\expandafter{\csname#2\endcsname}%
    \else%
      \def\argname{#1#2}%
    \fi%
  \fi}
\begin{document}
\SetMacro{\Answer} \Answer

\SetMacro{\csname A3\endcsname} \csname A3\endcsname

\SetMacro{\csuse{B21}} \csuse{B21}
\end{document}

enter image description here

To make it more interesting, \SetMacro can take 2 arguments, where the 2nd is what #1 is defined to:

\documentclass{article}
\usepackage{etoolbox}
\newcommand*{\SetMacro}[2]{\testtype#1\endtt\expandafter\def\argname{#2}}
\def\testtype#1#2\endtt{%
  \ifx\csname#1%
    \expandafter\def\expandafter\argname\expandafter{#1#2}\else%
    \ifx\csuse#1%
      \expandafter\def\expandafter\argname\expandafter{\csname#2\endcsname}%
    \else%
      \def\argname{#1#2}%
    \fi%
  \fi}
\begin{document}
\SetMacro{\Answer}{41} \Answer

\SetMacro{\csname A3\endcsname}{42} \csname A3\endcsname

\SetMacro{\csuse{B21}}{43} \csuse{B21}
\end{document}

enter image description here

0

A variant of Steven B. Segletes' approach where \romannumeral-expansion is used instead of defining the scratch-macro \argname.

\documentclass{article}
\usepackage{etoolbox}
\newcommand*{\SetMacro}[1]{\expandafter\def\romannumeral0\testtype#1\SetMacro}
\begingroup
\makeatletter
\@firstofone{%
  \endgroup
  \def\testtype#1#2\SetMacro{%
    \ifx\csname#1\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
    {\expandafter\expandafter\@firstofone{ #1#2}}{%
      \ifx\csuse#1\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
      {\expandafter\expandafter\@firstofone{ \csname#2\endcsname}}{ #1#2}%
    }%
  }%
}%
\begin{document}
\SetMacro{\Answer}{41} \Answer

\SetMacro{\csname A3\endcsname}{42} \csname A3\endcsname

\SetMacro{\csuse{B21}}{43} \csuse{B21}
\end{document}

enter image description here

0

An approach where (only) a single \expandafter-chain "going into the first argument" of \SetMacro is needed:

\documentclass{article}
\usepackage{etoolbox}

%%===============================================================================
%% Define a mechanism where in expansion-contexts a single "hit" by \expandafter
%% triggers K "hits" by \expandafter.
%%------------------------------------------------------------------------------- 
%% Syntax of that mechanism is:
%% 
%% \romannumeral\Expandtimes{<number K>}<token sequence> 
%% ->
%% K times the leading token of <token sequence> will be "hit" by \expandafter .
%% 
%% In expansion contexts the leading \romannumeral being "hit" by one 
%% \expandafter is sufficient for obtaining these K "hits" by \expandafter
%% on the leading token of <token sequence>.
%%
%% In expansion contexts instead of several \expandafter-chains going
%% to the leading token of <token sequence>, you need only one 
% \expandafter-chain going to the token \romannumeral.
%%
\begingroup
\makeatletter
\@firstofone{%
  \endgroup
  \newcommand\innerdfork{}\def\innerdfork#1d#2#3dd{#2}%
  \newcommand*\dfork[1]{\innerdfork#1{\@firstoftwo}d{\@secondoftwo}dd}%
  \newcommand*\Expandtimes[1]{%
    0\expandafter\innerExp
     \expandafter{%
     \expandafter}%
     \romannumeral\number\number#1 000d%
  }%
  \newcommand*\innerExp[2]{\dfork{#2}{#1 }{\innerExp{#1#1\expandafter}}}%
}%


\newcommand*{\SetMacro}[1]{\def#1}%

\parskip=\bigskipamount
\parindent=0ex

\begin{document}

\SetMacro{\Answer}{42}%
\verb|\SetMacro{\Answer}{42}|\\
\(\to\)\\
\texttt{\string\Answer:~\meaning\Answer}

With \verb|\csname..\endcsname| one expansion-step is required for obtaining the control-sequence-token. Thus:

\expandafter\SetMacro\expandafter{\csname Answer\endcsname}{43}%
\verb|\expandafter\SetMacro\expandafter{\csname Answer\endcsname}{43}|\\
\(\to\)\\
\texttt{\string\Answer:~\meaning\Answer}


With \verb|\csuse{...}| three expansion-steps are required for obtaining the control-sequence-token. Thus:

\expandafter\SetMacro\expandafter{\romannumeral\Expandtimes{3}\csuse{Answer}}{44}%
\verb|\expandafter\SetMacro\expandafter{\romannumeral\Expandtimes{3}\csuse{Answer}}{44}|\\
\(\to\)\\
\texttt{\string\Answer:~\meaning\Answer}

\end{document}

enter image description here

0

I don't have an etoolbox-way.

But I have a solution where "backstage" a combination of

  • a nice opening-brace delimited argument,
  • the exhilarating \expandafter-thingie,
  • the bewildering \csname..\endcsname-thingie
  • and the flabbergasting exchange of undelimited/brace-nested macro arguments

comes in very handy for avoiding redefining \SetMacro/for avoiding changing the ⟨replacement text⟩ of \SetMacro. ;-)


By applying the #{-notation, you can define macros whose last argument is delimited by an opening brace. Unlike with other argument delimiters that get removed when gathering arguments, TeX will leave a delimiting opening brace in place.
(Actually the mechanism isn't restricted to opening brace character tokens. You can use any token whose category code is 1 at definition time. Could as well be #\WeIrd after \let\WeIrd={ .)
Delimited arguments can be empty.

Implement a macro \name

You can – by applying the #{-notation – implement a macro \name with the following syntax:

\name <set of preceding non brace tokens>{<name of control sequence token>}
→ 
<set of preceding non brace tokens><control sequence token>

(<name of control sequence token> can contain space-tokens as well.
 <set of preceding non brace tokens> can be empty.)

E.g., with  <set of preceding non brace tokens> = `\newcommand*`
      and <name of control sequence token> = `foo␣bar`:

      \name\newcommand*{foo␣bar} → \newcommand*\foo␣bar

E.g., with  <set of preceding non brace tokens> = <emptiness>
      and <name of control sequence token> = `foo␣bar`:

      \name{foo␣bar} → \foo␣bar

Have \name fetch the set of non brace tokens that shall precede the control sequence in question as an argument that is delimited by an opening brace, and have \name pass this set of tokens wrapped into a pair of braces to an internal macro which in turn processes two undelimited/brace nested arguments whereof the first one contains this set of non brace tokens that later shall precede the control sequence token in question, and whereof the second one contains the set of character tokens denoting the name of the control sequence token in question.

That internal macro in turn

  • exchanges the two arguments (resulting in the argument holding the name of the control sequence token in question being the first argument and the argument holding the set of non brace tokens that should precede the control sequence token in question being the second argument) and
  • via an \expandafter-chain has (La)TeX apply \csname..\endcsname to the content of the (now) first argument before having (La)TeX revert the exchanging of these two arguments while removing the surrounding braces from these two arguments.

Here is the code:

\newcommand\name{}%
\long\def\name#1#{\romannumeral0\UDinnername{#1}}%
\newcommand\UDinnername[2]{%
  \expandafter\UDexchange\expandafter{\csname#2\endcsname}{ #1}%
}%
\newcommand\UDexchange[2]{#2#1}%

Example exhibiting the temporal course of macro expansion:

\name foo{bar} 

→ expansion step 1 yields:

\romannumeral0\UDinnername{foo}{bar}  

→ expansion step 2 yields:

% \romannumeral-expansion in progress
0\UDinnername{foo}{bar}  

\romannumeral-expansion consumes the zero and keeps expanding.

% \romannumeral-expansion in progress
\UDinnername{foo}{bar}  

\romannumeral-expansion step 1 yields:

% \romannumeral-expansion in progress
\expandafter\UDexchange\expandafter{\csname bar\endcsname}{ foo}

\romannumeral-expansion step 2 via \expandafter-chain triggers \csname-expansion and thus yields:

% \romannumeral-expansion in progress
\UDexchange{\bar}{ foo}

\romannumeral-expansion step 3 yields:

% \romannumeral-expansion in progress
 <space token>foo\bar

\romannumeral-expansion finds the <space token>, thus stops gathering digits and hereby expanding tokens while having gathered the digit 0 followed by a space-token terminating the number while not delivering any tokens when finding non-positive numbers.

Thus in the end you have:

% \romannumeral-expansion terminated:
foo\bar

Usage-examples for the macro \name:

\name{foo}\foo

\name\newcommand{foo}\newcommand\foo

\name\DeclareRobustCommand{foo}\DeclareRobustCommand\foo

\name\global\long\outer\def{foo}\global\long\outer\def\foo

\name\expandafter{foo}\bar\expandafter\foo\bar

\name\expandafter\foo{bar}\expandafter\foo\bar

\name\let{foo}=\bar\let\foo=\bar

\name\string{foo}\string\foo

\name\meaning{foo}\meaning\foo

You can as well use such a macro for defining/calling macros whose names contain spaces:

\name{foo }\foo␣

\name\newcommand{foo }\newcommand\foo␣

\name\DeclareRobustCommand{foo }\DeclareRobustCommand\foo␣

\name\global\long\outer\def{foo }\global\long\outer\def\foo␣

\name\expandafter{foo }\bar\expandafter\foo␣\bar

\name\let{foo }=\bar\let\foo␣=\bar

\name\string{foo }\string\foo␣

\name\meaning{foo }\meaning\foo␣

You can also nest the calls of \name:

Example 1:

\name\name\expandafter{f o o }{b a r }

Processing the first \name yields:

\name\expandafter\f␣o␣o␣{b a r }

Processing the second \name yields:

\expandafter\f␣o␣o␣\b␣a␣r␣

(Analogously: \name\name\let{f o o }={b a r }\let\f␣o␣o␣=\b␣a␣r␣)

Example 2:

\name\name\name\expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }

Processing the first \name yields:

\name\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter{b a r }{c r a z y }

Processing the second \name yields:

\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣{c r a z y }

Processing the third \name yields:

\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣

Why do I think that the \name-macro might be of interest to you?

You have defined

\newcommand*{\SetMacro}[1]{\def#1{42}}

, and you wish to leave that definition untouched.

Another usage-example of the \name-macro can be:

\name\SetMacro{Answer for Section Y}

This yields:

\SetMacro\Answer␣for␣Section␣Y

This in turn yields:

\def\Answer␣for␣Section␣Y{42}

You see:

In the input just prepend \name to the call of \SetMacro.
Changing the definition of \SetMacro is not necessary.

Usage of primitives like \expandafter and \csname, which seem to frighten many people, is hidden from the user.


You can also do:

\name{Answer for Section Y}

This yields:

\Answer␣for␣Section␣Y

With the definition above this in turn yields:

42

By the way:

If you don't want the <replacement-texts> of macros defined via \SetMacro to be "hard-coded" within the definition of \SetMacro, just don't have them hard-coded there. Instead always have them supplied as \SetMacro's second argument ;-)  :

\newcommand*\SetMacro[1]{\def#1}

With that definition of \SetMacro, the sequence

\name\SetMacro{Answer for Section Y}{42}

yields:

\SetMacro\Answer␣for␣Section␣Y{42}

This in turn yields:

\def\Answer␣for␣Section␣Y{42}

With this definition you can, e.g., also do:

\name\SetMacro{Answer for Section Z}{43}

, yielding:

\SetMacro\Answer␣for␣Section␣Z{43}

, yielding:

\def\Answer␣for␣Section␣Z{43}

I hope this helps.

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.