0

I had a .tex file which had the following magical code to translate the adadi page number to its roman equivalent.

\makeatletter
\newcommand{\aresame}[2]{%
  \aresame@massage\aresame@tempa{#1}%
  \aresame@massage\aresame@tempb{#2}%
  \ifx\aresame@tempa\aresame@tempb
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}
\def\aresame@massage#1#2{%
  \begingroup\let\@xp\expandafter\let\@nx\noexpand
  \everyeof{}%
  \catcode`{=9 \catcode`}=9
  \catcode`<=1 \catcode`>=2
  \begingroup\edef\x{%
    \endgroup\@nx\scantokens{\def\@nx\1<#2>\@nx\empty}%
  }\x
  \uppercase\@xp{\@xp\endgroup\@xp\def\@xp#1\@xp{\1}}%
}
\makeatother

accompanying with the following code:

\def\myadadipage{\getpagerefnumber{lastpreamblepage}} %this reads the page number in *adadi*, since we used \pagenumbering{adadi} for the preamble part of the document.
\newcounter{searchforadadi}
\loop
\relax%
\ifnum\value{searchforadadi} < 1000
\addtocounter{searchforadadi}{1}%
\aresame{\myadadipage}{\adadi{searchforadadi}}{\xdef\myromanpage{\roman{searchforadadi}}}{}%
\repeat

now I can use \myromanpage anywhere in my document, without any problem.

But

My question arises as:

"How can I move these code to a supplementary .cls file and put them exactly inside another command, I am curious to see how can I define a command, like this one, but just works, internally/locally inside another command?"

Since when I simply copy all these codes from the .tex file to the .cls file, where I need to use the command \aresame inside a definition of another command, say \@setabstract, used to typeset/pageset the English abstract/summary page of the work,

\def\@setabstract{%
\newcommand{\aresame}[2]{%
  \aresame@massage\aresame@tempa{#1}%
  \aresame@massage\aresame@tempb{#2}%
  \ifx\aresame@tempa\aresame@tempb
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}
\def\aresame@massage#1#2{%
  \begingroup\let\@xp\expandafter\let\@nx\noexpand
  \everyeof{}%
  \catcode`{=9 \catcode`}=9
  \catcode`<=1 \catcode`>=2
  \begingroup\edef\x{%
    \endgroup\@nx\scantokens{\def\@nx\1<#2>\@nx\empty}%
  }\x
  \uppercase\@xp{\@xp\endgroup\@xp\def\@xp#1\@xp{\1}}%
}
\newpage
\thispagestyle{empty}
\if@bachelor
\else
\begin{framed}
Number of Pages:
\xdef\myadadipage{\getpagerefnumber{lastpreamblepage}}
\newcounter{searchforadadi}
\loop
\relax%
\ifnum\value{searchforadadi} < 1000
\addtocounter{searchforadadi}{1}%
\aresame{\myadadipage}{\adadi{searchforadadi}}{\xdef myromanpage{\roman{searchforadadi}}}{}%
\repeat
\pageref{LastPage} + \myromanpage
\end{framed}}%end of set abstract

I will receive an error like:

! Illegal parameter number in definition of \@setabstract.
<to be read again> 
                   1
l.697   \aresame@massage\aresame@tempa{#1
                                         }%
?

Let me clear that, if I paste these codes outside the definition it is globally accessible and no errors found! But I wanted to know if I can have them local just for \@setabstract function.

Thanks in advance!

This question is sequel to another TeX.SX question, which could be found here.

  • 2
    You seem to have copied your code in the middle of some other definition. – Ulrike Fischer Sep 16 '16 at 15:45
  • @UlrikeFischer I need it just in that command \@setabstract. What shall I do? – Omid Ghayour Sep 16 '16 at 15:56
  • 1
    you haven't shown what you did so hard to say what you diid wrong but just copy the definitions to the class file (without \makeatletter and \makeatother and they should work the same way do not put them inside another definition – David Carlisle Sep 16 '16 at 15:59
  • 1
    @OmidGhayour The question is unanswerable as it stands because it explains nothing at all. – egreg Sep 16 '16 at 16:00
  • @DavidCarlisle I found some definitions having one or more definitions inside them. So isn't anyway to define internal/local definitions/commands? – Omid Ghayour Sep 16 '16 at 19:04
  • @egreg I try to restated the problem! – Omid Ghayour Sep 16 '16 at 19:19
  • 1
    @OmidGhayour moving code from a tex file to a cls file should not cause issues. When you nest "definitions", lots of things can happen. At a minimum, you need to double the # signs (for example #1 becomes ##1). As you are messing with catcodes there may be other issues, but we really need an MWE to help you. – StrongBad Sep 16 '16 at 19:47
  • 1
    @OmidGhayour there is no point, in general a macro definition does not imply any scope for nested macros they still have definitions when the outer macro ends, so unless the definition depends on the arguments of the outer macro why do it? Certainly if you have some code that generates an error then you should show that code not other parts of different documents that are error free – David Carlisle Sep 16 '16 at 19:47
  • 1
    if you are asking about placing the definitions inside another, please change the title of the question and the example. Moving the definitions inside another has the same issues whether you do it in the same file or move the code to a different file, and moving the code to another file without nesting has no issues at all. – David Carlisle Sep 16 '16 at 19:50
  • @DavidCarlisle I changed the title. – Omid Ghayour Sep 16 '16 at 19:54
  • you still haven't made the question answerable (the class file and tex primitives are not relevant) what is relevant is the nested definition that generates the error that you have not shown. – David Carlisle Sep 16 '16 at 20:01
  • 1
    As I said, you probably are using the wrong approach to solving the problem you have; state it instead of asking how the code can be made to fit. It's quite likely that there's a simpler solution, when we know what the problem is. – egreg Sep 16 '16 at 20:27
  • @egreg I will add your name as an contributor to my .cls package. for the magical code! :) – Omid Ghayour Sep 16 '16 at 20:36
  • There's no need here to go to the effort of putting the definition into a group: why not just copy the code and use it where you want it? – Joseph Wright Sep 16 '16 at 20:36
  • @JosephWright since it gives me an error! – Omid Ghayour Sep 16 '16 at 20:38
  • @OmidGhayour The error you describe in the question would result from placing the code inside another macro, as already mentioned. But that would need a group to localise it and would be a waste of time: I can't see how the obvious approach of copying as-is would give an error. – Joseph Wright Sep 16 '16 at 20:40
  • @DavidCarlisle I added the \@setabstract command which gives me the error. – Omid Ghayour Sep 16 '16 at 20:53
  • @JosephWright I know the error is made by my insufficient knowledge of how to insert a command inside another. What I want is a solution telling me, how to put a command in another. Not telling me I get this just because I shouldn't put a simple (not ready to be included) command inside another. – Omid Ghayour Sep 16 '16 at 20:56

1 Answers1

5

The 'basics' of the need to handle # correctly when nesting commands are covered in What is the meaning of double pound symbol (number sign, hash character) ##1 in an argument?. Here, \@setabstract takes no arguments, so TeX will not allow #1 to appear inside it (there is no argument to refer to). A nested definition inside \@setabstract needs the # doubling, so #1 becomes ##1, etc. Thus one might be tempted to to

\def\@setabstract{%
\newcommand{\aresame}[2]{%
  \aresame@massage\aresame@tempa{##1}%
   ....

This will avoid the error but is still not a great idea. As a macro language, TeX has no grouping associated with definitions, only with explicit groups. As such, with the above you'll get an error if you try to use \@setabstract twice as on the second use \newcommand will detect that \aresame is already defined. There is no need for \aresame to be defined within \@setabstract (which is normally only required if there is some context-dependence to allow for). Instead, you should simply define \aresame globally and use it within \@setabstract

\def\@setabstract{%
  ... % Code using \aresame
}
\newcommand{\aresame}[2]{%
  \aresame@massage\aresame@tempa{#1}%
  \aresame@massage\aresame@tempb{#2}%
  \ifx\aresame@tempa\aresame@tempb
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}

To make a local definition you need a group. That might then look something like

\def\@setabstract{%
  \begingroup
    \def\aresame##1##2{%
      \aresame@massage\aresame@tempa{##1}%
      \aresame@massage\aresame@tempb{##2}%
      \ifx\aresame@tempa\aresame@tempb
        \expandafter\@firstoftwo
      \else
        \expandafter\@secondoftwo
      \fi
    }%
  \def\aresame@massage##1##2{%
    \begingroup\let\@xp\expandafter\let\@nx\noexpand
    \everyeof{}%
    \catcode`{=9 \catcode`}=9
    \catcode`<=1 \catcode`>=2
    \begingroup\edef\x{%
      \endgroup\@nx\scantokens{\def\@nx\1<##2>\@nx\empty}%
    }\x
    \uppercase\@xp{\@xp\endgroup\@xp\def\@xp##1\@xp{\1}}%
  }%
  % Use of \aresame here
  \endgroup
}

but there is little point. Putting all of the definitions inside \@setabstract makes the code harder to read without any obvious benefit, whilst you now have to watch that everything is typeset, saved globally or 'smuggled' out of the group. I'd strongly recommend not doing this.

Note that there's little point in using \newcommand in a 'throw-away' definition in a group, so I've dropped it in favour of \def.

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • Now I called it a good answer! As I said in the question I found a way to just get my job done by globally defining \aresame but, I was looking a way to define it locally. I needed to use ##1 and ##2. Thanks! – Omid Ghayour Sep 16 '16 at 21:10
  • 2
    @OmidGhayour As I've said, just doubling the # tokens won't make the definition local: you'll also need a group (and then watch that you get your result out of the group if you need to retain the tokens). – Joseph Wright Sep 16 '16 at 21:13
  • @OmidGhayour as the answer says, with the local definition @setabstract is not going to work the section time you use it. – StrongBad Sep 16 '16 at 21:13
  • @StrongBad Using the notes given by @Joseph, I made the local definition working by adding a an extra #, i.e. using ##1 and ##2 instead of #1 and `#2. – Omid Ghayour Sep 16 '16 at 21:22
  • @JosephWright And tell me about making it local, Please! – Omid Ghayour Sep 16 '16 at 21:23
  • @OmidGhayour It's not a local definition! After using \@setabstract, do \show\aresame and you'll find the definition is global (as expected), unless you have a group. – Joseph Wright Sep 16 '16 at 21:23
  • @JosephWright And tell me about making it local, Please! How can I have group? – Omid Ghayour Sep 16 '16 at 21:28