Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I'm trying to do the same as this one, but with \def, \gdef or \xdef instead.

Very quick summary of the other question:
I wanted to print the command (not the contents of the command) regardless of it being defined by \csname or not. Solution was to check whether the first string is \csname or the command.

So \def needs an \expandafter if used with \csname. If not then everything is ok:

\def\str{String}
\expandafter\def\csname str\endcsname{String}

Both are correct. But if I wanted to change \str or \csname str\endcsname with an argument (#1). Now it's not known whether I need to use \expandafter or not. If used when not needed, it results in an error. I wrote this (in analogy with the other question)

\def\defcmd#1{
  \ifx\csname#1
    \expandafter\def\csname
  \else
    \def#1
  \fi
}

\defcmd\str{String}
\defcmd\csname str\endcsname{String}

This will absorb the first string after \defcmd as argument (#1), check whether it's \csname or anything else and will then replace itself either with \expandafter\def or \def and place the argument back.
When running that code, no problem at all. But when I try to output the command (either directly or in a file) I get the following error:

! Use of \str doesn't match its definition
l.24 

And I have no clue why that is. (l.24 is one line after the use of \str)

share|improve this question
    
you didn't put in the extra expandafter: I said in comments in the other question that it is confusing if you don't:-) –  David Carlisle Jul 24 '12 at 13:14
add comment

2 Answers

up vote 8 down vote accepted

Let's see what happens in the two cases.

\defcmd\str{foo}

This becomes

\ifx\csname\str\expandafter\def\csname\else\def\str\fi

and the test is false, so what remains in the input stream is

\def\str\fi{foo}

which is perfectly acceptable for TeX, but has two issues: the token \str must be followed by \fi and there will also be an error about a forgotten \fi

\defcmd\csname str\endcsname{foo}

This becomes

\ifx\csname\csname\expandafter\def\csname\else\def\csname\fi str\endcsname{foo}

and TeX will leave in the input stream

\expandafter\def\csname\else\def\csname\fi str\endcsname{foo}

Now \expandafter expands \csname which will get rid of what is between \else and \fi, resulting in

\def\str{foo}

as required.

Solution

We have to get rid of the \fi before \def is executed. One way has been shown by David. Alternatively:

\makeatletter
\def\defcmd#1{%
  \ifx\csname#1%
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\expandafter\def\csname}{\def#1}%
}
\makeatother

In the case of \def\str{foo} we have

\ifx\csname\str

which is false, so what remains is

\expandafter\@secondoftwo\fi{\expandafter\def\csname}{\def\str}{foo}

which becomes (the expansion of \fi is empty)

\@secondoftwo{\expandafter\def\csname}{\def\str}{foo}

and finally

\def\str{foo}

In the case of \defcmd\csname str\endcsname{foo} we'd have

\ifx\csname\csname\expandafter\@firstoftwo
\else\expandafter\@secondoftwo\fi
{\expandafter\def\csname}{\def\csname}str\endcsname{foo}

and, since the test is true,

\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\expandafter\def\csname}{\def\csname}str\endcsname{foo}

The expansion of \else is empty and gets rid of everything up to the matching \fi, so we are left with

\@firstoftwo{\expandafter\def\csname}{\def\csname}str\endcsname{foo}

and finally

\expandafter\def\csname str\endcsname{foo}

which is what wee need.


"Skipping tokens" in a conditional is performed differently when the condition is true or false: \iftrue X\else Y\fi leaves X\else Y\fi and it will be the expansion of \else that gets rid of Y; conversely, \iffalse X\else Y\fi will leave Y\fi, skipping tokens up to and including \else. The expansion of \fi is empty, but until it's not expanded it remains in the input stream.

Here I used \iftrue and \iffalse to be any conditional construction that returns true or false, respectively; X and Y represent any token list.

share|improve this answer
    
Wow, that's some insight in how LaTeX works. Didn't know a lot of stuff you mentioned. Thanks again egreg :). –  Didii Jul 24 '12 at 13:46
add comment
\def\defcmd#1{%
  \ifx\csname#1%
    \expandafter\expandafter\expandafter\def\expandafter\csname
  \else
    \expandafter\def\expandafter#1%
  \fi
}

\defcmd\str{String}

\show\str

\defcmd\csname str\endcsname{String2}


\show\str

\bye

Two classic sins: forgetting to have enough \expandafter and forgetting % at ends of lines:-)


If you replace the definition with your original, the \show will show

 tex foo
This is TeX, Version 3.1415926 (TeX Live 2012)
(./foo.tex
> \str=macro:
 \fi ->String.
l.23 \show\str

? 
> \str=macro:
->String2.
l.28 \show\str

So the second call actually worked as intended but the first defined \str as if you had done

\def\str\fi{....}

and it requires a \fi token to follow its use. That is why you get an error if you try to use the defined token later.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.