5

I have a command \mycom{} which takes 1 argument. The command should perform a test on the argument and do something accordingly. For simple arguments, I can use \ifthenelse, but I need to consider more general arguments, e.g. containing commands or environments. The following MWE follows a solution for a more robust string test, which was given in this post:

\documentclass{article}
\makeatletter
\newcommand{\strtest}[2]{%
\ifnum\pdfstrcmp{#1}{#2}=\z@
  \expandafter\@firstoftwo
\else
  \expandafter\@secondoftwo
\fi}
\makeatother

\newcommand{\mycom}[1]{%
\strtest{#1}{}%
{content :  empty}%
{content : #1}%
}

\begin{document}
\mycom{}

\mycom{coucou}
\end{document}

This works. But if i try to call \mycom{\textbf{coucou}}, then the compilation fails.

Loic Rosnay
  • 8,167
  • Ah, now I see! The quoted answer tells you that this test works only for simple strings, and suggests a way to cope with more complicated input. – egreg Feb 06 '12 at 15:49

2 Answers2

6

The \pdfstrcmp primitive fully expands its argument. Thus things go wrong with macros that cannot be expanded, which includes \textbf. You can prevent this using the \unexpanded primitive:

\newcommand{\strtest}[2]{%
  \ifnum\pdfstrcmp{\unexpanded{#1}}{\unexpanded{#2}}=\z@
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}
Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • BTW, if you want this to work cross-engine, you should load the pdftexcmds package and use \pdf@strcmp, which deals with the fact that XeTeX calls the primitive \strcmp and LuaTeX requires it is implemented in Lua. – Joseph Wright Feb 06 '12 at 16:06
  • Would you prefer \unexpanded or \detokenize in this case? – egreg Feb 06 '12 at 16:09
  • @egreg We've used \unexpanded in LaTeX3, as the idea is to prevent expansion rather than turn into a string per se (which happens anyway). That I know of, there is no technical reason to prefer one approach over another. – Joseph Wright Feb 06 '12 at 16:16
  • So I guess this can be closed as a duplicate, as your answer is already included in mine. – egreg Feb 06 '12 at 16:19
  • thanks, it works. BY the way (i know it is another question...) i would like to follow the same logic, for a command with one argument, which would perform a test of the form IF #1=a OR #1= b THEN. I mean, with an OR. But i don't manage to do it. – Loic Rosnay Feb 06 '12 at 16:29
  • @nicolasroy http://tex.stackexchange.com/a/42791/4427 or http://tex.stackexchange.com/a/41830/4427 or http://tex.stackexchange.com/a/42713/4427 – egreg Feb 06 '12 at 17:44
  • 1
    @egreg, Joseph, there is one technical reason to prefer \unexpanded: it is slightly more efficient since we move around less tokens: \pdfstrcmp converts the tl to a string internally, rather than having to first produce character tokens. For instance, \edef\x{\exp_not:c{\prg_replicate:nn{100000}x}} \edef\x{\prg_replicate:nn{1000}\x} \str_if_eq:xxTF{\tl_to_str:N\x}{}{}{} blows up when trying to build a string with 100002000 tokens, whereas using \exp_not:o works fine. – Bruno Le Floch Feb 06 '12 at 18:39
  • This solution works perfectly, when i use it in a command like \mycom. Now, i would like to use it with an environment defined with \NewEnviron instead of a command, and to refer to \BODY instead of #1. But this does not work... precisely because of \BODY is not expanded, because of the \unexpanded. HOw could i force the expansion of \BODY in the call of the command \strtest ? – Loic Rosnay Feb 07 '12 at 16:06
  • You can get a single expansion using \unexpanded\expandafter{\BODY}. – Joseph Wright Feb 07 '12 at 16:09
  • Well, i don't get it. Do you mean :

    \NewEnviron{monenv}{ \strtest{\unexpanded\expandafter{\BODY}}{} {content : empty} {content : \BODY} } ?

    – Loic Rosnay Feb 07 '12 at 16:13
  • I think the correct solution, inside the monenv should rather be : \expandafter\strtest\expandafter{\BODY}{}. But i don't feel so sure with expandafter's and all that stuff ... :( – Loic Rosnay Feb 07 '12 at 16:23
  • @nicolasroy One for a separate question, I think. – Joseph Wright Feb 07 '12 at 16:25
3

I want to show an alternative. Based on the LaTeX3 engine one approach is:

\documentclass{article}
\usepackage{xparse,expl3,}
\ExplSyntaxOn

\NewDocumentCommand{ \mycom } { m } {
 \tl_if_empty:nTF { #1 }
   {content:~empty}
   {content:~#1}
}
\ExplSyntaxOff
\begin{document}
\mycom{}

\mycom{coucou}

\mycom{\textbf{coucou}}
\end{document}

Base on the comments question you can compare strings with:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\mystringcase}{  m }
  {
    \prg_case_str:nnn { #1 }
         {
          { a }  { Your~string~\textbf{a} }
          { b }  { Your~string~\textbf{b} }
          { c }  { Your~string~\textbf{c} }
         }       
         { Not~in~list }
  }
\ExplSyntaxOff
\begin{document}
\mystringcase{a}

\mystringcase{b}

\mystringcase{d}
\end{document}
Marco Daniel
  • 95,681