8

I would like to define a LaTeX (!) command which would change its behavior if it occurs withing the scope of another command. Something like:

\newcommand{\aaa}[1]{#1}
\newcommand{\bbb}[1] IF WITHIN \aaa{} >>> {\it #1} 
                     ELSE>                {\bf #1}

Sorry for the "code" but I have little idea where to start looking for it or how to formulate my question correctly.

For example,

\bbb{sometext}

prints: sometext

while

\aaa{\bbb{sometext}}

prints: sometext

What I was able to find is only a similar question from some years ago: Detecting if inside \section. The answer was to use "modes" in ConTeXt which is impossible for me as I am restricted to LaTeX due to very specific packages I need.

  • 1
    Does it have to be \bbb detecting if it's within \aaa? Could \aaa locally redefine \bbb instead? – Teepeemm Aug 30 '18 at 1:42
  • Thank you for your comment. The latter is also an option of course but I still do not know how it could be implemented. – Wa Wo Aug 30 '18 at 1:46
  • This question has already got its answer, I’m wondering what is the typical use of this? Any example? – Mithun Aug 30 '18 at 16:12
8

EDITED to reflect helpful insight from Jonas that the original method (setting 0/1) could be spoofed with repeated invocations of \bbb inside of \aaa. Thus I converted over to an analogous counter approach, such that the counter value reflects the depth of nesting.

\documentclass{article}

\newcounter{inaaa}
\newcommand{\aaa}[1]{\stepcounter{inaaa}#1\addtocounter{inaaa}{-1}}
\newcommand{\bbb}[1]{%
  \ifnum\value{inaaa}=0\textbf{#1}\else\relax\textit{#1}\fi}
\begin{document}
\bbb{sometext}

\aaa{\bbb{sometext} text}

\aaa{\aaa{\bbb{foo}}\bbb{bar}}
\end{document}

enter image description here

  • Thank you very much. It worked for me! (it was of course not about bold or italics) – Wa Wo Aug 30 '18 at 1:58
  • 1
    This solution may fail if \aaa is used within itself, see this answer. – Jonas Granholm Aug 30 '18 at 21:58
  • 1
    \aaa{\aaa{\bbb{foo}}\bbb{bar}} gives foo bar instead of foo bar. The problem is that the inner \aaa “turns off” \inaaa when it ends, before the second \bbb is evaluated. – Jonas Granholm Aug 31 '18 at 6:51
  • 1
    A trick that should work is to increase and decrease \inaaa instead of setting it to 1 and 0, so that it measures nesting level instead. – Jonas Granholm Aug 31 '18 at 6:53
  • 1
    @JonasGranholm Thanks. I will adopt a counter approach. – Steven B. Segletes Aug 31 '18 at 12:42
9

You can use the \newif command to define a new (very low-level) conditional and commands that alter the conditional test:

\documentclass{article}

\newif\ifinsideaaa

\newcommand{\aaa}[1]{%
    \insideaaatrue
    #1%
    \insideaaafalse
}
\newcommand{\bbb}[1]{
    \ifinsideaaa
    {\itshape #1}%
    \else
    {\bfseries #1}%
    \fi
}

\begin{document}

\bbb{Some text outside of \texttt{\string\aaa}}

\aaa{\bbb{Some text inside of \texttt{\string\aaa}}}

\end{document}

enter image description here

The new commands defined by \newif\if<foo> are:

  • \if<foo>: the conditional check to be used with \if<foo> ... \else ... \fi,
  • \<foo>true: the command that sets the condition to true, and
  • \<foo>false: the command that sets the condition to false.

As a side note, you should use \itshape and \bfseries instead of \it and \bf in LaTeX. The latter are outdated.

  • Thank you very much for your explanation! \it and \bf are just examples. It's actually about other command which would just distract if mentioned. – Wa Wo Aug 30 '18 at 2:00
  • 1
    This solution may fail if \aaa is used within itself, see this answer. – Jonas Granholm Aug 30 '18 at 21:58
4

I tend to avoid the TeX primitives \if.. whenever avoiding them is possible.

\documentclass{article}

\makeatletter
\newcommand\My@CheckWhetherinAAA{}
\newcommand\My@SetInAAATrue{\let\My@CheckWhetherinAAA=\@firstoftwo}
\newcommand\My@SetInAAAFalse{\let\My@CheckWhetherinAAA=\@secondoftwo}
\global\My@SetInAAAFalse

\newcommand{\aaa}[1]{%
  \My@CheckWhetherinAAA{#1}%
                       {\My@SetInAAATrue#1\My@SetInAAAFalse}%
}%

\newcommand{\bbb}[1]{%
  \My@CheckWhetherinAAA{\textit}%
                       {\textbf}%
  {#1}%
}
%
% The result of the following is the same but LaTeX has to shuffle
% around more tokens:
%
% \newcommand{\bbb}[1]{%
%   \My@CheckWhetherinAAA{\textit{#1}}%
%                        {\textbf{#1}}%
%   }%
%
\makeatother


\begin{document}
\bbb{some text}

\aaa{\bbb{some text}}

\aaa{\aaa{\bbb{some text}} \aaa{\bbb{some more text}}}

\begingroup \aaa{\aaa{\bbb{some text}} \endgroup\aaa{\bbb{some more text}}}

\bbb{some text}

\end{document}

With this approach, the tokens forming the argument of \aaa get doubled and gobbled.

This can be avoided by defining another helper-macro:

\documentclass{article}

\makeatletter
\newcommand\My@CheckWhetherinAAA{}
\newcommand\My@SetInAAATrue{\let\My@CheckWhetherinAAA=\@firstoftwo}
\newcommand\My@SetInAAAFalse{\let\My@CheckWhetherinAAA=\@secondoftwo}
\newcommand\My@SetAAAconditions[1]{\My@SetInAAATrue#1\My@SetInAAAFalse}%
\global\My@SetInAAAFalse

\newcommand{\aaa}[1]{%
  \My@CheckWhetherinAAA{\@firstofone}%
                       {\My@SetAAAconditions}%
                       {#1}%
}%

\newcommand{\bbb}[1]{%
  \My@CheckWhetherinAAA{\textit}%
                       {\textbf}%
  {#1}%
}
%
% The result of the following is the same but LaTeX has to shuffle
% around more tokens:
%
% \newcommand{\bbb}[1]{%
%   \My@CheckWhetherinAAA{\textit{#1}}%
%                        {\textbf{#1}}%
%   }%
%
\makeatother


\begin{document}
\bbb{some text}

\aaa{\bbb{some text}}

\aaa{\aaa{\bbb{some text}} \aaa{\bbb{some more text}}}

\begingroup \aaa{\aaa{\bbb{some text}} \endgroup\aaa{\bbb{some more text}}}

\bbb{some text}

\end{document}
  • \global, \makeatletter, \makeatother, \@firstoftwo, and \@secondoftwo seems like a lot of machinery to avoid a tex primitive. And should \My@SetInaFalse be \My@SetInAAAFalse? – Teepeemm Aug 30 '18 at 22:09
  • 1
    @Teepeemm Indeed, should be \My@SetInAAAFalse. I avoid things like \if...#1...\else...\fi because providing unbalanced \if/\else/\fias components of arguments might cause problems... As a rule of thumb macros get a little more "user-proof" by avoiding putting an argument (where you don't know what tokens the user will provide) somewhere between \if..\else..\fi. When I use them, I often do \if..\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi{<true-case-stuff with macro arguments>}{false-case-stuff with macro arguments>} – Ulrich Diez Aug 31 '18 at 0:21
4

Siracusa's answer may fail if \aaa is used within itself, as in the following example:

\aaa{
    \aaa{\bbb{foo}}
    \bbb{bar}}

This will give foo bar instead of foo bar.

Ulrich's and the accepted answer do not have this weakness and are good solutions, especially if you want to define both versions of \bbb at the same time. A quicker and simpler way that also behaves as expected in the example above is to let \aaa redefine \bbb locally, as Teepeemm commented:

\newcommand{\bbb}[1]{\textbf{#1}}
\newcommand{\aaa}[1]{\bgroup\def\bbb##1{\textit{##1}}#1\egroup}

The double ## in the second line is needed because it is a definition inside another definition.

(This method is by the way also useful if \bbb should only be defined inside \aaa – just remove the first line.)

  • It looks like you can also take Ulrich's of delaying #1 and say \newcommand{\bbb}{\textbf}. It looks like that lets you avoid ##1 entirely. – Teepeemm Aug 30 '18 at 22:06
  • Indeed. I included the full form since OP mentioned replacing the command with something more complicated. Another way to avoid ##1 is to add \newcommand{\bbbinaaa}[1]{\textit{#1}} and use \newcommand{\aaa}[1]{\bgroup\let\bbb\bbbinaaa#1\egroup} instead. – Jonas Granholm Aug 30 '18 at 22:29
  • @JonasGranholm Defining both versions of \bbb at the same time is recommendable only in case there are more macros wherein \bbb shall work in the same way in which it shall work within \aaa. – Ulrich Diez Aug 31 '18 at 0:43

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.