7

I have some macros that expand to some text but when used together they should expand to something completely different. In this example:

  • \foo -> x
  • \bar -> y
  • \foo\bar -> Hello World

Doing it with \if... constructions works in principle, but requires a lot of typing, so I tried it with \csname, but couldn't get it to work:

\documentclass{article}

\newcounter{mycounter}

\newcommand\ifalternatetext[2]{\ifnumcomp{\value{mycounter}}{>}{0}{\addtocounter{mycounter}{-1}#1}{#2}}

\def\foo{\addtocounter{mycounter}{1}x}
\def\bar{\ifalternatetext{y}{z}}

\def\bazxy{Hello World}

\newcommand\print[2]{\csname baz#1#2\endcsname{}}

\begin{document}
This works: `\print{x}{y}'

This doesn't: `\print{\foo}{\bar}'
\end{document}
  • 1
    Omit the {} after \foo{} and \bar{} – user31729 Dec 28 '14 at 10:22
  • Works for the example as originally posted, but not for the real document, modified the example to reflect this. – user66554 Dec 28 '14 at 11:50
  • 1
    I don't see how is \emph a good thing to put inside \csname. What do you really want? – yo' Dec 28 '14 at 12:05
  • Right. That was a case of "cutting away too much on the way to the MWE", using \emph instead of the counter stuff I actually have there. Causes the same message (and what I really want is that message not appearing anymore), but is confusing to the reader. Modified it accordingly. – user66554 Dec 28 '14 at 12:49
  • @user66554: My solution worked right out of the box... if you changed the question/code it's quite natural that in most cases a provided solution will not work any longer. What do you expect me do now? – user31729 Dec 28 '14 at 13:20
7

The {} brackets after \foo and \bar prevent the expansion of the the macros in the OP's second call to \print.

\documentclass{article}

\def\foo{x}
\def\bar{y}

\def\bazxy{Hello World}

\newcommand\print[2]{\csname baz#1#2\endcsname{}}

\begin{document}
This works: `\print{x}{y}'

This also works: `\print{\foo}{\bar}'
\end{document}
  • +1 Yes, for the version as originally posted - but that one did nothing instead of complaining missing \endcsname as in my original document. I managed to reproduce the behaviour by adding \emph{}, but now your solution doesn't work anymore. And what would I have done if \foo and \bar had taken arguments and should not have been followed by a space? (they don't, though the macros called within them do.) – user66554 Dec 28 '14 at 11:49
  • @user66554: I will look later on – user31729 Dec 28 '14 at 12:16
6

You can't do assignments inside \csname...\endcsname. A good reference is What exactly do \csname and \endcsname do? and my answer to it where you find

It's possible to give many other interesting uses of this trick. But one should always keep in mind that TeX does complete expansion of what it finds in that context and that only characters must remain.

Since \addtocounter{mycounter}{1} relies on eventually doing \global\advance\c@mycounter by 1 it is not allowed in \csname...\endcsname.

If all you want is to have \foo behaving differently if followed by \bar or not, use \@ifnextchar:

\makeatletter
\newcommand{\foo}{\@ifnextchar\baz{\foobaz\@gobble}{x}}
\newcommand{\baz}{y}
\newcommand{\foobaz}{Hello world}
\makeatother

Note that \@gobble will remove the superfluous \baz token.

Complete example

\documentclass{article}

\makeatletter
\newcommand{\foo}{\@ifnextchar\baz{\foobaz\@gobble}{x}}
\newcommand{\baz}{y}
\newcommand{\foobaz}{Hello world}
\makeatother

\begin{document}
Separate: \foo{} and \baz.

Contiguous: \foo\baz
\end{document}

enter image description here

Here's a different approach with your \print macro:

\documentclass{article}
\usepackage{etoolbox}

\makeatletter
\providecommand\@secondofthree[3]{#2}
\providecommand\@thirdofthree[3]{#3}

\newcommand{\foo}{x\expandafter\@secondofthree}
\newcommand{\fuu}{T\expandafter\@secondofthree}
\newcommand{\baz}{\@secondoftwo{y}{z}}
\makeatother

\newcommand\bazz{Normal}
\newcommand\bazTy{Ops}
\newcommand\bazxy{Hello World}

\newcommand\print[2]{\csname baz#1#2\endcsname}

\begin{document}

`\print{\foo}{\baz}'

`\print{\fuu}{\baz}'

`\print{}{\baz}'

\end{document}

enter image description here

The macro \print{\foo}{\baz} becomes

\csname baz\foo\baz\endcsname

and, expanding \foo.

\csname bazx\expandafter\@secondofthree\baz\endcsname

Now \expandafter causes the expansion of \baz and disappears

\csname bazx\@secondofthree\@secondoftwo{y}{z}\endcsname

where the expansion of \@secondofthree selects y, giving

\csname bazxy\endcsname

When \print{}{\baz} is used, we have

\csname baz\@secondoftwo{y}{z}\endcsname

and \@secondoftwo selects z, giving \csname bazz\endcsname.

One can install an abstraction layer that eventually does the same, but makes definitions easier:

\documentclass{article}
\usepackage{etoolbox}

\makeatletter
\providecommand\@secondofthree[3]{#2}
\providecommand\@thirdofthree[3]{#3}

\newcommand{\newprefixcommand}[2]{%
  \newcommand{#1}{#2\expandafter\@secondofthree}%
}
\newcommand{\newalternatecommand}[3]{%
  \newcommand{#1}{\@secondoftwo{#2}{#3}}%
}
\makeatother

\newcommand\print[2]{\csname baz#1#2\endcsname}

\newprefixcommand{\foo}{x}
\newprefixcommand{\fuu}{T}
\newalternatecommand{\baz}{y}{z}

\newcommand\bazz{Normal}
\newcommand\bazTy{Ops}
\newcommand\bazxy{Hello World}

\begin{document}

`\print{\foo}{\baz}'

`\print{\fuu}{\baz}'

`\print{}{\baz}'

\end{document}
  • There are several dozen foos and bars each, making the \if...-based approach rather cumbersome. I thought I could use \let or something to evaluate \foo\bar to make the assignments and assign the result to a new, temporary macro containing no assignments which then could be used in \csname, but that didn't work. – user66554 Dec 28 '14 at 13:56
  • @user66554 You have better ask a new question, stating precisely your aim, with a sensible minimal example. – egreg Dec 28 '14 at 13:58
  • @user66554 I added a different approach with your \print macro. – egreg Dec 28 '14 at 14:35
0

Instead of putting \foo and \bar directly into the \csname I now have \foo and \bar assign a temporary macro which then can be used within \csname:

\documentclass{article}
\usepackage{etoolbox}

\newcounter{@mycounter}

\makeatletter

\def\@foo@out{}
\def\@bar@out{}

\newcommand\ifalternatetext[2]{%
    \ifnumcomp{\value{@mycounter}}{>}{0}{%
    \addtocounter{@mycounter}{-1}%
    \gdef\@bar@out{#1}%
    }{%
    \gdef\@bar@out{#2}%
    }}

\newcommand\@foohelper[1]{%
    \addtocounter{@mycounter}{1}%
    \gdef\@foo@out{#1}%
    }

\def\foo{\@foohelper{x}}
\def\bar{\ifalternatetext{y}{z}}

\def\bazxy{Hello World}

\newcommand\print[2]{#1#2\csname baz\@foo@out\@bar@out\endcsname{}}

\makeatother

\begin{document}
`\print{\foo}{\bar}'
\end{document}

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.