19

Often I find myself in the need of expanding some macro, whose contents should be given as an argument to another command. The usual solution is to invoke some \expandafter concoctions to somehow control the expansion, but I wonder if (at least for some common cases) there is an easier way.

As an example, imagine that I want to write something like the following

\somecommand{Some Argument}{\secondarg}

But I want to expand \secondarg before actually attempting to process \somecommand.

For this kind of scenarions, would it be possible to define a command so that

\expandafterallthat{\somecommand{Some Argument}}{\secondarg}

expands to something like

\somecommand{SomeArgument}{Contents of secondarg macro}
3

4 Answers 4

23

This is provided by etextools as \expandnext:

\usepackage{etextools}
\expandnext{\somecommand{Some Argument}}{\secondarg}

But if you only like to expand the second argument once you can use a double argument swapping, a trick I use in some of my packages:

\documentclass{article}
%% Implementation
\def\myswap#1#2{#2{#1}}

\def\expandafterallthat#1#2{%
    \expandafter\myswap\expandafter{#2}{#1}%
}
%%%%%%%%%%%%%%%%

% Test case:
%
\makeatletter
\def\somecommand#1#2{%
    \def\first{#1}%
    \@onelevel@sanitize\first
    \par\texttt{First argument: \first}%
    \def\second{#2}%
    \@onelevel@sanitize\second
    \par\texttt{Second argument: \second}%
}
\makeatother

\def\secondarg{\empty Some stuff\empty}

\begin{document}

\expandafterallthat{\somecommand{\empty Some Argument\empty}}{\secondarg}

\end{document}

This correctly prints: (the \emptys are to see if the argument got expanded further)

first argument: \empty Some Argument\empty
Second argument: \empty Some stuff\empty
1
  • This is nice! I'm now having a look at etextools, seems very useful! Commented Jul 4, 2011 at 14:28
15

Written without a package

\documentclass{article}
\newcommand\expandafterallthat[2]{%
  \expandafter\expandafterallthataux\expandafter{\romannumeral -`0#2 }{#1}%
}
\newcommand\expandafterallthataux[2]{#2{#1}}
\newcommand\somecommand[2]{\detokenize{#1:::#2}}
\newcommand\secondarg{\thirdarg}
\newcommand\thirdarg{\fourtharg}
\newcommand\fourtharg{example}
\begin{document}
\expandafterallthat{\somecommand{Some Argument}}{\secondarg}

\end{document}

will multiply-expand input. (You don't say whether to expand #2 once or several times.)


With the latest engine releases (MiKTeX or upcoming TL'19), one can use \expanded:

\documentclass{article}
\newcommand\expandafterallthat[2]{%
  \expanded{\unexpanded{#1}{#2}}}
\newcommand\somecommand[2]{\detokenize{#1:::#2}}
\newcommand\secondarg{\thirdarg}
\newcommand\thirdarg{\fourtharg}
\newcommand\fourtharg{example}
\begin{document}
\expandafterallthat{\somecommand{Some Argument}}{\secondarg}
\end{document}

(\expanded has always been in LuaTeX, so you can test there even without the latest updated engines.)

5
  • Just seeing now that my added solution is the almost the same as yours. (I really should use \detokenize more often; I still use \@onelevel@sanitize!). The second argument should actually only be expanded once, i.e. contain \thirdarg, shouldn't it? If not, then the \romannumeral` trick is nice1 Commented Jul 4, 2011 at 14:29
  • @Martin From the context in the question I was not sure, so I've gone for full expansion. (The \detokenize here is just a nice way to show quickly what the macro received.)
    – Joseph Wright
    Commented Jul 4, 2011 at 14:37
  • Why does \romannumeral-`0 still expands afterwards, but does not actually consume following numbers? I thought if #2 would start with numbers these would be consumed, but then figured that `0 actually already provides a full number (ASCII code of character 0). But why is it still expanding, i.e. in look-for-a-number mode? Commented Jul 4, 2011 at 14:40
  • @Martin Scharrer You see the same for \number. Try for example \def\test{1}\detokenize\expandafter{\number-`0\test}. I've never been quite clear why, but as it works ...
    – Joseph Wright
    Commented Jul 4, 2011 at 14:54
  • 5
    @Martin, @Joseph: TeX is looking for an optional space. Commented Jul 4, 2011 at 15:53
10

Since Joseph did not mention expl3 yet, let me do that: the LaTeX3 bundle includes a module for expansion control, l3expan. In particular, this includes functions to expand arguments of a command, of the form \exp_args:N... where the dots are letters corresponding to how the various arguments should be treated.

Many letters are in use: n for a brace group which should be passed with no modification, o to expand once, f to expand fully from the left, stopping at the first non-expandable token, x to expand fully throughout, like in an \edef. There is also N for a single non-braced token which should be passed as is, c for text that should be made into a control sequence, then treated as an N argument, and V and v for variables (see the l3expan documentation for details).

Here, you want to expand the second argument once, and leave the first unchanged, so the variant you need is \exp_args:Nno: leave the function unchanged (N), the first argument, braced, unchanged (n), and the second expanded once (o).

\newcommand{\foo}[2]{\showtokens{#1... #2}}
\newcommand{\sometext}{Some text.}
\RequirePackage{expl3}
\ExplSyntaxOn
\exp_args:Nno \foo {\error} {\sometext}
\ExplSyntaxOff
2
  • 1
    And to extend the example, if you wanted to recursively expand \sometext as in Joseph's answer, you'd use \exp_args:Nnx instead. Commented Jul 5, 2011 at 14:20
  • 1
    Actually, you'd want \exp_args:Nnf to get the same effect as my answer :-).
    – Joseph Wright
    Commented Apr 13, 2012 at 6:23
2

With functional package, tricky argument expansion can be replaced with intuitive function composition, which is similar to other programmming languages such as Lua:

\documentclass{article}

\usepackage{functional}
\Functional{scoping=true} % make every function become a group

\begin{document}

\IgnoreSpacesOn
\PrgNewFunction \SomeFunction { m m m } {
  \TlLog {#1}
  \TlLog {#2}
  \TlLog {#3}
}
\IgnoreSpacesOff

\newcommand\secondarg{Second Argument}
\newcommand\thirdarg{Third Argument}

\SomeFunction{First Argument}{\Value\secondarg}{\Value\thirdarg}

\end{document}

In the above code, \Value is a predefined function. And the result in the log file is the following:

> First Argument.
> Second Argument.
> Third Argument.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .