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 wish to define a meta-command that, whenever invoked, will define an instance of another command; but this inner command must include a progressive number (somewhat similar to what happens with numbered equations, theorems etc).

However, in my attempts so far, the number appears to be evaluated too late (at the time of processing the inner command, rather than at the time of defining it) because all inner commands end up with the final value of the counter (off by one, no less) instead of the value the counter when each of the inner commands was defined.

Perhaps the best way to explain the problem is with a small commented example, consisting of two files. The first file is meant to be included by several other files. The second is one of the several other files that would include the first.

First file (testdef.tex):

% This file may be included by several other files.

% In this example, we are creating a number of proverb-like "rules"
% that other documents will need to refer to.

% All the rules are defined in this file, each with a RULENAME and a
% text. Each rule is given a number, starting from 1, based on the
% order in which they appear here. By defining a rule, we create a new
% internal macro \ruleRULENAME that expands to the text of the rule,
% prefixed by the rule number. The command \showrule{RULENAME}
% displays the rule in the approved way (say in a box) and defines the
% anchor for the label RULENAME: \ref{RULENAME} gives the rule number
% and \pageref{RULENAME} gives the page where \showrule{RULENAME} was
% first issued. Because \showrule{RULENAME} is the anchor for the
% matching label, you cannot issue \showrule{RULENAME} more than
% once. If you need to show the rule without (re)defining the label, use
% \showrulenolabel{RULENAME}.

\newcounter{rule}

\newcommand{\defrule}[2]{
  \stepcounter{rule}
  \expandafter\newcommand\csname rule #1\endcsname{\textbf{Rule \therule:} #2}
}

\newcommand{\showrulenolabel}[1]{
  \noindent\framebox{\csname rule #1\endcsname}
}

\newcommand{\showrule}[1]{
  \label{#1}
  \refstepcounter{rule}
  \showrulenolabel{#1}
}

% the actual rules are defined here

\defrule{vegetables}{Eat more vegetables}

\defrule{smile}{Smile}

\defrule{stairs}{Take the stairs}

Second file, which includes the first:

\documentclass{report}
\begin{document}
\input{testdef}

This is one of several documents that will include the rules file. 

In here I should be able to display any rule by name, for example\ldots

\showrule{smile} (should be 2)

%The actual text of that rule is ``\rulesmile'' but I am not really supposed
%to be using that. Strangely, if I do, it says undefined control sequence.

The rules I know about are, in order:

\showrulenolabel{vegetables} (should be 1)

\showrulenolabel{smile} (should be 2)

\showrulenolabel{stairs} (should be 3)

and I should be able to say that 

\end{document}

Processing the second file yields a document in which all the rules are numbered 4, rather than the expected 1, 2, 3.

I am reasonably comfortable with Latex but not with plain Tex and I suspect this is why I can't quite get at the correct answer. The most relevant suggestions I found came from this other question and I tried applying some of them but my limited understanding of the raw Tex commands prevented me from achieving the desired results.

share|improve this question
    
If you want to learn more about the specific topic of plain tex expansion control, you may want to take a look at tex.stackexchange.com/questions/12668/… –  Christian Feuersänger Sep 20 '11 at 18:23
add comment

2 Answers 2

up vote 10 down vote accepted

You need to expand the definition of your macro fully: this needs \edef (a TeX primitive):

\newcommand{\defrule}[2]{%
  \stepcounter{rule}%
  \expandafter\edef\csname rule #1\endcsname{\noexpand\textbf{Rule \therule:} #2}%
}

As this will expand everything, you may need some precautions about #2. Assuming e-TeX is available (it is on any modern system):

\newcommand{\defrule}[2]{%
  \stepcounter{rule}%
  \expandafter\edef\csname rule #1\endcsname{\noexpand\textbf{Rule \therule:}   
    \unexpanded{#2}}%
}

If e-TeX is not available, some more work is needed using a token register.

share|improve this answer
    
Thanks a lot for showing me this! Very useful. The two-step process was helpful too. I don't know how to check if I have e-Tex and, if yes, how to activate it (I invoke pdflatex and get "This is pdfTeX, Version 3.1415926-1.40.10 (TeX Live 2009/Debian)") but it just worked. I'll mark this as my answer. Now trying hard to make \ref and \pageref also work... –  st01 Sep 20 '11 at 17:51
add comment

An \unexpanded-free version:

\newcommand{\defrule}[2]{%
  \stepcounter{rule}%
  \edef\temp{\noexpand\textbf{Rule \therule:}}%
  \expandafter\newcommand\csname rule#1\expandafter\endcsname
    \expandafter{\temp\space #2}}

Here we use \expandafter just before \endcsname to access at the following one that will expand \temp (defined with \edef before, so the number will be correct). The \expandafter\endcsname technique is quite handy in many situations.

This also has the advantage that we can perform the \newcommand check that the command is not already defined.

If one's afraid that some control sequence gets redefined, then it's possible to use a proper scratch control sequence

\makeatletter
\newcommand{\defrule}[2]{%
  \stepcounter{rule}%
  \edef\@tempa{\noexpand\textbf{Rule \therule:}}%
  \expandafter\newcommand\csname rule#1\expandafter\endcsname
    \expandafter{\@tempa\space #2}}
\makeatother

The control sequence \@tempa is used many times just for temporary use and no one should rely on its meaning after it's been used.

How does it work? First of all \defrule gathers its arguments; let's say the call is \defrule{smile}{Just smile!}.

The first task is to step the rule counter. Let's say the current value becomes 42. Then we do

\edef\@tempa{\noexpand\textbf{Rule \therule:}}

that, under the current conditions is equivalent to

\def\@tempa{\textbf{Rule 42:}}

because \noexpand nullifies the expandability of \textbf (just once).

Now comes the funny part:

\expandafter\newcommand\csname <token list>\endcsname

first expands \csname so that \newcommand is presented with a real token it can operate on. The <token list> is expanded all the way through, including the \expandafter that acts immediately, triggering the expansion of \@tempa after the brace. Then \newcommand comes into action and finds

\newcommand\rulesmile{\textbf{Rule 42:}\space Just smile!}

Et voilà.

share|improve this answer
    
Thanks a lot too! I'm afraid that's beyond me at this stage, though I'm doing my best to follow it. What about that \temp? Is it scoped to be invisible outside the \defrule, or will it pollute the namespace? On a namespace-related note, why do I get "undefined control sequence" if I try to display \rulesmile from the second file, while it's accessible to the commands in the first file? I thought that, with \input, they didn't even know they were in different files. –  st01 Sep 20 '11 at 17:52
    
\temp will remain defined, but that's not a very big deal. Probably \next would be a better choice; or \@tempa. For the other matter, you'd better open another question. There's no concept of "namespace" in TeX. –  egreg Sep 20 '11 at 17:58
    
Thanks! At least I fully understood that bit :-) –  st01 Sep 20 '11 at 18:08
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.