How to avoid defining something inside of a macro that uses '\csname \endcsname'?

by Bob Vesterman   Last Updated July 12, 2019 09:23 AM - source

The following code gives no error and works fine:

\documentclass{memoir}

\def\ThingyFred {This is the thingy named Fred}

\begin{document}
    \chapter{Fred}
    \ThingyFred
\end{document}

And the following code gives an error

"Undefined control sequence. \ThingyJoe",

which is what I would expect and hope it would do:

\documentclass{memoir}

\def\ThingyFred {This is the thingy named Fred}

\begin{document}
    \chapter{Fred}
    \ThingyJoe
\end{document}

But for various reasons, I want (in the main body of the document) to refer to things like \Thingy{Fred}, not \ThingyFred. The following works fine:

\documentclass{memoir}

\def\RawThingyFred {This is the thingy named Fred}

\def\Thingy#1 {\csname RawThingy#1\endcsname}

\begin{document}
    \chapter{Fred}
    \Thingy{Fred}
\end{document}

But, contrary to my naive expectations and hopes, the following code does not give an error (it instead treats \Thingy{Joe} as an empty variable):

\documentclass{memoir}

\def\RawThingyFred {This is the thingy named Fred}

\def\Thingy#1 {\csname RawThingy#1\endcsname}

\begin{document}
    \chapter{Fred}
    \Thingy{Joe}
\end{document}

After investigating a little more, it seems to me the problem is, when you do a \Thingy{Joe}, the \def\Thingy#1 \csname RawThingy#1\endcsname does not merely translate to \RawThingyJoe, but additionally defines \RawThingyJoe. For example, the following returns the error, I expect:

\documentclass{memoir}

\def\RawThingyFred {This is the thingy named Fred}

\def\Thingy#1 {\csname RawThingy#1\endcsname}

\begin{document}
    \chapter{Fred}
    \RawThingyJoe
    \Thingy{Joe}
\end{document}

But the following does not get an error:

\documentclass{memoir}

\def\RawThingyFred {This is the thingy named Fred}

\def\Thingy#1 {\csname RawThingy#1\endcsname}

\begin{document}
    \chapter{Fred}
    \Thingy{Joe}
    \RawThingyJoe
\end{document}

Apparently, in the first case, it sees \RawThingyJoe and has no idea what that means, whereas in the second case it sees \Thingy{Joe}, decides that \RawThingyJoe therefore means an empty string, and then sees \RawThingyJoe, which it interprets as an empty string.

Is there a way to take the "define a macro that takes a parameter" route, but still get the error-checking I had expected?

Tags : macros errors


Answers 1


As egreg wrote, \csname ... \endcsname defines the control sequence token to be \let-equivalent to \relax if it isn't already defined (you can think of it as a side effect of \csname). With e-TeX extensions, you can use \ifcsname to test whether a control sequence is defined, without defining it (no side effect):

\documentclass{article}

\newcommand*{\RawThingyFred}{This is the thingy named Fred}

\newcommand*{\Thingy}[1]{%
  \ifcsname RawThingy#1\endcsname
    \csname RawThingy#1\endcsname
  \else
    \errmessage{\string\RawThingy#1 is undefined}%
  \fi
}

\begin{document}

\Thingy{Fred}% okay
\Thingy{Joe}% prints \RawThingyJoe is undefined.

\end{document}

You can see other techniques for this kind of test here.

P.S.: I changed your \defs to \newcommand*, first because \newcommand and \newcommand* ensure you don't accidentally overwrite an existing macro, second because your parameter text had a probably unwanted space (for instance, after #1 in \def\Thingy#1 {...}).

frougon
frougon
July 12, 2019 08:55 AM

Related Questions


A macro that processes and hides text?

Updated June 21, 2016 08:09 AM

Too many }'s in tex file

Updated August 10, 2015 17:10 PM

cannot get a navigation newcommand correct

Updated May 12, 2016 08:09 AM