[Tex/LaTex] How to redefine the \emph command in Beamer

beameremphasismacros

I am using the Beamer class and would like to redefine \emph to use \bfseries instead of \itshape. I have tried the following command:

\renewcommand<>{\emph}[1]{{\only#2{\bfseries}#1}}

but I get the following error

! LaTeX Error: Command \beamerx@\emph already defined.
          Or name \end... illegal, see p.192 of the manual.

I cannot understand: Why is it a problem that \emph already is defined when I am using \renewcommand?

Best Answer

Looking at the code, I'd say that this was a bug. Beamer defines a wrapper around the \newcommand and \renewcommand environments which step in when they are called with a following <>. But the new \renewcommand seems to do the following:

  1. Save the original definition
  2. Call the modified \newcommand

Now the modified \newcommand calls internally calls the original \newcommand but what it ought to do here is now call the original \renewcommand. (The error message, by the way, is because the original \newcommand is not called on the command specified, in this case \emph, but again there's a level of wrapping on top to deal with the overlay stuff.)

So, to fix this:

  1. Email the beamer maintainers informing them that you think you've found a bug.
  2. You could use the following code to temporarily fix this problem. Basically, I've just copied out the code that handles \newcommand and replace "new" by "renew" at all instances, however
  3. Email the beamer maintainers to say that fixing this bug reveals one or two more! When I use the above code then I get a few errors about commands not being defined. My interpretation of this is that beamer does a few redefinitions (at the \begin{document} time) using \renewcommand. With the old version, if the command-to-be-redefined is not currently defined then this goes through just fine. However, with the new version then if the command-to-be-redefined is not currently defined then this produces errors! (The guilty commands are \includegraphics and \pgfuseimage.)

Anyway, here's the fix. Use at your own risk:

\makeatletter
\def\renewcommand{\@ifnextchar<{\beamer@renewcom}{\beamer@origrenewcommand}}
\def\beamer@renewcom<>{\@star@or@long\beamer@renew@command}
\def\beamer@renew@command#1{\@ifnextchar[{\beamer@@renewcom{#1}}{\beamer@renewcomnoopt
{#1}{0}}}
\def\beamer@@renewcom#1[#2]{\@ifnextchar[{\beamer@renewcomopt{#1}{#2}}{\beamer@renewcomnoopt{#1}{#2}}}
\long\def\beamer@renewcomnoopt#1#2#3{%
  \ifnum#2=0\relax%
    \expandafter\def\expandafter#1\expandafter{\expandafter\beamer@sortzero\expandafter{\csname beamerx@\string#1\endcsname}}%
  \else
    \expandafter\def\expandafter#1\expandafter{\expandafter\beamer@sort\expandafter{\csname beamerx@\string#1\endcsname}{#2}}%
  \fi%
  \beamer@argscount=#2\relax%
  \advance\beamer@argscount by 1\relax%
  \expandafter\renewcommand\csname beamerx@\string#1\endcsname[\beamer@argscount]{#3}%
}
\long\def\beamer@renewcomopt#1#2[#3]#4{%
  \expandafter\def\expandafter#1\expandafter{\expandafter\beamer@presort\expandafter{\csname beamerx@\string#1\endcsname}{#2}{#3}}%
  \beamer@argscount=#2\relax%
  \advance\beamer@argscount by 1\relax%
  \expandafter\renewcommand\csname beamerx@\string#1\endcsname[\beamer@argscount]{#4}%
}
\makeatother

(Added in edit): I made a mistake in the above code: it doesn't save the original version of the command which the beamer version of \renewcommand does. Also, due to the conflicts, I thought that actually it would be better to define a command \myrenewcommand which does the command. So the code below may be better. It's probably still nowhere near perfect - for example, I don't know if the \@star@or@long is appropriate for \renewcommands.

Anyway, here's version 2 wrapped up in a MWE:

\documentclass{beamer}
% http://tex.stackexchange.com/q/13726/86

\makeatletter
\def\myrenewcommand{\@ifnextchar<{\beamer@myrenewcom}{\beamer@origrenewcommand}}
\def\beamer@myrenewcom<>{\@star@or@long\beamer@myrenew@command}
\def\beamer@myrenew@command#1{%
  \expandafter\def\expandafter\beamer@name\expandafter{\csname @orig\string#1\endcsname}%
  \expandafter\let\beamer@name=#1\relax%
\@ifnextchar[{\beamer@@myrenewcom{#1}}{\beamer@myrenewcomnoopt
{#1}{0}}}
\def\beamer@@myrenewcom#1[#2]{\@ifnextchar[{\beamer@myrenewcomopt{#1}{#2}}{\beamer@myrenewcomnoopt{#1}{#2}}}
\long\def\beamer@myrenewcomnoopt#1#2#3{%
  \ifnum#2=0\relax%
    \expandafter\def\expandafter#1\expandafter{\expandafter\beamer@sortzero\expandafter{\csname beamerx@\string#1\endcsname}}%
  \else
    \expandafter\def\expandafter#1\expandafter{\expandafter\beamer@sort\expandafter{\csname beamerx@\string#1\endcsname}{#2}}%
  \fi%
  \beamer@argscount=#2\relax%
  \advance\beamer@argscount by 1\relax%
  \expandafter\renewcommand\csname beamerx@\string#1\endcsname[\beamer@argscount]{#3}%
}
\long\def\beamer@myrenewcomopt#1#2[#3]#4{%
  \expandafter\def\expandafter#1\expandafter{\expandafter\beamer@presort\expandafter{\csname beamerx@\string#1\endcsname}{#2}{#3}}%
  \beamer@argscount=#2\relax%
  \advance\beamer@argscount by 1\relax%
  \expandafter\renewcommand\csname beamerx@\string#1\endcsname[\beamer@argscount]{#4}%
}
\makeatother

\myrenewcommand<>{\emph}[1]{{\only#2{\bfseries}#1}}

\newcommand<>{\test}[1]{{\only#2{\bfseries}#1}}

\begin{document}

\begin{frame}
Some \emph{emphasised} text
\end{frame}
\end{document}