7

Why is my third O bigger than the first two ones?

\documentclass{report}
\usepackage[english]{babel}
\usepackage{pgffor}
\usepackage{amsmath}

% defining a plain macro
\def\macro{\text{O}}

% defining it dynamically
\foreach \mname/\cmd in {%
    dynmacro/\text{O},%
    } {%
    \expandafter\xdef\csname\mname\endcsname{\cmd}%
}

\begin{document}

\begin{equation}
    2^{\text{O}} \times % okay
    2^{\macro} \times   % okay
    2^{\dynmacro}       % this one yields a weird result
\end{equation}

\end{document}

enter image description here

How do I correct my dynamic macro?

7

If you ask \show\dynmacro after \begin{document} you’ll be answered

> \dynmacro=macro:
->\protect \unhbox \voidb@x \hbox {O}.

which is nothing you’d like, isn't it? The problem is \xdef. You're even lucky that something meaningful happens. With \textbf instead of \text you’d get several errors. You need just one step of expansion.

\documentclass{report}
\usepackage[english]{babel}
\usepackage{pgffor}
\usepackage{amsmath}

% defining a plain macro
\def\macro{\text{O}}

% defining it dynamically
\foreach \mname/\cmd in {%
    dynmacro/\text{O},%
    } {%
    \expandafter\gdef\csname\mname\expandafter\endcsname\expandafter{\cmd}%
}

\begin{document}

\show\dynmacro

\begin{equation}
    2^{\text{O}} \times % okay
    2^{\macro} \times   % okay
    2^{\dynmacro}       % this one yields a weird result
\end{equation}

\end{document}

The console now will report

> \dynmacro=macro:
->\text {O}.

Important note: \text is not the best for this, it should probably be \mathrm.


A different implementation: \dyndef wants three arguments

  1. the input form (optional, default #1=#2)
  2. the definition to perform
  3. the list of things to define

Here are a couple of examples.

\documentclass{report}
\usepackage{amsmath}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\dyndef}{O{##1=##2}mm}
 {
  \cs_set:Npn \__iagolito_dyndef_aux:w #1 \q_stop { #2 }
  \clist_map_function:nN { #3 } \iagolito_dyndef:n
 }
\cs_new:Nn \iagolito_dyndef:n { \__iagolito_dyndef_aux:w #1 \q_stop }
\ExplSyntaxOff

% defining it dynamically
\dyndef{\expandafter\newcommand\csname#1\endcsname{#2}}{
    dynmacro=\text{O},
    dynmacroA=\mathbf{0},
}

\dyndef[#1/#2/#3]{\newcommand#1{#2-#3}}{
  \foo/A/B,
}

\begin{document}

\begin{equation}
2^{\text{O}} \times 2^{\dynmacro} + 2^{\dynmacroA}+(\foo)
\end{equation}

\end{document}

enter image description here

As you see, the first example uses the default dynmacro=\text{O}; for the second example, the specification of the argument is #1/#2/#3; the same placeholders are used in the first mandatory argument.

  • Beat me by a few minutes, I see. I will delete my duplicative answer. – Steven B. Segletes Apr 19 '18 at 13:09
  • Next question would be why \text? – daleif Apr 19 '18 at 13:10
  • 1
    @daleif I added a note – egreg Apr 19 '18 at 13:20
  • I would propose the first argument to be optional with #1=#2 as a default which seems more usual. The fact that he used / in the question is because he was using \foreach. – Manuel Apr 19 '18 at 13:40
  • @Manuel I concur. – egreg Apr 19 '18 at 13:44
5

The main problem is that \xdef completely expands its argument, and this adds an \unhbox\voidb@x to your \dynmacro. Using etoolbox's \expandonce you can make the \cmd expand once only (and using \csxdef to make it simpler):

\documentclass{report}
\usepackage[english]{babel}
\usepackage{pgffor}
\usepackage{amsmath}
\usepackage{etoolbox}

% defining a plain macro
\def\macro{\text{O}}

% defining it dynamically
\foreach \mname/\cmd in {%
    dynmacro/\text{O},%
    } {%
    \csxdef{\mname}{\expandonce\cmd}%
}

\begin{document}

\begin{equation}
    2^{\text{O}} \times % okay
    2^{\macro} \times   % okay
    2^{\dynmacro}       % okay :)
\end{equation}

\end{document}
  • Interesting as well, cheers :) – iago-lito Apr 19 '18 at 15:25
2

You can use a loop not encapsulating items in macros

\documentclass{report}
\usepackage[english]{babel}
\usepackage{pgffor}
\usepackage{amsmath}
\usepackage{xinttools}

% defining a plain macro
\def\macro{\text{O}}

\makeatletter % for LaTeX's \@namedef
% defining it dynamically
\xintForpair #1#2 in {%
  (dynmacro, \text{O})%
  % add more comma-separated "pairs" if needed
  % (no ending comma, though, only to separate pairs)
}\do
{%
    \@namedef{#1}{#2}%
}
\makeatother

\begin{document}

\begin{equation}
    2^{\text{O}} \times % okay
    2^{\macro} \times   % okay
    2^{\dynmacro}        % this one is also okay
\end{equation}

% \showoutput
\end{document}

enter image description here

  • Really nice alternative way to define dynamical macros as well. Cheers :) – iago-lito Apr 19 '18 at 15:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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