4

I want to test if two macro have identical definition. My naive attempt to use \detokenize does not seem to work in this simple case. However, I have used a similar approach before and it seems to have worked on a much more complicated case, but now that it fails with this simple test case I am a bit confused:

enter image description here

Since the \meaning of \iff and \MyIffWithDotsbNonStar is identical I would have expected them to match.

Related (but different):

MWE

\documentclass{article}
\usepackage{mathtools}
\usepackage{xstring}
\usepackage{xcolor}
\usepackage{bm}% only for highlighting thre cross
\usepackage{amssymb}% only for checkmark symbol
\usepackage[paperwidth=10in]{geometry}% for better display of output

\newcommand{\MyIffWithDotsbNonStar}{\DOTSB;\Longleftrightarrow ;} \newcommand{\MyIffWithoutDotsbNonStar}{;\Longleftrightarrow ;}

\newcommand{\MyIffWithDotsbStar}{\DOTSB;\Longleftrightarrow ;} \newcommand{\MyIffWithoutDotsbStar}{;\Longleftrightarrow ;}

\newcommand{\OK}{\textcolor{blue}{\checkmark}} \newcommand{\NotOK}{\textcolor{red}{\pmb{$\times$}}}

\newcommand{\MacrosHaveSameDefintiion}[4]{% % #1 = macros 1 % #2 = macros 2 % #3 = code to execute if macros are identical % #4 = code to execute if macros differ Macros \texttt{\string#1} and \texttt{\string#2} are \IfStrEq{\detokenize{#1}}{\detokenize{#2}}{#3}{#4}% }%

\begin{document} \par \verb|\iff|: \texttt{\meaning\iff} \par \verb|\MyIffWithDotsbNonStar|: \texttt{\meaning\MyIffWithDotsbNonStar} \medskip

\par\MacrosHaveSameDefintiion{\iff}{\iff}{identical}{different} \OK

\par\MacrosHaveSameDefintiion{\iff}{\MyIffWithDotsbNonStar}{identical}{different} \NotOK \par\MacrosHaveSameDefintiion{\iff}{\MyIffWithoutDotsbNonStar}{identical}{different} \OK

\par\MacrosHaveSameDefintiion{\iff}{\MyIffWithDotsbStar}{identical}{different} \OK \par\MacrosHaveSameDefintiion{\iff}{\MyIffWithoutDotsbStar}{identical}{different} \OK

\end{document}

Peter Grill
  • 223,288
  • 2
    \ifx compares the first level expansion of two macros, but only after checking if they have the same status with respect to \long. – egreg Jan 02 '16 at 10:35
  • 2
    You are comparing the result of \detokenize{\iff} with the result of \detokenize{\MyIffWithDotsbNonStar}, which are clearly different. You need \detokenize\expandafter{#1} – egreg Jan 02 '16 at 10:48

1 Answers1

5

The conditional \ifx does exactly what you want. If you do

\ifx\macroA\macroB

where both \macroA and \macroB are macros (either defined by \def, \gdef, \edef, \xdef or \let to one defined that way), then \ifx returns true if all the following conditions are satisfied:

  1. both macros have the same status with respect to \long (and \outer, but dealing with LaTeX we can leave this aside)

  2. both macros have the same parameter text

  3. both macros have the same replacement text

This should be the first choice, as it's a TeX primitive. The tests

\ifx\iff\iff
\ifx\iff\MyIffWithDotsbNonStar

both return true. However,

\ifx\iff\MyIffWithDotsbStar

will return false, because \iff is \long and \MyIffWithDotsbStar isn't.

Your test fails because you're not expanding the macros, so you're comparing the token lists produced by

\detokenize{\iff}

and

\detokenize{\MyIffWithDotsbNonStar}

which are clearly different. If you add \expandafter, the test should return true.

\documentclass{article}
\usepackage{mathtools}
\usepackage{xstring}
\usepackage{xcolor}
\usepackage{bm}% only for highlighting thre cross
\usepackage{amssymb}% only for checkmark symbol
\usepackage[paperwidth=10in]{geometry}% for better display of output

\newcommand{\MyIffWithDotsbNonStar}{\DOTSB\;\Longleftrightarrow \;}
\newcommand{\MyIffWithoutDotsbNonStar}{\;\Longleftrightarrow \;}

\newcommand*{\MyIffWithDotsbStar}{\DOTSB\;\Longleftrightarrow \;}
\newcommand*{\MyIffWithoutDotsbStar}{\;\Longleftrightarrow \;}

\newcommand*{\OK}{\textcolor{blue}{\checkmark}}
\newcommand*{\NotOK}{\textcolor{red}{\pmb{$\times$}}}

\newcommand{\MacrosHaveSameDefintiion}[4]{%
    % #1 = macros 1
    % #2 = macros 2
    % #3 = code to execute if macros are identical
    % #4 = code to execute if macros differ
    Macros \texttt{\string#1} and \texttt{\string#2} are
    \IfStrEq{\detokenize\expandafter{#1}}{\detokenize\expandafter{#2}}{#3}{#4}%
}%

\begin{document}
\par \verb|\iff|: \texttt{\meaning\iff}
\par \verb|\MyIffWithDotsbNonStar|: \texttt{\meaning\MyIffWithDotsbNonStar}
\medskip

\par\MacrosHaveSameDefintiion{\iff}{\iff}{identical}{different}  \OK

\par\MacrosHaveSameDefintiion{\iff}{\MyIffWithDotsbNonStar}{identical}{different} \NotOK
\par\MacrosHaveSameDefintiion{\iff}{\MyIffWithoutDotsbNonStar}{identical}{different} \OK

\par\MacrosHaveSameDefintiion{\iff}{\MyIffWithDotsbStar}{identical}{different} \OK
\par\MacrosHaveSameDefintiion{\iff}{\MyIffWithoutDotsbStar}{identical}{different} \OK

\end{document}

enter image description here

But it seems killing a fly with a sledgehammer. And the test gives false positives: tests 2 and 4 give “identical” when they shouldn't.

egreg
  • 1,121,712
  • Why is test 2 a false positive? They should match in this case. I agree with test 4 being strictly a false positive. However, it seems that the modified string version only fails to detect the * defined versus defined without the star. Reason for using xstring is that in my actual use case I am using \IfStrEqCase. Is there something equivalent to \ifxcase? – Peter Grill Jan 02 '16 at 14:34
  • @PeterGrill One is \long, the other one isn't. – egreg Jan 02 '16 at 14:37