4

I tried to define a macro myself, which outputs different strings according to the input variable (like select-case statements in other programming languages).

The problem:

If I use the code in headings, captions, etc., I get error messages and no pdf, unless I put a \protect before the macro (see example below).

I'd like to avoid that - can I redefine the macro to make it "robust" also in such environments?

Example file

\documentclass[11pt, a4paper]{scrbook}

\usepackage{color}
\usepackage{xstring}

\newcommand{\ed}[1]{% source: http://tex.stackexchange.com/q/64131/4009, needs package xstring
\textcolor{magenta}{
\,\bfseries\itshape\IfStrEqCase{#1}{{1}{A}
    {8}{B}
    {5}{C}
{3}{D}
{7}{E}
{11}{F}
{14}{G}
{16}{H}
{19}{J}}
    [??]
}} 

\listfiles

\begin{document}


1 \ed{1}

2 \ed{2}

3 \ed{3}

4 \ed{4} 

test

\section{in headings it does not work like \ed{11}, at leat in the main document}



\end{document}
  • 1
    \DeclareRobustCommand perhaps? ;-) –  Mar 16 '15 at 16:11
  • 1
    See http://tex.stackexchange.com/questions/61503/newcommand-vs-declarerobustcommand for a discussion on \newcommand vs. \DeclareRobustCommand. –  Mar 16 '15 at 16:16

2 Answers2

7

\DeclareRobustCommand can be used for such cases, however, there are expansion issues (i.e. it's not expandible).

The code uses the original version and a \DeclareRobustCommand version.

In addition one could use

  • \newrobustcmd from etoolbox package
  • \NewDocumentCommand from xparse package

\documentclass[11pt, a4paper]{scrbook}

\usepackage{etoolbox}
\usepackage{xparse}

\usepackage{color}
\usepackage{xstring}

\newcommand{\ed}[1]{% source: http://tex.stackexchange.com/q/64131/4009, needs package xstring
\textcolor{magenta}{
\,\bfseries\itshape\IfStrEqCase{#1}{{1}{A}
    {8}{B}
    {5}{C}
{3}{D}
{7}{E}
{11}{F}
{14}{G}
{16}{H}
{19}{J}}
    [??]
}} 

\newrobustcmd{\edetoolbox}[1]{% source: http://tex.stackexchange.com/q/64131/4009, needs package xstring
\textcolor{magenta}{
\,\bfseries\itshape\IfStrEqCase{#1}{{1}{A}
    {8}{B}
    {5}{C}
{3}{D}
{7}{E}
{11}{F}
{14}{G}
{16}{H}
{19}{J}}
    [??]
}} 


\NewDocumentCommand{\edxparse}{m}{% source: http://tex.stackexchange.com/q/64131/4009, needs package xstring
\textcolor{magenta}{
\,\bfseries\itshape\IfStrEqCase{#1}{{1}{A}
    {8}{B}
    {5}{C}
{3}{D}
{7}{E}
{11}{F}
{14}{G}
{16}{H}
{19}{J}}
    [??]
}} 




\DeclareRobustCommand{\edother}[1]{% source: http://tex.stackexchange.com/q/64131/4009, needs package xstring
\textcolor{magenta}{
\,\bfseries\itshape\IfStrEqCase{#1}{{1}{A}
    {8}{B}
    {5}{C}
{3}{D}
{7}{E}
{11}{F}
{14}{G}
{16}{H}
{19}{J}}
    [??]
}} 


\listfiles

\begin{document}


1 \ed{1}

2 \ed{2}

3 \ed{3}

4 \ed{4} 

test

\section{in headings it does not work like \protect\ed{11}, at leat in the main document}

\section{in headings it does not work like \edother{11}, at leat in the main document}

\section{in headings it does not work like \edetoolbox{11}, at leat in the main document}

\section{in headings it does not work like \edxparse{11}, at leat in the main document}



\end{document}
2

When we are using plain TeX then we have two ways how to protect the macro. First one is possible by eTeX extension (commonly used). Instead of \def\macro we must say

\protected\def\macro 

This technique is possible in LaTeX too.

The second more comfortable way is available when OPmac macro package is used. We can say

\addprotect\macro 

which registers the control sequence \macro as protected. We needn't to change the definition of the \macro itself. The \addprotect declaration from OPmac works in classical TeX and we can use it before definition of the macro or after it. This doesn't matter.

wipet
  • 74,238