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.
\documentclass{article}
\makeatletter
\usepackage{ltxcmds}

\newcounter{mycounter}
\setcounter{mycounter}{7}

\newcommand{\mysndc}[3]{#1 #2 #3}

\newcommand{\mycommand}{}

\newcommand{\mytrdc}[1]{\ltx@GlobalAppendToMacro{\mycommand}{%
\mysndc{\arabic{mycounter}}{#1}{\thepage}}}

\makeatother
\begin{document}
x

\mytrdc{8}

\newpage
\setcounter{mycounter}{4}

\mycommand
\end{document}

When \mycommand is called, it is \mysndc{\arabic{mycounter}}{8}{\thepage} resulting in 4 8 2. What I would like it to be is \mysndc{7}{8}{2} resulting in 7 8 1. To achieve this, the arguments of \mysndc must be expanded before \mysndc is appended to \mycommand. \expandafter should be useful, but diverse placements of it (also more than one \expandafter) did not yield the desired result, e.g.

\newcommand{\mytrdc}[1]{\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\ltx@GlobalAppendToMacro{\mycommand\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter}{\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\mysndc\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter{\expandafter\expandafter\expandafter\arabic{mycounter}\expandafter\expandafter\expandafter}\expandafter\expandafter\expandafter{\expandafter#1\expandafter}\expandafter{\thepage}}}

. Where must I place how many \expandafters to expand the arguments of \mysndc before appending it to \mycommand and why exactly? I found numerous similar questions and answers to them here at tex.SE, but still was not able to get it right. If possible, I prefer a solution with expandafters, so that I can learn from it, and without using e-TeX/etextools/etextoolbox/LaTeX3/...

share|improve this question
    
Can you be sure that the arguments are all expandable? If so, does your macro itself need to be expandable? To what extent are we allowed to make assumptions about the input? Can we use e-TeX? –  Joseph Wright Jan 26 at 16:29
    
@JosephWright: \arabic{mycounter}, 8, \thepage should all be expandable, but I would prefer an e-TeX-free solution. –  Stephen Jan 26 at 16:33
add comment

2 Answers 2

up vote 7 down vote accepted

The easiest way to tackle this is probably to force expansion of everything using \edef, as that was we avoid needing to shuffle arguments. (\mytrdc is not expandable in any case, so we can use an assignment.) One possible approach using \edef:

\documentclass{article}

\usepackage{ltxcmds}

\newcounter{mycounter}
\setcounter{mycounter}{7}

\newcommand{\mysndc}[3]{#1 #2 #3}

\newcommand{\mycommand}{}
\makeatletter
\newcommand{\mytrdc}[1]{%
  \begingroup
  \edef\x{%
    \endgroup
    \noexpand\ltx@GlobalAppendToMacro{\noexpand\mycommand}{%
    \noexpand\mysndc{\arabic{mycounter}}{#1}{\thepage}}}%
  \x}

\makeatother
\begin{document}
x

\mytrdc{8}

\newpage
\setcounter{mycounter}{4}

\show\mycommand
\end{document}

The idea is simple: create a temporary, \edefed macro \x which contains the expanded material required plus the necessary 'set up', then execute it. Everything is done in a group so we don't mess up any other meaning of \x.

share|improve this answer
    
I'm taking it we don't have a full description of the expandability of the arguments beyond the fact that they are expandable. (Number of expansion steps required is not fixed.) –  Joseph Wright Jan 26 at 16:39
    
Instead of expanding something you expand everything but \noexpand those things that shall not be expanded? Clever! –  Stephen Jan 26 at 16:46
    
@Stephen Yes: working on the basis that we don't have to use \expandafter as your overall aim involves an assignment anyway, I took the easiest approach for forcing expansion. –  Joseph Wright Jan 26 at 16:47
add comment

You could do it with \expandafter, but it would be really long, as passing from \arabic{mycounter} to 7 requires four steps of expansion:

\arabic{mycounter}
\expandafter \@arabic \csname c@mycounter\endcsname
\@arabic \c@mycounter
\number \c@mycounter
7

Expanding \thepage requires one step more, because the first level expansion is \arabic{page}.

Let's see it in a simpler case. You have

\newcommand{\mymacro}[1]{\dosomething{#1}}

where \dosomething is your “append” macro, where you want that #1 is the “final” result after expansion of the argument. So, if you have

\def\foo{A}

you want that \dosomething is fed with A, rather than with \foo. This is easy:

\expandafter\mymacro\expandafter{\foo}

If instead you have

\def\foo{\baz}\def\baz{A}

you need to expand \foo twice:

\expandafter\expandafter\expandafter\mymacro
\expandafter\expandafter\expandafter{\foo}

In general, if you want n expansions, you need 2n–1 \expandafter tokens. In the case of \arabic{mycounter} you'd need 15 before and after \mymacro. For \thepage they would be 31. But the affair is complicated by having three arguments!

So a different approach is needed. The simplest one is to use \edef or, in case your arguments can contain “dangerous” things such as \textbf, with \protected@edef; I'll do it slightly differently from Joseph's answer, just for fun.

\documentclass{article}

\usepackage{etoolbox}

\newcounter{mycounter}
\setcounter{mycounter}{7}

\newcommand{\mysndc}[3]{#1 #2 #3}

\newcommand{\mycommand}{}

\makeatletter
\newcommand{\mytrdc}[1]{%
  \begingroup
  \protected@edef\x{{\arabic{mycounter}}{#1}{\thepage}}%
  \expandafter\endgroup
  \expandafter\gappto\expandafter\mycommand\expandafter
    {\expandafter\mysndc\x}%
}

\makeatother
\begin{document}
x

\mytrdc{8}

\newpage
\setcounter{mycounter}{4}

\texttt{\meaning\mycommand}
\end{document}

The \x temporary macro will expand to {7}{8}{1} and it will disappear as soon as the chain of \expandafter's is performed (the group will be ended after \x has been expanded.

Here's a different approach, with LaTeX3.

\documentclass{article}
\usepackage{xparse}

\newcounter{mycounter}
\newcommand{\mysndc}[3]{#1 #2 #3}

\ExplSyntaxOn
\NewDocumentCommand{\mytrdc}{ m }
 {
  \stephen_mytrdc:n { #1 }
 }
\NewDocumentCommand{\mycommand}{ }
 {
  \tl_use:N \g_stephen_list_tl
 }

\tl_new:N \g_stephen_list_tl

\cs_new_protected:Npn \stephen_mytrdc:n #1
 {
  \stephen_appto_mycommand:ffn { \arabic{mycounter} } { \thepage } { #1 }
 }

\cs_new_protected:Npn \stephen_appto_mycommand:nnn #1 #2 #3
 {
  \tl_gput_right:Nn \g_stephen_list_tl { \mysndc { #1 } { #3 } { #2 } }
 }
\cs_generate_variant:Nn \stephen_appto_mycommand:nnn { ff }
\ExplSyntaxOff

\begin{document}
\setcounter{mycounter}{7}

x

\mytrdc{8}

\newpage
\setcounter{mycounter}{4}
y

\mytrdc{42}

\expandafter\show\csname g_stephen_list_tl\endcsname
\end{document}

In a real document you'd use \mycommand instead of \show. The double inversion of the arguments is for efficiency. The output on the terminal will be

> \g_stephen_list_tl=macro:
->\mysndc {7}{8}{1}\mysndc {4}{42}{2}.
share|improve this answer
    
Thanks for the additional information! While I accepted Joseph's answer, I gave +1 for both answers and assume that also your answer will help some people (or maybe myself at another time). The other up-voters seem to think the same. –  Stephen Jan 26 at 18:27
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.